Vue2 コンポーネントまとめ ②

##はじめに
Vue.jsを理解する上で重要なコンポーネント。
その理解を深めるためにまとめておきます。

- [Vu2 コンポーネントまとめ①](https://qiita.com/morioka1206/items/a9ff2c21f5aa32221e63)


#データの受け渡し
次はコンポーネント間でのデータの受け渡しについてまとめます。
コンポーネント間でのデータの受け渡しができれば別々に作成されたコンポーネントを連携して利用することができます。

##そもそもコンポーネントの親子関係とは
コンポーネントのテンプレートで他のコンポーネントを使うことを、コンポーネントの親子関係と呼びます。
親コンポーネントとはこの例では、hello-worldのコンポーネントを利用したVueインスタンスです。

```html:html
<div id="app">
	<hello-world></hello-world>
</div>
```
子コンポーネントはVue.componentで作成したhello-worldになります。

```vuejs:javascript
Vue.component('hello-world',{
  template : '<h1>Hello World</h1>'
})
```	

Vueにおけるコンポーネントの親子関係は

- <strong>親はコンポーネントを利用する側
- <strong>子はコンポーネントを利用される側

になります。

上記の例だとのHTMLが親、Vueコンポーネントが子になるとのこと。
ややこしいですが。

親子関係はVueインスタンスとコンポーネントのパターンだけでなく
コンポーネントとコンポーネントというパターンもあります。

```vuejs:javascript
Vue.component('comp-parent', {
  template: '<div><h1>componentの親</h1><comp-child /></div>'
})
 
Vue.component('comp-child', {
  template: '<p>componentの子</p>'
})
 
new Vue({
  el: '#app'
})
```

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="js,result" data-user="rm5912" data-slug-hash="abpxVbZ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js vueコンポーネント練習10">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/abpxVbZ">
  Vue.js vueコンポーネント練習10</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

##親から子へのデータの受け渡し
親コンポーネントから子コンポーネントへのデータの受け渡しにはpropsを使います。

```vuejs:javascript
Vue.component('hello-world',{
  template: '<h1>Hello World and {{ message }}</h1>',
  props: ['message']
```

```html:html
<div id="app">
  <hello-world message="Hello Vue.js"></hello-world>
  <hello-world message="Hello Japan"></hello-world>
</div>
```
この場合親コンポーネントはmessage属性としてその値を渡します。
messageの値が親コンポーネントから子コンポーネントに渡されて表示されています。

<p class="codepen" data-height="400" data-theme-id="light" data-default-tab="js,result" data-user="rm5912" data-slug-hash="eYgoYyB" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js vueコンポーネント練習9">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/eYgoYyB">
  Vue.js vueコンポーネント練習9</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

##propsとは
そうなんだ〜とは思っていただけたかと思いますが、いまいちそのpropsってなに？って方も多いと思います。
<strong>propsを利用することで親コンポーネントから子コンポーネントに値を渡すことができます。</strong>
値を渡すというと関数がありますが、関数に値を渡す時に引数を利用するのと同様にpropsでも引数のようにコンポーネントに対して値を渡すことができます。
コンポーネントへpropsを渡すことでコンポーネント内で決められた処理を行うことができます。

##親のdataを子へ
messageに直接、値を入れていましたが、
v-bindを使うと親コンポーネントのdataを子コンポーネントに渡すことができます。
inputTextを親コンポーネントのdataに追加します。

```vuejs:javascript
Vue.component('hello-world',{
  template: '<h1>Hello World and {{ message }}</h1>',
  props: ['message']
})	

var app = new Vue({
  el: '#app',
  data: {
    inputText: ''
  }
})	
```

```html:html
<div id="app">
  <input type="text" v-model="inputText">
  <hello-world v-bind:message="inputText"></hello-world>
</div>
```

v-modelディレクティブを使って双方向データバインディングしています。
入力すると即座に出力されているのがわかるかと思います。

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="js,result" data-user="rm5912" data-slug-hash="KKaYyrV" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js vueコンポーネント練習11">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/KKaYyrV">
  Vue.js vueコンポーネント練習11</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

##v-forでリストデータを子に渡す
v-forを使うことで配列を子コンポーネントに渡すことができます。

```vuejs:javascript
Vue.component('flamework',{
  template: '<div><h2>{{ flame.title }}</h2><p>{{ flame.content }}</p></div>',
  props: ['flame']
})	

new Vue({
  el: '#app',
  data: {
    flames: [
      {'id':0, 'title': 'vue.js', 'content': 'JavaScriptフレームワーク'},
      {'id':1, 'title': 'Rails', 'content': 'Rubyフレームワーク'},
      {'id':2, 'title': 'Laravel', 'content': 'PHPフレームワーク'},
    ]
  }
})
```
子コンポーネントはflameをオブジェクトとして受け取りtemplate内でflameの展開を行なっています。

```html:html
<div id="app">
  <flamework v-for="flame in flames" v-bind:key="flame.id" v-bind:flame="flame"></b>
</div>
```
v-bind:keyを設定しないとエラーになりますので設定しましょう。

<p class="codepen" data-height="402" data-theme-id="light" data-default-tab="js,result" data-user="rm5912" data-slug-hash="YzNMYPd" style="height: 402px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js vueコンポーネント練習12">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/YzNMYPd">
  Vue.js vueコンポーネント練習12</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

またオブジェクトではなく、変数としても渡すことができます。

<p class="codepen" data-height="402" data-theme-id="light" data-default-tab="js,result" data-user="rm5912" data-slug-hash="PoWgEEP" style="height: 402px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js vueコンポーネント練習13">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/PoWgEEP">
  Vue.js vueコンポーネント練習13</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

##子から親へイベントを送る

<strong>親から子へはv-bindとpropsを使います。
子から親へのデータの受け渡しは$emitというメソッドを使います。</strong>

まずは$emitを理解するためにイベントを渡してみます。

アラートが発生するイベントを作成します。

```vuejs:javascript
Vue.component('emit-event, {
  template: '<button v-on:click="clickEvent">ボタン</button>
```

クリックイベントが発火したら、子コンポーネントはそのイベントの処理で$emitを実行して親に渡したいイベントを発火させます。

```vuejs:javascript
methods: {
    clickEvent: function(){
      this.$emit('from-child')
    }
  }	
```

親は子から来るイベントに対するメソッドを設定して子から来るイベントを待ちます。

```html:html
<div id="app">
  <emit-event v-on:from-child="alertMessage"></emit-event>
</div>
```

```vuejs:javascript
var app = new Vue({
  el: '#app',
  methods:{
    alertMessage: function(){
      alert('子からイベント受け取ったよ')
    }
  }
})	
```

親は子が発生したイベントを受け取って親が用意していたメソッドを実行します。
ややこしいですね。

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="result" data-user="rm5912" data-slug-hash="QWdPQpr" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js vueコンポーネント練習14">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/QWdPQpr">
  Vue.js vueコンポーネント練習14</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

また子コンポーネントでそのまま$emitをtemplateに書くこともできるようです。
その場合はmethodsを書く必要がなくなります。

```vuejs:javascript
template: `<button v-on:click="$emit('from-child')">ボタン</button>`,
```

##子から親へデータを渡す

次は子コンポーネント側のdataを親側に送る処理をしてみます。

子コンポーネントに入力フォームを作成して入力後に送信ボタンを押すとイベントが発火して
$emitメソッドを使って入力した内容を親コンポーネントに送ります。

```vuejs:javascript
Vue.component('emit-event',{
  template: `<div>
  <input type="text" v-model="inputText">
  <button v-on:click="clickEvent">送信ボタン</button>
  </div>`,
  data: function(){
    return {
      inputText : ''
    }
  },
  methods: {
    clickEvent: function(){
      this.$emit('from-child',this.inputText)
    }
  },
})	
```

$emitは引数を取ることができます。
引数を利用してデータを送ります。また複数のデータを渡す場合はオブジェクトを利用して渡すことができます。

```html:html
<div id="app">
  <p>{{ message }}</p>
  <emit-event v-on:from-child="receiveMessage"></emit-event>
</div>
```

入力されて受け取ったデータをdataのmessageに設定します。

```vuejs:javascript
var app = new Vue({
  el: '#app',
  data: {
    message: ''
  },
  methods:{
    receiveMessage: function(message){
      this.message = message;
    }
  }
})
```

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="js,result" data-user="rm5912" data-slug-hash="zYNXRLY" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js vueコンポーネント練習15">
  <span>See the Pen <a href="https://codepen.io/rm5912/pen/zYNXRLY">
  Vue.js vueコンポーネント練習15</a> by morioka (<a href="https://codepen.io/rm5912">@rm5912</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

子コンポーネントのフォームに入力されたテキストが親コンポーネントに渡されて、表示されています。

##終わりに
コンポーネントについてまとめてみました。
まとめたもののまだまだ理解が足りないなと感じています。。。
slotについてはまた次にまとめてみたいと思います。


>参考サイト
[REFFECT vue.jsコンポーネントの基礎](https://reffect.co.jp/vue/vue-component-basic#i)
[Qiita [Vue.js]コンポーネントの親子関係とpropsについて](https://qiita.com/lightcafe_tsuchiya/items/e424a3fb4b8c79e00829)
[Qiita propsと$emitでデータを引き渡す](https://qiita.com/d0ne1s/items/f88ecd6aaa90c7bbc5d4)