【Vue.js】コンポーネント間でデータを連携する方法
Vue.jsでコンポーネント間でデータを連携する方法について、理解するのに若干時間がかかったのでまとめておきます。
連携は、公式サイトで「props down, events up」と呼ばれている方式を使います。 (https://jp.vuejs.org/v2/guide/components.html より引用)
親コンポーネント・子コンポーネント1・子コンポーネント2があるとき、子コンポーネント1と子コンポーネント2の間の連携は以下のようになります。
- 子コンポーネント1でデータ更新時、親コンポーネントのイベントを発火する (
$emit(eventName)
による発火) - 親コンポーネントのイベントから、子コンポーネント2のデータを更新する (
props
を使ったデータ連携)
これらの流れをサンプルを買いて試しながら確認します。
子コンポーネント1でデータ更新時、親コンポーネントのイベントを発火する ($emit(eventName)
による発火)
子コンポーネント1のデータ更新を親コンポーネントで受け取るために、以下の設計でサンプルを作成します。
- 親コンポーネントは、子コンポーネント1に発生する
on-child-updated
イベントを待ち受ける- イベントを受け取ると、
childUpdated
メソッドを実行し、プロパティparentValue
を書き換える
- イベントを受け取ると、
- 子コンポーネント1はプロパティ
myValue
が更新されたとき、updated
メソッドを実行するupdated
メソッドの中で、$emit(eventName)
を使ってon-child-updated
イベントを発生させる- このとき、プロパティ
myValue
を引数に渡す
- このとき、プロパティ
サンプルコード
<html> <head><title>test</title></head> <body> <div id="app"> <p>this is parent</p> a: {{parentValue}} <hr> <child1 v-on:on-child-updated="childUpdated"></child1> </div> <script type="text/x-template" id="child1"> <div> <p>this is child1</p> b: <input type="text" v-model="myValue" v-on:input="updated"> </div> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script> <script> var component1 = { template: "#child1", data: function() { return { myValue: "" } }, methods: { updated: function() { this.$emit('on-child-updated', this.myValue); } } } var v = new Vue({ el: "#app", data: function() { return { parentValue: "" } }, components: { 'child1': component1 }, methods: { childUpdated: function(v) { this.parentValue = v; } } }); </script> </body> </html>
実行し、子コンポーネント1の入力欄(「b:」)に “test” と入力したのが以下の図です。
親コンポーネントの {{parentValue}}
の箇所が “test” に書き換わっているのがわかります。
親コンポーネントのイベントから、子コンポーネント2のデータを更新する (props
を使ったデータ連携)
次に、子コンポーネント2を作成し、親コンポーネントの値を子コンポーネント2へ渡すようにします。
親コンポーネントから子コンポーネントへのデータ連携には props
を使い、プロパティとして連携します。
親コンポーネントの値を子コンポーネント2に渡すため、以下の設計を追加します。
- 親コンポーネントは、プロパティ
parentValue
を子コンポーネント2のプロパティmyValue
(DOMで指定するときはmy-value
) に連携する
サンプルコード
<html> <head><title>test</title></head> <body> <div id="app"> <p>this is parent</p> a: {{parentValue}} <hr> <child1 v-on:on-child-updated="childUpdated"></child1> <child2 v-bind:my-value="parentValue"></child2> </div> <script type="text/x-template" id="child1"> <div> <p>this is child1</p> b: <input type="text" v-model="myValue" v-on:input="updated"> </div> </script> <script type="text/x-template" id="child2"> <div> <p>this is child2</p> c: <input type="text" v-model="myValue"> </div> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script> <script> var component1 = { template: "#child1", data: function() { return { myValue: "" } }, methods: { updated: function() { this.$emit('on-child-updated', this.myValue); } } } var component2 = { template: "#child2", props: ["myValue"] } var v = new Vue({ el: "#app", data: function() { return { parentValue: "" } }, components: { 'child1': component1, 'child2': component2 }, methods: { childUpdated: function(v) { this.parentValue = v; } } }); </script> </body> </html>
実行し、子コンポーネント1の入力欄(「b:」)に “testaaa” と入力したのが以下の図です。
親コンポーネントの {{parentValue}}
の箇所と、子コンポーネント2の入力欄(「c:」)が “testaaa” に書き換わっているのがわかります。
以上で、子コンポーネント1から子コンポーネント2へデータ連携できました。
ちなみにこれは無理
親コンポーネントのプロパティを共有する場合、子コンポーネントの更新が親コンポーネントに連携されませんでした、という例です。。
親コンポーネントに parentValue
を定義し、2つの子コンポーネントにprops経由で連携してみます。
サンプルコード
<html> <head><title>test</title></head> <body> <div id="app"> <p>this is parent</p> a: <input type="text" v-model="parentValue"> <hr> <child1 v-bind:my-value="parentValue"></child1> <child2 v-bind:my-value="parentValue"></child2> </div> <script type="text/x-template" id="child1"> <div> <p>this is child1 -> {{myValue}}</p> b: <input type="text" v-model="myValue"> </div> </script> <script type="text/x-template" id="child2"> <div> <p>this is child2 -> {{myValue}}</p> c: <input type="text" v-model="myValue"> </div> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script> <script> var component1 = { template: "#child1", props: ["myValue"] } var component2 = { template: "#child2", props: ["myValue"] } var v = new Vue({ el: "#app", data: function() { return { parentValue: "" } }, components: { 'child1': component1, 'child2': component2 } }); </script> </body> </html>
図のように、子コンポーネントの変更は親コンポーネントに連携されませんでした。 (「a]」に"aaa"を入力した後、「b:」に"CCC"を追加入力した図)