[[📗12 2つの条件属性と違い]] < [[📒Vue.jsクエスト]] > [[📗14 帰ってきたイベントハンドリング]]
## Abstract
リストをforループで扱う方法について学びます。
## Lesson
### 配列
[[v-for]]を使うと、[[配列 (JavaScript)|配列]]の値を使って複数の要素を描画できます。
```html
<script setup lang="ts">
const numbers = [1, 3, 5];
</script>
<template>
<ul>
<li v-for="n in numbers">{{ n }}</li>
</ul>
</template>
```
> [!note]
> `v-for="n in numbers"`は`v-for="n of numbers"`でも問題ありません。
### オブジェクト
[[配列 (JavaScript)|配列]]だけでなく、[[オブジェクト (JavaScript)|オブジェクト]]の場合でも[[v-for]]を使えます。
```html
<script setup lang="ts">
const numbers = { one: 1, two: 2, three: 3 };
</script>
<template>
<ul>
<li v-for="(v, k) in numbers">{{ k }}: {{ v }}</li>
</ul>
</template>
```
### keyの指定
[[v-for]]を使う属性には可能なかぎり`key`を指定してください。先ほどのコードなら以下のようになります。
```html
<script setup lang="ts">
const numbers = { one: 1, two: 2, three: 3 };
</script>
<template>
<ul>
<li v-for="(v, k) in numbers" :key="k">{{ k }}: {{ v }}</li>
</ul>
</template>
```
上記のコードでは`key`を指定してもしなくても結果は変わりません。ただ、**状態の変更により、v-forの内部における描画状態が変わった**とき、==状況によっては状態変更後の描画が正しく行われないケースがあります==。それを避ける意味でも`key`を指定しましょう。
## Missions
### Misson 1
#😁EASY
以下の`humans`に対して、名前を箇条書きで表示するコードを書いてください。
```html
<script setup lang="ts">
import { ref } from "vue";
type Human = { id: number; name: string };
const humans = ref([
{ id: 1, name: "ichiro" },
{ id: 2, name: "jiro" },
{ id: 3, name: "saburo" },
]);
</script>
```
%%
回答例
```html
<script setup lang="ts">
import { ref } from "vue";
type Human = { id: number; name: string };
const humans = ref([
{ id: 1, name: "ichiro" },
{ id: 2, name: "jiro" },
{ id: 3, name: "saburo" },
]);
</script>
<template>
<ul>
<li v-for="human in humans" :key="human.id">{{ human.name }}</li>
</ul>
</template>
```
%%
### Misson 2
#🙂NORMAL
[[#Misson 1]]のコードに対して、クリックすると表示順が逆転する『反転』ボタンを追加してください。
%%
回答例
```html
<script setup lang="ts">
import { ref } from "vue";
type Human = { id: number; name: string };
const humans = ref([
{ id: 1, name: "ichiro" },
{ id: 2, name: "jiro" },
{ id: 3, name: "saburo" },
]);
const handleClick = () => {
humans.value.reverse();
};
</script>
<template>
<ul>
<li v-for="human in humans" :key="human.id">{{ human.name }}</li>
<button @click="handleClick">反転</button>
</ul>
</template>
```
%%
### Misson 3
#🙂NORMAL
以下のコードにはバグが存在します。**事象、再現手順、期待値**を記載してください。
```html
<script setup lang="ts">
import { ref } from "vue";
const names = ref(["ichiro", "jiro", "saburo"]);
const handleClick = () => {
names.value.shift();
};
</script>
<template>
<div v-for="name in names">
<span>{{ name }}</span>
<input type="text" />
</div>
<button @click="handleClick">先頭を削除</button>
</template>
```
%%
回答例
- 事象: 『先頭を削除』ボタンを押したとき、入力欄が正しく表示されない
- 再現手順
1. 3つの入力欄にそれぞれ `aaa` `bbb` `ccc` と入力する
2. 『先頭を削除』ボタンを押す
=> jiro `aaa` と表示される
- 期待値: jiro `bbb` と表示されること
%%
### Misson 4
#🙂NORMAL
[[#Misson 3]]のバグについて、**修正したコード**を記載してください。
> [!hint]- Hint 1
> https://vuejs.org/guide/essentials/list.html#maintaining-state-with-key
%%
回答例
```html
<script setup lang="ts">
import { ref } from "vue";
const names = ref(["ichiro", "jiro", "saburo"]);
const handleClick = () => {
names.value.shift();
};
</script>
<template>
<div v-for="name in names" :key="name">
<span>{{ name }}</span>
<input type="text" />
</div>
<button @click="handleClick">先頭を削除</button>
</template>
```
%%
### Misson 5
#😵HARD
以下のコードは[[#Misson 3]]と同じバグを生じます。その理由を**説明**してください。(コードの間違い指摘は説明ではありません)
```html
<script setup lang="ts">
import { ref } from "vue";
const names = ref(["ichiro", "jiro", "saburo"]);
const handleClick = () => {
names.value.shift();
};
</script>
<template>
<div v-for="(name, i) in names" :key="i">
<span>{{ name }}</span>
<input type="text" />
</div>
<button @click="handleClick">先頭を削除</button>
</template>
```
> [!hint]- Hint 1
> なんのために`key`を指定しているのか。レンダリングエンジンの気持ちになって考えてみるとおのずと答えは出てきます。
## References
- [List Rendering \| Vue\.js](https://vuejs.org/guide/essentials/list.html)