はじめに
この記事では、Vue3系でカンマ区切りで表示するinputコンポーネントを作成します。
使用技術
- Vue3
- CompositionAPI
作成するinputコンポーネントの要件
- 数値の入力のみを許容する
- コンポーネント利用側は値を数値型(null含む)として入力値を受け取れる
- blur時は
1,234
のようにカンマ区切りで値を表示する - focus時は'1234'のように数値として値を表示する
- 以下のように
v-model
ディレクティブを使用できる
<CommaInput v-model="hoge" />
成果物
CodeSandBoxでソースコードを書きました。
こちらで主題のinputコンポーネントの挙動を確認してみてください!
CodeSandBox上で書いたコード
実装
主題のinputコンポーネント
<template>
<input
:value="commaSeparetedValue"
v-on:focus="onFocus"
v-on:blur="onBlur"
@input="onInput"
/>
</template>
<script>
import { ref, computed, defineComponent } from "vue";
export default defineComponent({
props: {
modelValue: {
required: true,
},
},
setup(props, context) {
const isFocus = ref(false);
const commaSeparetedValue = computed(() => {
if (props.modelValue === null) {
return "";
}
return isFocus.value
? props.modelValue
: Number(props.modelValue).toLocaleString();
});
const onFocus = () => {
isFocus.value = true;
};
const onBlur = () => {
isFocus.value = false;
};
const onInput = (event) => {
const value = event.target.value;
if (isNaN(value)) {
return;
}
context.emit("update:modelValue", value);
};
return {
commaSeparetedValue,
onFocus,
onBlur,
onInput,
};
},
});
</script>
<style scoped>
input {
text-align: right;
}
</style>
呼び出し側
<template>
<p>カンマ区切りのInput</p>
<CommaInput v-model="inputItem" />
</template>
<script>
import { ref } from "vue";
import CommaInput from "./components/CommaInput.vue";
export default {
name: "App",
components: {
CommaInput,
},
setup() {
const inputItem = ref(null);
return {
inputItem,
};
},
};
</script>
ソースコード解説 (CommaInput.vue について)
template
<template>
<input
:value="commaSeparetedValue"
v-on:focus="onFocus"
v-on:blur="onBlur"
@input="onInput"
/>
</template>
主題のコンポーネントは、focus
, blur
, input
のイベントを制御する必要があるため、それぞれのイベントの受け取りを設定します。
表示する値については、カンマ区切りの文字列として表示する場合(blur時)と、通常の数値を表示する場合(focus時)があるため、computed
な値をバインドする必要があります。
そのため、:value(v-bind:valueの省略記法)
を用い、のちに解説するcommaSeparetedValue
をバインドします。
script
propsについて
<script>
import { ref, computed, defineComponent } from "vue";
export default defineComponent({
props: {
modelValue: {
required: true,
},
},
~省略~
});
</script>
主題のコンポーネントは、使用側がv-model
ディレクティブで入力値をセットできる必要があるため、propsにはv-model="hoge"
として使用するための予約語であるmodelValue
を宣言します。
今回はv-model="hoge"
として使用するためにmodelValue
としましたが、v-model:hoge
として使用したい場合は、propsもhoge
とします。
後述するemit
の特別な記載方法によって、使用側がv-model
ディレクティブを使用できるようになります。
setupについて
こちらは解説項目が多いため、ソースコードにコメントで記載しております。
// setup関数の引数に props(表示制御に使用するため), context(emitで使用するため)を受け取る
setup(props, context) {
// 該当要素がフォーカス状態であるかを保持するリアクティブな値を宣言
const isFocus = ref(false);
// isFocusに応じて表示する内容を決定する算出プロパティ
const commaSeparetedValue = computed(() => {
// null時には空白を表示させる
// これがないと、入力値がnullの場合に0が表示される
if (props.modelValue === null) {
return "";
}
// カンマ区切りには標準のtoLocaleString()メソッドを使用
return isFocus.value
? props.modelValue
: Number(props.modelValue).toLocaleString();
});
// focusイベントで使用
const onFocus = () => {
isFocus.value = true;
};
// blurイベントで使用
const onBlur = () => {
isFocus.value = false;
};
// inputイベントで使用
const onInput = (event) => {
const value = event.target.value;
// 数値以外の入力を許容しない
if (isNaN(value)) {
return;
}
// emitの予約語である "update:modelValue(引数名)"を使用 これにより使用側がv-modelディレクティブを使える
// 例: propsのhogeに対して使用側がv-modelを使用する場合、emitは以下のように記載
// context.emit("update:hoge", value);
// 上記の場合、使用側は v-model:hoge="hoge" として使用可能
context.emit("update:modelValue", value);
};
return {
commaSeparetedValue,
onFocus,
onBlur,
onInput,
};
},
まとめ
今回はカンマ区切りのinputコンポーネントの作成について解説しました。
上記のようなコンポーネントは、よくある要望だと思います。
しかし、Vue3系での情報などが少なかったため、記事にしてみました。
また、記載したコードのようにv-model
ディレクティブなどを自前で実装できるようになると、プロジェクトを通して共通的に使用するコンポーネントを作成する際のクオリティが上がるかと思います。
ぜひ使ってみてください!!