簡単なForm作成から学ぶVue(TypeScript)の書き方 その3

# その他のディレクレィブ etc

〜contents〜

* v-for
* v-if v-else v-else-if
* v-show
* 算術プロパティ

## RadioButtonコンポーネントから学ぶv-forディレクティブ

フォームの性別選択のラジオボタンを作ってみる。

v-forディレクティブは、オブジェクトや配列を受け取り、エイリアスを用いて各要素にアクセスできる。

```vue:構文
<div v-for="item in items">
  {{item.attr}}   //itemsの一要素を取得できる。
</div>
```

今回はラジオボタンの項目をリストで受け取りその項目数forループでラジオボタンを表示する。
`components`ディレクトリ下に`RadioButton.vue`を作成する。

はじめにラジオボタンの項目を詰めたオブジェクトに型名をつけておく。

```ts:components/RadioAlternative.ts
export interface RadioAlternative {
  value: string;
  alternative: string;
}
```

前回学んだことと組み合わせると親コンポーネントから配列を受け取り、v-forでラジオボタンを配置して選択が変更されるたびに親にEmitすることができる。

```vue:親コンポーネント(Form)
<template>
  <div>
    <RadioButton :alts="sexList" name="sex" @updateSelected="updateSelected" />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import RadioButton from '@/components/RadioButton.vue'
import { RadioAlternative } from '@/components/RadioAlternative'

@Component({
  components:{
    RadioButton
  }
})
export default class Form extends Vue {
  private readonly sexList: Array<RadioAlternative> = [
    {
      value: "male",
      alternative: "男"
    }, {
      value: "female",
      alternative: "女"
    }
  ]

  private updateSelected(selected: string){
    console.log("parent:" + selected)
  }
}
</script>
```

```vue:子コンポーネント(RadioButton)
<template>
  <div>
    <span v-for="alt in alts" :key="alt.value">
      <label><input type="radio" :name="name" :value="alt.value" @change="changeSelected" >{{alt.alternative}}</label>
    </span>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Emit } from 'vue-property-decorator';
import { RadioAlternative } from './RadioAlternative'

@Component
export default class TextBox extends Vue {
  private alt: RadioAlternative

  @Prop()
  private alts: Array<RadioAlternative>

  @Prop()
  private name: string

  private changeSelected() :void {
    if (event!.target instanceof HTMLInputElement) {
      console.log(event!.target.value)
      this.updateSelected(event!.target.value)
    }
  }

  @Emit('updateSelected')
  private updateSelected(selected: string): void{
  }
}
</script>
```

この場合初めからどちらかを選択した状態にする`checked`はつけられないのだろうか。。

## コンテンツの表示/非表示を切り替える条件分岐

ラジオボタンの選択に応じて表示するコンテンツを変更する機能を実装する。
今回は上のラジオボタンで性別により異なるコンテンツが表示されるように分岐してみる。

* v-if
* v-show

### v-ifディレクティブ

`v-if`ディレクティブは値が`true`であれば表示、`false`であれば非表示とするもの。
`v-else`と`v-else-if`もだいたい予想がつく通り。なお、`v-if`の直後に配置しないと認識されない。

```vue
<template>
  <div>
    <RadioButton :alts="sexList" name="sex" @updateSelected="updateSelectedSex" />
    <div v-if="sex === 'male'">**ここに何かしらのコンテンツ**</div>
    <div v-else>**何か別のコンテンツ**</div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import RadioButton from '@/components/RadioButton.vue';
import { RadioAlternative } from '../components/RadioAlternative';

@Component({
  components:{
    RadioButton
  }
})
export default class Form extends Vue {
  private readonly sexList: Array<RadioAlternative> = [
    {
      value: "male",
      alternative: "男"
    }, {
      value: "female",
      alternative: "女"
    }
  ]

  private sex: string = ""

  private updateSelectedSex(selected: string){
    console.log("parent:" + selected)
    this.sex = selected
  }
}
</script>
```

### v-showディレクティブ

`v-show`は同じく値が`true`の時のみ表示し、`false`では非表示とするもの。
こちらは分岐させることはできない。

```vue
    <div v-if="sex === 'male'">**ここに何かしらのコンテンツ**</div>
```

`v-if`と`v-show`の使い分けは表示を切り替える頻度を参考にする。

切り替えの頻度が低ければ`v-if`
切り替えの頻度が高ければ`v-show`

というのも`v-show`はDOM自体は保持しているが、CSSで表示をオフにしているだけなので、表示/非表示を何度も切り替えても描画コストが低い。一方で、`v-if`は`false`であればDOM自体が消失するため、表示切り替えをするたびにDOMを構成し直す必要があり、描画コストが高い。そのため、何度も表示/非表示が切り替わるようなコンテンツには不向きである。

## 算術プロパティ

上記の例では表示の条件判定の際に条件式`sex === 'male'`を直接書き込んでいた。
しかし、もし、条件が複雑となった場合、この一行に記述するのはひどく可読性を損なう。
そこで、算術プロパティを使用する。

算術プロパティは関数のように記述し、内部で使用するデータ（変数）の値が変更されると勝手に再計算され、値が`return`される。計算結果を`return`するgetterのようなものと考える。
なお、一般の関数`method`と異なる点は、算術プロパティは内部のデータに変更があるまでは以前の値をキャッシュし、それを`return`し続ける点である。

注意点としては初期化されていない変数は追跡されない（変更が検出されない）こと。算術プロパティ内部に分岐処理があり初期の経路では通らない処理にのみ置かれた変数も同様に追跡の対象とならない。

jsでは`Computed:`として記述される。

```vue
    <div v-if="isMale">**ここに何かしらのコンテンツ**</div>
```

```ts:親コンポーネントに追記
  private get isMale(): boolean{
    if (this.sex === "male")
      return true
    else
      return false
  }
```