[Vue.js+Vuetify]段階評価ボタン ~評価項目の数と評価対象の数に応じて動的に表示する~

# 概要
v-btn-toggleで生成した画像のような段階評価ボタンをコンポーネント化し、評価項目の数だけイテレートする。
[![Image from Gyazo](https://i.gyazo.com/7c29731421c70bd9e55803f8036cd3dd.png)](https://gyazo.com/7c29731421c70bd9e55803f8036cd3dd)

# 今回の使用例
生徒一人ひとりの各教科の成績を7段階評価するシステム。
生徒と科目のデータは外部入力などで取得する動的な値。

イメージ
[![Image from Gyazo](https://i.gyazo.com/36e801efef346353ef7142a63a015bab.png)](https://gyazo.com/36e801efef346353ef7142a63a015bab)


# コンポーネント構成
孫： EvaluationItem.vue　一組のトグルボタン 科目の数だけ表示する。
子： EvaluationList.vue　複数のトグルボタンのリスト 生徒の数だけ表示する。
親： index.vue　

### 孫コンポーネント（トグルボタンコンポーネント) 

```EvaluationItem.vue
<template>
  <v-btn-toggle
    v-model="value"
    @change="sendValue"
  >
    <v-btn
      v-for="n in 7"
      :key="n"
      :value="n"
    >
      {{ n }}
    </v-btn>
  </v-btn-toggle>
</template>

<script>
export default {
  name: 'EvaluationItem',
  data() {
    return {
      value: null
    }
  },
  methods: {
    sendValue() {
      this.$emit('catch-value', this.value)
    }
  }
}
</script>
```
1~7の数字が書かれたボタン。この数字は評点（value）とバインドしている。
ボタンが押される（changeイベント）のたびにvalueがリストコンポーネントに送られる
### 子コンポーネント（リストコンポーネント）

```EvaluationList.vue
<template>
   <div
     v-for="(subject, index) in subjects"
     :key="index"
   >
     {{ subject }}
     <EvaluationItem
       @catch-value="sendData(index, $event)"
     />
   </div>
</template>

<script>
import EvaluationItem from './EvaluationItem.vue'
export default {
  name: 'EvaluationList',
  components: {
    EvaluationItem
  },
  props: {
    subjects: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      evalListData: []
    }
  },
  methods: {
    // リスト内の全ての科目の評点がつけられたかを検証するメソッド
    isInputDataEnough(arr) {
      const l = arr.filter(v => v).length // filterによって空要素が除去される
      return l == this.subjects.length ? true : false // 空要素を除去した後の要素数が科目数と等しければ全て評点がつけられたということ
    },
    sendData(index, value) {
      // 送られてきた評点を表示順に配列に格納し、全部揃い次第親クラスに配列を送る
      this.evalListData[index] = value
      if (this.isInputDataEnough(this.evalListData)) {
        this.$emit('catch-data', this.evalListData)
      }
    }
  }
</script>
```
リスト内で科目の数だけトグルボタンコンポーネントがイテレートされており、それぞれの上に科目名subjectが表示されている。
科目の配列subjectsは親コンポーネントから渡されている。

トグルボタンコンポーネントでchangeイベントのたびに評点が送られてくるとともに、sendDataメソッドが発火する。
それぞれのトグルボタンコンポーネントのリスト内における表示順indexと評点valueが渡されており、評点はその科目の表示順通りに配列evalListDataに格納される。
全ての科目の評点がつけられたとき、評点を格納したevalListDataが親コンポーネントに送られる。

### 親コンポーネント（ページコンポーネント）

```index.vue
<template>
  <v-container>
     <div
       v-for="(student, index) in students"
       :key="index"
     >
       {{ student }} 
       <EvaluationList
         :subjects="subjects"
         @catch-data="setEvalDataCollection(index, $event)"
       />
     </div>
  </v-container>
</template>

<script>
import EvaluationList from './components/EvaluationList.vue'
export default {
  components: {
    EvaluationList,
  },
  data() {
    return {
      evalDataCollection: [],
    }
  },
  computed: {
    subjects() {
      return // 科目（動的な値）
    },
    students() {
      return // 生徒（動的な値）
    }
  },
  methods: {
    setEvalDataCollection(index, array) {
      // 複数のリストコンポーネントから送られてきた評点配列たちを表示順通りに格納
      this.evalDataCollection[index] = array
    }
  }
}
```
それぞれのリストで全ての科目の評点が終わり次第、リストコンポーネントから評点が格納された配列が送られてきて、setEvalDataCollectionメソッドが発火する。
それぞれのリストコンポーネントのページ内における表示順indexと評点配列arrayが渡されており、arrayは表示順通りに配列evalDataCollection内に格納される。

```
students = ['佐藤', '井口', '新井']
subjects = ['国語', '算数', '理科']

のとき

evalDataCollection = [
  [佐藤の国語の評点, 佐藤の算数の評点, 佐藤の理科の評点],
  [井口の国語の評点, 井口の算数の評点, 井口の理科の評点],
  [新井の国語の評点, 新井の算数の評点, 新井の理科の評点]
]
```