vue.jsの使い方の基礎＃４【v-forの使い方色々】

ドキュメントに沿って手を動かしたことを自分用教科書として書いていきます（リストレンダリングの部分）
[公式ドキュメント](https://v3.ja.vuejs.org/guide/list.html)とやっていることは基本的に同じです。

Vue.js基礎知識に関する記事を随時更新しています一覧はこちら。もしよければ参考にしてください。

https://qiita.com/kty000/items/c919e01fc0c118098eb9

#v-forの基本的な使い方
`v-for`ディレクティブを使用することでアイテムのリストを配列内のデータを使って表示することができます。
`v-for` ディレクティブには、`color in colors`のように　xx in xx 形式の特別な構文が必要です。
`colors`はソースデータの配列、`color`は繰り返される配列要素のエイリアス（識別子）です。

```Index.vue
<template>
  <div id="app">
        <!-- v-for="識別名 in 配列データ名" v-bind:key="識別名.key" -->
        <p v-for="color in colors" v-bind:key="color.name">{{ color.name }}</p>
  </div>
</template>

<script>
export default {
    data() {
        return {
            colors:[
              {name: 'red'},
              {name: 'blue'},
              {name: 'yellow'}
            ]
        }
    },
}
</script>
```

以下のように`color in colors`は`color of colors` を使用することもできます。

```Index.vue
<p v-for="color of colors" v-bind:key="color.name">{{ color.name }}</p>
```

##２つ目の引数を使用（インデックス）
`v-for`ディレクティブでは、現在のアイテムに対する配列のインデックスを、２つ目の引数（任意）としてサポートしてくれます。便利～

```Index.vue
<template>
  <div id="app">
        <p v-for="(color, index) in colors" v-bind:key="color.name">{{ index }} 番目：{{ color.name }}</p>
  </div>
</template>

--- 以下略 ---
```

実行結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/ccc6db77-f305-5fab-f4de-0cb819317017.png)


##オブジェクトに使用する
オブジェクトのプロパティに対して反復処理することもできます。

```Index.vue
<template>
  <div id="app">
        <p v-for="color in colors" v-bind:key="color">{{ color }}</p>
  </div>
</template>

<script>
export default {
    data() {
        return {
            colors:{
                name: 'red',
                example: 'apple'
            }
        }
    },
}
</script>
```

実行結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/9c77b0da-626f-9e15-9d3b-d9908866c570.png)

##２つ目の引数を使用
2 つ目の引数としてプロパティ名（すなわちキー）を使用することもできます。

```Index.vue
<template>
  <div id="app">
        <p v-for="(color, key) in colors" v-bind:key="color">{{ key }} :{{ color }}</p>
  </div>
</template>

--- 以下略 ---
```

実行結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/414557cb-3cef-eb37-d808-2919fadf7b66.png)

また、インデックスも使用可能です。

```Index.vue
<template>
  <div id="app">
        <p v-for="(color, key, index) in colors" v-bind:key="color">
           {{ index }} 番目{{ key }} :{{ color }}
        </p>
  </div>
</template>

--- 以下略 ---
```

:::note info
オブジェクトを反復処理するとき、順序は Object.keys() の列挙順のキーに基づいており、全ての JavaScript エンジンの実装で一貫性が保証されていません。
:::

#配列の変化を検出
##配列の内容が変更されたとき
配列の内容が更新された時、元の配列が変更されます。結果、即次画面に反映されます（再描画）
ラップされているメソッドは以下です。

・`push()`　---配列の末尾に値を追加
・`pop()`　---配列の末尾を削除
・`shift()`　---配列の先頭を削除して詰める
・`unshift()`　---配列の先頭に値を追加
・`splice()`　---配列の指定した位置に一つ以上の値を追加
・`sort()` ---並び替え
・`reverse()` ---配列内の要素を逆順にする

`push`を使用して試してみました。
ボタンを押すと入力した文字が配列に追加される処理をします。

```Index.vue
<template>
  <div id="app">
        <p v-for="(color, index) in colors" v-bind:key="color">
           {{ index }} 番目:{{ color.name }}
        </p>
        <input v-model="text" />
        <button @click="addColor()">追加</button>
  </div>
</template>

<script>
export default {
    data() {
        return {
            text: "",
            colors:[
              {name: 'red'},
              {name: 'blue'}
            ]
        }
    },
    methods: {
        addColor() {
            this.colors.push({name: this.text})
            this.text = ""
        }
    }
}
</script>
```

実行結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/8f641621-31c1-6158-360f-29d67b5f8e28.png)

↓「yellow」を入力して追加を押下
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/dd14d92a-2204-4914-eb6e-3eda49a385d7.png)

インデックスと、追加したテキストが表示されました。
既存の DOM を破棄し、リスト全体を再描画しています。

#配列を置き換えたとき
先ほどは元ある配列を変更しましたが、元の配列を変更せず、常に新しい配列を返すメソッドもあります。例えば以下のメソッド。

・`filter()`  ---指定された条件に該当する要素ををもつ新しい配列を作成
・`concat()`　---2つ以上の配列を結合する
・`slice()`  ---文字列の一部分を取り出し、新しい文字列として返す

新しい配列を返している（元の配列を変更していない）ので、再描画されません。

##フィルタ/ソートされた結果を表示したいとき
元のデータを変更せずにフィルタリングやソートされたバージョンの配列を表示したいことがあります。この場合、フィルタリングやソートされた新しい配列を返す算出プロパティを作ることができます。

例えば、以下のように偶数のみを表示するようにする処理を書いてみました。コンソールで元の配列と比較しています。

```Index.vue
<template>
  <div id="app">
        <p v-for="number in sortNumber" v-bind:key="number">
           {{ number }}
        </p>
  </div>
</template>

<script>
export default {
    data() {
        return {
            text: "",
            numbers:[1,2,3,4,5,6]
        }
    },
    computed: {
        sortNumber() {
            var newArray = this.numbers.filter(number => number % 2 == 0)
            console.log(this.numbers)
            console.log(newArray)
            return newArray
        }
    }
}
</script>
```
実行結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/b8ce8dea-4c8a-59d5-bcf7-7d3359f84f0f.png)

元の配列データはそのまま保持されていますね。

#範囲付きv-for
`v-for`は整数値を取ることもできます。指定された数だけ繰り返し処理がされます。

```Index.vue
<template>
  <div id="app">
        <span v-for="number in 10" v-bind:key="number">{{ number }}</span>
  </div>
</template>
```
実行結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/4e0852a5-4891-a774-f3de-4ac8a979a9ef.png)

#複数要素ブロックへのv-for
`v-if`と同様に複数の要素のブロックをレンダリングすることができます。
以下公式の引用

```sample.vue
<ul>
  <template v-for="item in items" :key="item.msg">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>
```
#コンポーネントへのv-for
コンポーネントにも`v-for`を使用することができます。
>ただし、コンポーネントは自身の隔離されたスコープを持っているため、これによってコンポーネントにデータが自動的に渡されることはありません。繰り返されたデータをコンポーネントに渡すには、プロパティも使用する必要があります

何で自動的に渡してくれないのか、ちょっと書き方迷いそうだなと思ったら公式に理由もちゃんとありました。
>v-for の動作と密結合になってしまうからです。どこからデータが来たのかを明確にすることで、他の場面でコンポーネントを再利用できるようになります。

なるほど。コンポーネントはそもそも再利用可能なパーツとして切り出す機能なので、いろんなとこで使うならデータもどこからきたのか明確にしようねということらしい。

以下サンプル

##子コンポーネントの書き方

```Header.vue
<template>
  <div>
    <li>{{ menu }}</li>
  </div>
</template>

<script>
export default {
  name: 'Header',
  props: {
    menu: Object
    }
}
</script>
```

##親コンポーネントの書き方

```Index.vue
<template>
  <div id="app">
    <Header v-for="menu in menus" :key="menu" :menu="menu" />
  </div>
</template>

<script>
import Header from './components/Header.vue'

export default {
    components: {
        Header
    },
    data() {
        return {
            menus:["メニュー１","メニュー２","メニュー３"]
        }
    },
}
</script>
```

#v-forとv-ifの同時利用について

:::note warn
v-if と v-for を同時に利用することは推奨されません。 
:::
公式に強調して書かれていました。ではなぜ推奨されないのか？
Vue がディレクティブを処理する場合、**`v-if` は `v-for` よりも優先度が高い**ためです。

例えば、配列内の要素をもとに表示するか判断したいというとき、以下のように書きたくなってしまいます。

```Index.vue
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

<!-- userオブジェクト内のisActiveがtrueのときforで回したいという例-->
```
この場合、`v-if`が先に評価されるのですが、forが回る前なので`user`がこの時点では存在しません。そのため、user.isActiveなんてないよとエラーが出てしまいます。

##v-ifとv-forを使いたいときの書き方１
ではどのように書いたらいいのか？
同じノードに存在することが原因なので、`<template>`で囲って移動させましょう。
これで`v-for`が先に実行されるので`user`が認識され、判定ができるようになりました。

```Index.vue
    <ul>
        <template v-for="user in users" :key="user.id">
            <li v-if="user.isActive">
                {{ user.name }}
            </li>
        </template>
    </ul>
```

##v-ifとv-forを使いたいときの書き方２

算出プロパティをもとに反復処理をすることでエラーなく表示ができます。個人的には２の書き方のほうがいい気がしますがどうなんでしょう。どっちのほうが早いよ～とかわかる方いたらコメントで教えてくださいm(__)m

```Index.vue
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>

～～略～～
<script>
   computed: {
     activeUsers() {
       return this.users.filter(user => user.isActive)
     }
   }
</script>
```

#さいごに
v-forもしっかり使い方を覚えればとっても便利ですね。。。
もし何かあればコメント等いただけると嬉しいです。

最後まで読んでいただきありがとうございます！