くんすとの備忘録

IT系技術メモ

移転しました。

15秒後に自動的にリダイレクトします。

【Vue.js】コンポーネント間でデータを連携する方法

Vue.jsでコンポーネント間でデータを連携する方法について、理解するのに若干時間がかかったのでまとめておきます。

連携は、公式サイトで「props down, events up」と呼ばれている方式を使います。 f:id:kunst1080:20170612211404p:plain (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” と入力したのが以下の図です。

f:id:kunst1080:20170612211428p:plain

親コンポーネントの {{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” と入力したのが以下の図です。

f:id:kunst1080:20170612211454p:plain

親コンポーネントの {{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"を追加入力した図)

f:id:kunst1080:20170612211634p:plain

参考URL