本記事の内容
Vue.jsでテキストボックスを作成する方法について解説していきます。
「v-bind」・「v-model」・「emit」の練習になるので、ぜひ参考にしてください。
親コンポーネントを作成する
今回は素早く、必要な環境構築ができる「Vue-CLI」を使っています。
WebpackやTypescriptの複雑な設定をやってくれるので、非常に便利です。
Vue-CLIで作成されたApp.vueが「親コンポーネント」で、InputTextModelが「子コンポーネント」となります。
子コンポーネントには、v-modelを使ってデータをバインドさせています。
また、nameやplaceholderなどをInputTextModelにpropsとして渡しています。
<template>
<div>
<label>テキストボックス編(v-modelを使う)</label>
<input-text-model
name="InputText"
placeholder="入力してください"
v-model="inputContentVModel"
></input-text-model>
<p>テキストボックスの結果:{{ inputContentVModel }}</p>
<br />
</div>
</template>
<script lang="ts">
import Vue from "vue";
import InputTextModel from "./components/InputTextModel.vue";
// Vue.extendはtypescriptを使う場合の書き方です
export default Vue.extend({
components: {
"input-text-model": InputTextModel,
},
data() {
return {
inputContentVModel: "",
};
},
});
</script>
子コンポーネントを作成する
子コンポーネントにはフォームの設定を書いていきます。
まずは子コンポーネントのコードの全体像を見てみましょう。
<template>
<fieldset>
<input
type="text"
:name="name"
:placeholder="placeholder"
:value="value"
@input="updateValue"
/>
</fieldset>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
props: {
name: { type: String },
value: { type: String },
placeholder: { type: String },
},
methods: {
updateValue(e: Event) {
if (e.target instanceof HTMLInputElement) {
this.$emit("input", e.target.value);
}
},
},
mounted() {
this.$emit("input", "");
},
});
</script>
v-bindとinput要素について理解しよう
おそらくVue初学者の方がつまづく点として、「:name = "xxx"」や「:value = "xxx"」という書き方でしょう。
まず「:」は「v-bind」の略称で、HTMLの要素の属性を動的に変えたい場合に使用します。
では属性を動的に変えるとはどういうことでしょう。通常のinputタグは下記のように表現されます。
<input type="text" name="hoge" placeholder="入力してね"
value="sample">
タグの中の「type」や「placeholder」が属性です。
各HTMLの要素がどのような属性を持っているのかについては、MDNのドキュメントを確認するのが良いでしょう。
今のinputタグでは、nameやvalue属性の値は固定されており、常に同じ値になってしまいます。
動的に値を変えたい場合に、「v-bind」を使います。
<input type="text" v-bind:name="name" v-bind:placeholder="placeholder"
v-bind:value="value">
毎回v-bindと書くのはめんどくさいので、省略して下記のように書くことが多いです。
:nameや:placeholderは、僕が好き勝手つけたわけではなく、inputタグが持つ属性だったのですね。
後々v-modelとv-bindを使い分ける際に、この辺の理解が必要ですので、理解しておきましょう。
<input type="text" :name="name" :placeholder="placeholder"
:value="value">
propsについて理解しよう
"name"や"value"はpropsであり、親コンポーネントから渡された値を指します。
<input type="text" :name="name" :placeholder="placeholder"
:value="value">
親コンポーネントからpropsを受け取った場合、必ず子コンポーネントでpropsを下記のように定義する必要があります。
propsにはtypeやdefaultなどのオプションが用意されており、propsの型を指定することができます。
export default Vue.extend({
props: {
name: { type: String },
placeholder: { type: String },
value: { type: String },
},
親コンポーネントからは下記のようにnameやplaceholderの値を渡してあげることで、v-bindでバインドされた属性の値が変わるわけですね。
しかし、"value"というpropsは渡していないじゃないか!と思った方もいるかと思います。
これについては次の章で解説していきます。
<input-text-model
name="InputText"
placeholder="入力してください"
v-model="inputContentVModel"
></input-text-model>
v-modelについて理解しよう
カスタムコンポーネント(自分で作成した独自のコンポーネント)でv-modelを使う場合、v-modelは「v-bind:value & v-on:input」に分解できます。
このように複数の処理をまとめた書き方を「糖衣構文」と呼びます。もう一度親コンポーネントの記述を確認してみます。
<input-text-model
name="InputText"
placeholder="入力してください"
v-model="inputContentVModel"
></input-text-model>
これをv-bind:value (:value)とv-on:input (@input)で書き直すと、下記のようになります。
$eventについては次の章で解説しますが、ひとまずフォームに入力された値と理解しておいてください。
<input-text-model
name="InputText"
placeholder="入力してください"
:value="inputContentVModel"
@input="inputContentVModel = $event"
></input-text-model>
こうして分解すると、:valueが存在しており、valueをpropsとして子コンポーネントに渡しているのが分かります。
カスタムコンポーネント(自分で作成した独自のコンポーネント)でv-modelを使う場合、v-modelは「v-bind:value & v-on:input」に分解できます。
$emitについて理解しよう
続いて子コンポーネントから親コンポーネントに値を渡す「$emit」について解説していきます。
子コンポーネントでは、入力されると「updateValue」というメソッドが発火するようにしています。
<template>
<fieldset>
<input
type="text"
:name="name"
:placeholder="placeholder"
:value="value"
@input="updateValue"
/>
</fieldset>
</template>
メソッドの中身は下記のようになっています。
e:Eventやif (e.target instanceof HTMLInputElement)はTypescriptを使わない場合は不要です。
export default Vue.extend({
methods: {
updateValue(e: Event) {
if (e.target instanceof HTMLInputElement) {
this.$emit("input", e.target.value);
}
},
},
e.target.valueには、フォームに入力された値が入っています。
また親コンポーネントが@input = xxxとすると入力された値を取得できるようにするため、emitの中で”input”と書いています。
全体像を確認しよう
最後に親コンポーネントと子コンポーネントのコードを確認しましょう。
// 親コンポーネント
<template>
<div>
<label>テキストボックス編(v-modelを使う)</label>
<input-text-model
name="InputText"
placeholder="入力してください"
v-model="inputContentVModel"
></input-text-model>
<p>テキストボックスの結果:{{ inputContentVModel }}</p>
<br />
</div>
</template>
<script lang="ts">
import Vue from "vue";
import InputTextModel from "./components/InputTextModel.vue";
// Vue.extendはtypescriptを使う場合の書き方です
export default Vue.extend({
components: {
"input-text-model": InputTextModel,
},
data() {
return {
inputContentVModel: "",
};
},
});
</script>
// 子コンポーネント
<template>
<fieldset>
<input
type="text"
:name="name"
:placeholder="placeholder"
:value="value"
@input="updateValue"
/>
</fieldset>
</template>
<script lang="ts">
import Vue from "vue";
export default Vue.extend({
props: {
name: { type: String },
value: { type: String },
placeholder: { type: String },
},
methods: {
updateValue(e: Event) {
if (e.target instanceof HTMLInputElement) {
this.$emit("input", e.target.value);
}
},
},
mounted() {
this.$emit("input", "");
},
});
</script>