はじめに
通常のel-inputは以下のように使いますが、
<el-input>
<template #prepend>前</template>
<template #append>後</template>
</el-input>
(↓のようなUIになります)
入力時に値を整形するようにラッピングコンポーネントを作ろうとしたんですが、prependとappendを再利用する方法に詰まったので、解決方法を備忘録として残しておきます。
NG例:雑にラッピングしてみた
<template>
<el-input v-model="innerValue" @input="oninput">
<template #prepend>
<slot name="prepend" />
</template>
<template #append>
<slot name="append" />
</template>
</el-input>
</template>
これでも、以下のようにラッピングコンポーネントにprepend・append指定したら表示されます。
<Wrapper>
<template #prepend>前</template>
<template #append>後</template>
</Wrapper>
ただ、以下のように<template #prepend>``<template #append>
を指定してなくても、el-inputのprependとappendが表示されてしまいます。
<Wrapper>
</Wrapper>
そもそもElementPlus側がどうやって実現してんのかな~?って疑問になったので、el-inputのコードを見てみました。
<template>
<div
v-bind="containerAttrs"
:class="containerKls"
:style="containerStyle"
:role="containerRole"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<!-- input -->
<template v-if="type !== 'textarea'">
<!-- prepend slot -->
<div v-if="$slots.prepend" :class="nsInput.be('group', 'prepend')">
<slot name="prepend" />
</div>
引用:https://github.com/element-plus/element-plus/blob/dev/packages/components/input/src/input.vue
なにやら<div v-if="$slots.prepend"
で<slot name="prepend" />
の表示制御している様子。
$slotsについては公式にも記載がありました。
https://ja.vuejs.org/api/component-instance.html#slots
解決
スロットプラグイン($slots
)を使うことで解決できました。
<template>
<el-input v-model="innerValue" @input="oninput">
<!-- ラッパーコンポーネントのprependが指定されていればel-inputのprependを表示 -->
<template v-if="$slots.prepend" #prepend>
<slot name="prepend" />
</template>
<!-- ラッパーコンポーネントのappendが指定されていればel-inputのappendを表示 -->
<template v-if="$slots.append" #append>
<slot name="append" />
</template>
</el-input>
</template>
<scirpt lang="ts" setup>
import { ref, watch } from 'vue';
// modelValueはv-modelで指定した値
const props = defineProps<{ modelValue: number; }>();
// v-modelで指定された値をこのコンポーネントから更新するためのイベント
const emits = defineEmits(['update:modelValue']);
// コンポーネント内部用の変数
const innerValue = ref();
// 呼び元での値の更新を反映
watch(() => props.modelValue, (v) => innerValue.value = cnv(v));
const cnv = (value) => {
// 数値以外を削除
return value.replace(/[^0-9]/g, '')
}
const oninput = () => {
// ここで入力値を整形したりする
const value = cnv(innerValue.value);
// 入力されたら呼び元の値を更新するためのイベント発火
emits('update:modelValue', value);
}
</setup>
$slots.prepend
・$slots.append
で以下のようにラッピングコンポーネント呼び出しにて、<template #prepend>
<template #append>
が指定されているか判断できます。
<Wrapper>
<!-- これを指定すると、$slots.prependにインスタンスが設定される -->
<template #prepend>前</template>
<!-- これを指定すると、$slots.appendにインスタンスが設定される -->
<template #append>前</template>
</Wrapper>