Vue.jsを更に学ぶ(4日目)

#コンポーネントの親子間のデータの受け渡し

親コンポーネントのnumberの値を子コンポーネント内で/2をして表示させたい。

```LikeNumber.vue
<template>
  <div>
   <p>いいね({{number / 2}})</p>
  </div>
</template>
<script>
  export default {
    props: ['number'],
    data() {
      return {

      }
    }
  }
</script>

<style scoped>
  div {
    border: 1px solid red;
  }
</style>
```

注目すべきは

```.vue
<script>
  export default {
    props: ['number'],
```

このように、props: ['属性名']とする。


次に

```App.vue
<template>
  <div>
    <LikeHeader></LikeHeader>
    <LikeNumber number="6"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue"

export default {
  data() {
    return {
      number: 10
    }
  },
  components: {
    'LikeHeader': LikeHeader
  }
}
</script>
```

propsで指定した属性名を子コンポーネントタグで使用する

```vue
 <LikeNumber number="6"></LikeNumber>
 <LikeNumber v-bind:number="number"></LikeNumber>
省略系
 <LikeNumber :number="number"></LikeNumber>
```

これで、子コンポーネントのnumberに6が代入される。(上)

numberに入れたい数字を動的にするには、v-bindディレクティブを使って書く。(下)

これで、App.vueに定義されてあるnumberの値が子コンポーネントに受け渡され、その数を/2した数字を表示することができる。

propsで指定する任意の名前はキャメルケースで書いた場合でも、html内ではケバブケースで書くことができる。

おすすめは、propsを定義する時はキャメルケースで書いて、htmlのタグ内ではケバブケースで書くことが多いらしい。

##propsはdataのように使用できる。

```LikeNumber.vue
<template>
  <div>
   <p>いいね({{halfNumber}})</p>
  </div>
</template>
<script>
  export default {
    props: ['number'],
    computed: {
      halfNumber() {
        return this.number / 2
      }
    }
  }
</script>
```

このように、computedプロパティ内で、this.numberというように書くことができる。

##propsにはバリデーションを指定することができる

```LikeNumber.vue
<template>
  <div>
   <p>いいね({{halfNumber}})</p>
  </div>
</template>
<script>
  export default {
    props: {
      number: {
        //numberの型を指定
        type: Number,
        //必ずこの属性が必要
        //つまり<LikeNumber>だけでは使うことができない。<LikeNumber number=~~>としなくてはいけない。
        required: true,
        // 何も指定されなかった時〜〜の値になる。ただ、required:と共存することができない。
        //default: 10
      }
    },
    computed: {
      halfNumber() {
        return this.number / 2
      }
    }
  }
</script>
```

##複数のpropsを定義する

```LikeNumber.vue
<template>
  <div>
   <p>いいね({{halfNumber}})</p>
   <p>{{ testProps }}</p>
  </div>
</template>
<script>
  export default {
    props: {
      number: {
        type: Number,
        required: true,
      },
      testProps: {
        type: String,
      }
    },
    computed: {
      halfNumber() {
        return this.number / 2
      }
    }
  }
</script>
```

このように書けば、複数のpropsを定義することができる。

##$emitメソッドを使って子コンポーネントから親コンポーネントに値を受け渡す

親から子の時の逆で、子コンポーネント内に送り口を設定し、親コンポーネントに受け取り口を設定するようにする。

かなり難しいけど、$emitはカスタムイベントを作れるものというのは覚えておこう。

```LikeNumber.vue
<template>
  <div>
   <p>いいね({{halfNumber}})</p>
   <button @click="increment">+1</button>
  </div>
</template>
<script>
  export default {
    props: {
      number: {
        type: Number,
        required: true,
      }
    },
    computed: {
      halfNumber() {
        return this.number / 2
      }
    },
    methods: {
      increment() {
        //第一引数はカスタムイベントの名前
        //第二引数は親に渡したい値
        this.$emit("my-click", this.number + 1)
      }
    }
  }
</script>
```

```App.vue
<template>
  <div>
    <LikeHeader></LikeHeader>
    <LikeNumber :number="number" v-on:my-click="number = $event"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>
```


注目すべきは

```vue
<LikeNumber :number="number" v-on:my-click="number = $event"></LikeNumber>
```

このように書くことで、numberに$event（つまり子コンポーネントで渡したかった値)が代入される。

@my-click以下をメソッドとして書く方法もある。

```App.vue
<template>
  <div>
    <LikeHeader></LikeHeader>
    <LikeNumber :number="number" @my-click="incrementNumber"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue"

export default {
  data() {
    return {
      number: 10
    }
  },
  components: {
    'LikeHeader': LikeHeader
  },
  methods: {
    //適当に引数名を指定することで、ここに子コンポーネントで指定した
    //→第二引数が代入される。
    //引数名はなんでも良い。
    incrementNumber(value) {
      this.number = value
    }
  }
}
</script>
```

細かくいうと、

子コンポーネントが親のデータを変えているのではなく、

@my-clickというタイミングで親自身が自分のデータを変えているという解釈が正しい。

親は子のデータに依存して変わるデータを持っていないのである。

また、$emitで作るカスタムイベントは全てケバブケースにするのをおすすめする。



#コンポーネント発展編

##slotを使って親から子にhtmlタグを含んだデータを渡す

```App.vue
<template>
  <div>
    <LikeHeader>
      <h1>トータルのいいね数</h1>
      <h2>{{ number }}</h2>
    </LikeHeader>
    <LikeNumber :number="number" @my-click="incrementNumber"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>
```

まず、子コンポーネントタグの中にhtmlタグを含んだ要素を書く。
次に

```LikeHeader.vue
<template>
  <div>
    <slot></slot>
  </div>
</template>

<script>
export default {
  props: ["headerText"]
}
</script>
```

templateタグ内に、slotタグを用意する。

このslot内にhtmlタグを含んだ先ほどの要素が丸ごと代入される。

propsではデータの値しか送れなかったが、今回はhtmlタグを含んで送れるという違いがある。

slotと入れ替わるhtmlタグを含む要素は、cssに関してだけ親と子どちらでも指定することができる。

##slotにフォールバックコンテンツを設定する

子コンポーネントタグ内に何も書かれていない時は、
slotタグ内に書かれてある要素が表示されるようになる。

```LikeHeader.vue
<template>
  <div>
    <slot>デフォルトのタイトル</slot>
  </div>
</template>
```

```App.vue
<template>
  <div>
    <LikeHeader>
      
    </LikeHeader>
    <LikeNumber :number="number" @my-click="incrementNumber"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>
```

##v-slotを使用して複数のslotを扱う(名前付きslot)

```App.vue
<template>
  <div>
    <LikeHeader>
      <template v-slot:title>
        <h2>こんにちは</h2>
      </template>
      <template v-slot:number>
        <p>{{number}}</p>
      </template>
    </LikeHeader>
    <LikeNumber :number="number" @my-click="incrementNumber"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>
```

```LikeHeader.vue
<template>
  <div>
    <slot name="title"></slot>
    <hr>
    <p>いいねの数</p>
    <slot name="number"></slot>
  </div>
</template>
```

1.分けたいものをtemplateタグに分けて、v-slot:任意の名前　とする。
注意点としては、v-slotを使う時は必ずtemplateタグを使うようにする。使わないとエラーが出る。

2.子コンポーネントで、slotタグにname属性をつけて、そこに先ほど決めた任意の名前をいれる。

そうすることで、子コンポーネントタグ内に書かれているhtmlタグが含まれた要素を複数のslotに分けてデータを渡すことができる。

##デフォルトスロット

```App.vue
<div>
    <LikeHeader>
      <template v-slot:title>
        <h2>こんにちは</h2>
      </template>
      <h3>初めまして</h3>
      <template v-slot:number>
        <p>{{number}}</p>
      </template>
    </LikeHeader>
    <LikeNumber :number="number" @my-click="incrementNumber"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
```

ここでいう

```vue
<h3>初めまして</h3>
```

は名前付きスロットに指定されておらず、テンプレートタグの外に書かれてある。

この要素を取得する時は

```LikeHeader.vue
<template>
  <div>
    <slot name="title"></slot>
    下のこれ
    <slot></slot>
    <hr>
    <p>いいねの数</p>
    <slot name="number"></slot>
  </div>
</template>
```

普通にslotタグをおけば良い。

デフォルトスロットは、名前付きスロットに指定されたtemplateタグの部分を取り除いたものが入る。

厳密には、見えないところでv-slot:defaultがついたテンプレートタグ内に残りの全てが入ることから、デフォルトスロットと呼ばれる。

##slotの中身になる要素に子コンポーネントのデータを使いたい

```LikeHeader.vue
<template>
  <div>
    <slot name="title" v-bind:fafa="user"></slot>
    <slot></slot>
    <hr>
    <p>いいねの数</p>
    <slot name="number"></slot>
  </div>
</template>
```

まず、slotタグにv-bind:任意の名前="親に渡したいプロパティ名"

と書く。

```App.vue
<LikeHeader>
      <h2>みなさん</h2>
      <template v-slot:title="slotProps">
        <h2>こんにちは</h2>
        <h2>{{ slotProps.fafa.firstName }}</h2>
      </template>
      <h3>初めまして</h3>
      <template v-slot:number>
        <p>{{number}}</p>
      </template>
    </LikeHeader>
```

次に、名前付きslotを指定したtemplateタグに
v-slot:title="任意の名前"とすることで、子コンポーネントで指定したプロパティを取得することができる。

ここでいうtitleは子コンポーネントファイルのslotタグに指定したname属性のことである。(slotの名前、名前付きスロット)


あとは

```vue
<h2>{{ slotProps.fafa.firstName }}</h2>
```

こんな感じでデータにアクセスする。

##デフォルトslotしかない時の省略記法

```LikeHeader.vue
<template>
  <div>
    <slot v-bind:fafa="user"></slot>
    <slot></slot>
    <hr>
    <p>いいねの数</p>
    <slot></slot>
  </div>
</template>
<script>
export default {
  data() {
    return {
      user: {
        firstName: 'Taro',
        lastName: 'Tanaka'
      }
    }
  }
}
</script>
```

```App.vue
<template>
  <div>
    <LikeHeader>
      <template v-slot:default="slotProps">
        <p>{{ slotProps }}</p>
        <h2>みなさん</h2>
        <h3>初めまして</h3>
      </template>
    </LikeHeader>
    <LikeNumber :number="number" @my-click="incrementNumber"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>
```

この場合、3つあるslotタグの中でも、v-bind:fafa="user"を指定したslotタグだけuserオブジェクトのデータが渡される

また、App.vueは

```App.vue
<template>
  <div>
    <LikeHeader v-slot="slotProps">
      <p>{{ slotProps }}</p>
      <h2>みなさん</h2>
      <h3>初めまして</h3>
    </LikeHeader>
    <LikeNumber :number="number" @my-click="incrementNumber"></LikeNumber>
    <LikeNumber v-bind:number="number"></LikeNumber>
  </div> 
</template>
```

このように、子コンポーネントタグに直接v-slotディレクティブを指定し、子コンポーネントからのデータを受け取ることができる。

ただ、名前付きslotがある場合は無理。


##動的なスロット名

```App.vue
<template v-slot:[title]></template>
```

v-slot:[]の形にすれば、data:以下で定義したプロパティを使うことができる。

##名前付きスロットは#に置き換える

v-slot:は#に置き換える

```vue
//動的な場合
<template #[title]></template>

//静的な場合
<template #title></template>
```

あくまでも名前付きスロットの時だけ省略する。

デフォルトスロットのみの時は

```App.vue
<LikeHeader #default="slotProps">
</LikeHeader>
```

このように#default=""と略すことができる。