はじめに
Vue で親コンポーネントと子コンポーネントの間でデータをやり取りする方法について、今まで何となくやっていたので改めて勉強しました。
分かりにくい部分だと思うので、よかったら参考にして頂ければと思います。
対象者
- 基本的な JavaScript のコーディングができる方
- Vue で開発をしたことがある方
環境
- Vue.js 2.6.10
- Vuetify 2.1.0
勉強しながら作ったもの
名前と血液型を入力する子コンポーネントを作りました。
親コンポーネントから初期値を渡して初期表示したり、子コンポーネントで入力した名前・血液型を親コンポーネントに渡したりできるようにしています。
下の画像がスクリーンショットで、氏名から【反映】ボタンまでの3段が子コンポーネントで、parent:
の段が親コンポーネントです。
親子間のデータやりとりについて
作ったものの説明の前に親子間のデータやり取りの基本に触れておきます。
親 → 子 のデータフロー
親は子に渡すデータを属性として指定します。
子はそれを props
で受け取ります。
下記の例では、名前を value
で親子間で一致させています。
<child :value="param"></child>
<script>
export default {
props: {
value: {
name: String,
bloodType: String
}
}
}
</script>
子 → 親 のデータフロー
子は親に渡すデータを $emit
で渡します。
$emit
で親側のイベントを発火させて、データが渡ります。
下記の場合、子で $emit
を実行すると this.inputs
を引数として親の input
イベントが発火します。親側では value
に子が指定した引数が渡ります。
<child @input="value => param = value"></child>
this.$emit('input', this.inputs)
親コンポーネントの記述をまとめる
上記の 親 → 子
子 → 親
で説明した親コンポーネントの実装内容をまとめると次のようになります。
<child
:value="param"
@input="value => param = value"
>
</child>
なお次のように v-model
を使うと属性一つで記述できます。
<child v-model="param"></child>
作ったものの説明
それぞれのコンポーネント内のデータの状態を確認したいので、本来の入力項目以外に {{ }}
を使ってデータをダンプしています。
子コンポーネント
コード
<template>
<v-row>
<v-col>
<v-text-field v-model="name"></v-text-field>
<v-radio-group v-model="bloodType" row>
<v-radio
v-for="elem in bloodTypes"
:key="elem.value"
:label="elem.label"
:value="elem.value"
></v-radio>
</v-radio-group>
<v-btn @click="handleClick">反映</v-btn>
{{ inputs }}
</v-col>
</v-row>
</template>
<script>
export default {
props: {
value: {
name: String,
bloodType: String
}
},
data: () => ({
name: '',
bloodType: '',
bloodTypes: [
{ label: 'A型', value: 'a' },
{ label: 'B型', value: 'b' },
{ label: 'O型', value: 'o' },
{ label: 'AB型', value: 'ab' }
]
}),
computed: {
inputs: function () {
return {
name: this.name,
bloodType: this.bloodType
}
}
},
created () {
this.name = this.value.name
this.bloodType = this.value.bloodType
},
methods: {
handleClick () {
this.$emit('input', this.inputs)
}
}
}
</script>
<template>
側
- 【反映】ボタンを押すと
handleClick
関数が実行され、$emit
により親コンポーネントに入力内容が伝わります
<script>
側
-
props
のvalue
に、親コンポーネントで指定した値(初期値)が設定されます- 親側では
:value
やv-model
で初期値を指定します
- 親側では
-
created
で親コンポーネントから指定された値をdata
配下にバラして設定しています -
computed
のinputs
は、子コンポーネントで入力された値を親コンポーネントへ反映するためのオブジェクトです -
methods
のhandleClick
関数でthis.$emit
して親コンポーネントに反映します- 反映ボタンを押したときに動きます
親コンポーネント
コード
<template>
<v-container>
<v-row>
<v-col>
<child v-model="param"></child>
parent: {{ param }}
</v-col>
</v-row>
</v-container>
</template>
<script>
import Child from './Child'
export default {
components: {
'child': Child
},
data: () => ({
param: {
name: 'hoge',
bloodType: 'a'
}
})
}
</script>
<template>
側
- 子コンポーネントを使用します
-
v-model
で子コンポーネントに渡す値を指定します -
:value
でも値を渡すことができます- その場合は前述の通り
@input
指定も必要になります
- その場合は前述の通り
-
<script>
側
-
import
で子コンポーネントをインポートします -
components
で子コンポーネントをコンポーネント定義します -
data
には子コンポーネントに初期値として渡す値を定義します- 子コンポーネントで値が書き換えられて
$emit
されると、ここが書き換わります
- 子コンポーネントで値が書き換えられて
動かしてみる
名前や血液型を入力して【反映】を押すと、親コンポーネントに入力内容が伝わります。
ダンプされている内容から確認できると思います。
- 【反映】ボタンの右は、子コンポーネント側の値
-
parent:
の右は、親コンポーネント側の値
処理の流れ
- 氏名や血液型の欄を編集する
- 子コンポーネントの
data
の中が書き換わる - この時点では親コンポーネントには反映されない
- 子コンポーネントの
- 【反映】ボタン押す
- 子コンポーネントの
handleClick
関数により$emit
される - 子コンポーネントで入力された値が、親コンポーネントに伝わる
- 子コンポーネントの
所感
今回勉強したことで、今まで何となくだった理解が深まって良かったと思っています。でもやっぱりしばらくするとまた忘れそうなので、記事見て思い出そうと思います。