[[📗11 Vueらしくスタイルをはめる]] < [[📒Vue.jsクエスト]] > [[📗13 リストの取り扱い]] ## Abstract [[Vue]]の描画に関する条件について学びます。 ## Lesson [[Vue]]には条件を満たすかどうかによって、描画の可否を決定する[[属性 (HTML)|属性]]が用意されています。[[v-if]]と[[v-show]]の2つがあります。 ### [[v-if]] [[属性値]]が`true`のときに要素を描画し、`false`のときは要素を描画しません。たとえば、以下の[[HTML]]は1つ目の[[divタグ]]のみが表示されます。 ```html <template> <div v-if="true">これは描画される</div> <div v-if="false">これは描画されない</div> </template> ``` また、`v-else`や`v-else-if`と組み合わせて、[[JavaScript]]における`else`や`else if`文相当の表現も可能です。 ```html <script setup lang="ts"> const x = 15; </script> <template> <div v-if="x > 100">これは描画されない</div> <div v-else-if="x > 10">これは描画される</div> <div v-else>これは描画されない</div> </template> ``` ### [[v-show]] [[属性値]]が`true`のときに要素を表示し、`false`のときは表示しません。たとえば、以下の[[HTML]]は1つ目の[[divタグ]]のみが表示されます。 ```html <template> <div v-show="true">これは表示される</div> <div v-show="false">これは表示されない</div> </template> ``` ### [[v-if]]と[[v-show]]の違い [[v-ifとv-showの違い]]は以下の点です。 | [[属性 (HTML)\|属性]] | `true`のとき | `false`のとき | | --------------------- | -------------------- | ------------------------------ | | [[v-if]] | 要素を描画・表示する | 要素を描画**しない** | | [[v-show]] | 要素を描画・表示する | 要素を描画するが表示**しない** | [[v-if]]は要素を描画するかしないかを制御しますが、[[v-show]]は==常に描画はする代わりに==表示/非表示を制御します。 ## Missions ### Misson 1 #🙂NORMAL 以下は`動物を取得`ボタンを押したら、結果を取得して表示するWebアプリを目指した途中のコードです。 ```html <script lang="ts" setup> import { Ref, ref } from "vue"; interface Animal { kind: string; name: string; } const animal = ref(null) as Ref<Animal | null>; const fetchAnimal = () => { window.setTimeout(() => { animal.value = { kind: "dog", name: "Goemon" }; }, 1000); }; </script> <template> <div> <button @click="fetchAnimal">動物を取得</button> </div> <div> <!-- ローディング画像 --> <img src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Attachments/mimizou-momochi.gif" /> </div> <div v-show="animal"> <!-- 結果 --> <pre>{{ JSON.stringify(animal) }}</pre> </div> </template> ``` 以下2つの不具合を修正するよう、コードを変更してください。 - データ取得前は結果を表示**しない** - ローディング画像は**データ取得中( `fetchAnimal` 実行中)のみ**表示する %% 回答例 ```html <script lang="ts" setup> import { Ref, ref } from "vue"; interface Animal { kind: string; name: string; } const animal = ref(null) as Ref<Animal | null>; const loading = ref(false); const fetchAnimal = () => { loading.value = true; window.setTimeout(() => { animal.value = { kind: "dog", name: "Goemon" }; loading.value = false; }, 1000); }; </script> <template> <div> <button @click="fetchAnimal">動物を取得</button> </div> <div v-show="loading"> <!-- ローディング画像 --> <img src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Attachments/mimizou-momochi.gif" /> </div> <div v-show="animal"> <!-- 結果 --> <pre>{{ JSON.stringify(animal) }}</pre> </div> </template> ``` %% ### Mission 2 #🙂NORMAL 以下のコードはエラーになり画面が表示されません。**その理由を説明**し、**最低限の修正で動くコードになおしたdiffを提示して**ください。 ```html <script lang="ts" setup> import { Ref, ref } from "vue"; interface Animal { kind: string; name: string; } const animal = ref(null) as Ref<Animal | null>; const fetchAnimal = () => { window.setTimeout(() => { animal.value = { kind: "dog", name: "Goemon" }; }, 1000); }; </script> <template> <div> <button @click="fetchAnimal">動物を取得</button> </div> <div v-show="animal"> <!-- 結果 --> <ul> <li>{{ animal.kind }}</li> <li>{{ animal.name }}</li> </ul> </div> </template> ``` %% 回答例 初期状態では`animal`が`null`であり、`<template>`の中で`animal`のプロパティにアクセスできないから。常に描画される[[v-show]]ではなく、[[v-if]]にすることで問題を解決できる。 ```diff - <div v-show="animal"> + <div v-if="animal"> ``` %% ### Mission 3 #🙂NORMAL 以下のコードには[[Vue]]として非推奨の書き方のある行が1行あります。**その行を指定**し、**何が非推奨であるかを説明**してください。 なお、`State.animals`がNullableであることは問題ないとします。 ```html <script lang="ts" setup> import { reactive } from "vue"; interface Animal { kind: string; name: string; } interface State { animals: Animal[] | null; } const state = reactive<State>({ animals: null, }); const fetchAnimals = () => { window.setTimeout(() => { state.animals = [ { kind: "dog", name: "Goemon" }, { kind: "cat", name: "Mitarashi" }, ]; }, 1000); }; </script> <template> <div> <button @click="fetchAnimals">動物を取得</button> </div> <ul> <li v-if="state.animals" v-for="animal in state.animals"> {{ animal.kind }}: {{ animal.name }} </li> </ul> </template> ``` > [!hint]- Hint 1 > https://vuejs.org/guide/essentials/conditional.html#v-if-with-v-for %% 回答例 \<li v-if="state.animals" v-for="animal in state.animals"> 同一要素に[[v-if]]と[[v-for]]を両方指定してはいけないから。 %% ### Mission 4 #😵HARD [[#Mission 3]]のコードについて、非推奨にならないようコードを修正してください。ただし、==コードの意味合いを変更しないでください。== %% 回答例 ```html <script lang="ts" setup> import { reactive } from "vue"; interface Animal { kind: string; name: string; } interface State { animals: Animal[] | null; } const state = reactive<State>({ animals: null, }); const fetchAnimals = () => { window.setTimeout(() => { state.animals = [ { kind: "dog", name: "Goemon" }, { kind: "cat", name: "Mitarashi" }, ]; }, 1000); }; </script> <template> <div> <button @click="fetchAnimals">動物を取得</button> </div> <ul> <template v-if="state.animals"> <li v-for="animal in state.animals"> {{ animal.kind }}: {{ animal.name }} </li> </template> </ul> </template> ``` %% > [!hint]- Hint 1 > `<ul>`に[[v-if]]属性を追加するのは、コードの意味合いが変わってしまうのでNG。 > [!hint]- Hint 2 > https://vuejs.org/guide/essentials/conditional.html#v-if-on-template ## References - [Conditional Rendering \| Vue\.js](https://vuejs.org/guide/essentials/conditional.html)