2016/10/08

Vue.js2.x系で親から子コンポーネントにデータを渡す方法

Vue.jsの2.0がリリースされたということで、1年半ぶりに勉強している。
そんな中、なぜか親コンポーネントから子コンポーネントにデータを渡すことができずハマりかけたので共有する。

ちなみにvue-cliを使っているので、次のようなディレクトリ構成になっている。
.
├── src
│   ├── App.vue
│   ├── assets
│   │   ├── icon.png
│   ├── components
│   │   ├── Card.vue
│   │   └── TimeLine.vue
│   └── main.js
└── static
    └── data.json

vue-cliの使い方は、次の記事を参考にしてほしい。




親コンポーネントから子コンポーネントにデータを渡す


Compotentsの関係は、App.vue > TimeLine.vue > Card.vueとしている。
App.vuedata.jsonを読み込み、そのデータをTimeLine.vueを経由して、Card.vueまで渡す方法を例にまとめる。

App.vue → TimeLine.vueは、普通にdataで定義した値をpropsで受け取る。
TimeLine.vue → Card.vueは、v-forでループさせたときの値を渡す。
// data.json
{
  "timelines": [
    {
      "date": "2016/10/07",
      "event": "できごと1"
    },
    {
      "date": "2016/10/08",
      "event": "できごと2"
    },
    (以下略)
  ]
}
<!-- App.vue -->
<template>
    <time-line :timelines="timelines"></time-line>
</template>

<script>
import TimeLine from './components/TimeLine'
import data from '../static/data.json'

export default {
  components: {
    TimeLine
  },
  data () {
    return {
      timelines: data.timelines
    }
  }
}
</script>
<!-- TimeLine.vue -->
<template>
  <div>
    <ul>
      <card v-for="tl in timelines" :timeline="tl"></card>
    </ul>
  </div>
</template>

<script>
import Card from './Card'

export default {
  components: {
    Card
  },
  props: ['timelines']
}
</script>
<!-- Card.vue -->
<template>
  <li>{{timeline.date}} - {{timeline.event}}</li>
</template>

<script>
export default {
  props: ['timeline']
}
</script>



propsだけでなくv-bindが必要


すべてのコンポーネントは別々のスコープを持っているので、子コンポーネントから親コンポーネントのデータを参照することができない。
そのため、データを渡すときも明示的に指定しなければならない。

もうちょっと簡単な例に置き換えてみる。
<!-- Parend.vue -->
<template>
  <my-component v-bind:myMessage="parentData"></my-component>
</template>

<script>
import MyComponent from './components/MyComponent'

export default {
  components: { MyComponents },
  data: {
    parentData: ['Alfa', 'Bravo', 'Charlie']
  }
}
</script>
<!-- MyComponent.vue-->
<template>
  <p>{{myMessage}}</p>
</template>

<script>
export default {
  props: ['myMessage'],
  created: () => {
    console.log(this.myMessage)
  }
}
</script>

このような関係の場合について説明する。
 

親コンポーネントで、すること


親コンポーネントから子コンポーネントにデータを渡す場合は、カスタムディレクティブ(ここではmy-component)のところに「v-bind:<渡すときの変数名>="<自身のデータ>"」と明示的に指定しなければならない。ちなみにv-bind:myMessage:myMessageと省略することができる

<渡すときの変数名>は、子コンポーネントのpropsで指定する名前。
<自身のデータ>は、new Vue(ここではexport defaultのところ)で指定したdataのデータ(この場合はparentData)を指定する。

子コンポーネントで、すること


親コンポーネントからデータを受け取る場合は、propsに親から渡ってくる変数名を書く(ここではmyMessage
各メソッドでは、this.myMessageの要領でアクセスできる。


追記: 2016/10/11 11:30
Twitterで以下のような指摘をされた。

ということで、実際にためしてみた。

すべてのコンポーネントは別々のスコープを持っているので、子コンポーネントから親コンポーネントのデータを参照することができない。
そのため、データを渡すときも明示的に指定しなければならない。

の部分がちょっと違う。正しくは以下のとおり。


さいごに


公式ドキュメントをざっと流し読みしただけで、「props」さえ指定していればデータが受け取れると思っていた。
また、公式ドキュメントのpropsでは、子コンポーネントのサンプルしか載ってなく、親コンポーネントでどうすればいいのかわからず、かなり悩んだ。

この記事が、同じようにハマった人の参考になればと思う。



追記: 2016/10/26 10:00
Vue.js 2.x系で親子コンポーネント間のデータのやりとりについて、サンプルコードと図解を交えて解説している。





参考サイト


余談だが、ちょっと前にjp.vuejs.orgにコントリビュートした。(vuejs/jp.vuejs.org#125
中の方たちは日本人で、安心して日本語でPullRequestを送ることができる。
気づいた点があったらぜひPullRequestを送ってみて欲しい!


以上

written by @bc_rikko

2 件のコメント :

  1. とても参考になりました。

    が、App.vue のサンプル内にて、
    <scritp> になっていてコピペで動きませんでした・・・。
    <script> ですね。(tp→pt)
    あえての罠?まったく気付かずにハマりました(汗)

    お陰で、理解は深まりました!

    返信削除
    返信
    1. コメント&ご指摘ありがとうございます!

      ああああお恥ずかしい。
      さっそく修正いたしました。

      削除