採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

Vue — v-model

テクノロジ系 / L01 template-syntax — テンプレート構文

Vue — v-model

React との大きな差

// React (双方向の手書き)
<input value={text} onChange={e => setText(e.target.value)} />

// Vue (v-model 1つで完結)
<input v-model="text" />

v-model = :value + @inputシュガー

各要素での挙動

<script setup>
const text = ref("");
const checked = ref(false);
const selected = ref("");
const colors = ref([]);
</script>

<template>
  <input v-model="text" />                <!-- 文字列 -->

  <input type="checkbox" v-model="checked" />   <!-- 真偽 -->

  <select v-model="selected">              <!-- 文字列 -->
    <option value="a">A</option>
    <option value="b">B</option>
  </select>

  <input type="checkbox" value="red" v-model="colors" />   <!-- 配列 -->
  <input type="checkbox" value="blue" v-model="colors" />
</template>

要素ごとに自動で適切な属性 (value / checked) に紐づく。

v-model modifier

<template>
  <!-- .lazy: input でなく change で同期 -->
  <input v-model.lazy="text" />

  <!-- .number: 自動で数値化 -->
  <input v-model.number="age" type="number" />

  <!-- .trim: 前後空白除去 -->
  <input v-model.trim="email" />
</template>

.number は特に頻出。type="number" でも文字列で来るので必須。

textarea / radio

<template>
  <textarea v-model="message"></textarea>

  <input type="radio" value="cat" v-model="animal" />
  <input type="radio" value="dog" v-model="animal" />
</template>

radio は同じ v-model を共有 → どれかが選ばれている。

TypeScript

<script setup lang="ts">
import { ref } from "vue";

const text = ref<string>("");
const age = ref<number>(0);
const tags = ref<string[]>([]);
</script>

<template>
  <input v-model="text" />
  <input v-model.number="age" type="number" />
</template>

ref の型注釈で型安全。

コンポーネントでも v-model

L02 ch02 で詳しく:

<MyInput v-model="text" />

→ 子側で defineModel() で受け取り。

独自コンポーネントでも双方向が書ける。

まとめ

  • v-model = 双方向バインディング (:value + @input のシュガー)
  • 要素タイプによって自動で適切な属性に
  • modifier: .lazy / .number / .trim
  • TS では ref<T>(...) で型注釈
  • コンポーネントの v-model は L02 で

L01 完。次は L02 コンポーネント設計。

考えてみよう

既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。

Lv1: 基本 — input / textarea / select / checkbox / radio

問題

<script setup lang="ts">
const text = ref("");
const bio = ref("");
const role = ref("user");
const agree = ref(false);
const color = ref("red");
const tags = ref<string[]>([]);
</script>

<template>
  <input v-model="text" placeholder="text" />
  <textarea v-model="bio" />
  <select v-model="role">
    <option value="user">User</option>
    <option value="admin">Admin</option>
  </select>
  <input type="checkbox" v-model="agree" />
  <input type="radio" value="red" v-model="color" /> Red
  <input type="radio" value="blue" v-model="color" /> Blue
  <input type="checkbox" value="sale" v-model="tags" /> Sale
  <input type="checkbox" value="new" v-model="tags" /> New

  <pre>{{ JSON.stringify({ text, bio, role, agree, color, tags }, null, 2) }}</pre>
</template>

合格基準

  • 6種類の入力が双方向動作
  • React と比べて記述が短いことを実感

Lv2: 応用 — modifier

問題

<script setup>
const trimmed = ref("");
const numeric = ref(0);
const lazy = ref("");
</script>

<template>
  <input v-model.trim="trimmed" placeholder="trim" />
  <input v-model.number="numeric" type="number" placeholder="number" />
  <input v-model.lazy="lazy" placeholder="lazy (change で更新)" />

  <p>trimmed: "{{ trimmed }}" (前後空白なし)</p>
  <p>numeric: {{ numeric }} (typeof: {{ typeof numeric }})</p>
  <p>lazy: "{{ lazy }}"</p>
</template>

各 modifier の効果を確認。

合格基準

  • .trim: 前後空白除去
  • .number: 数値化
  • .lazy: フォーカスアウトで更新

Lv3: 発展 — 子コンポーネントで v-model

問題

子コンポーネント MyInput を作って親から v-model。

<!-- MyInput.vue (Vue 3.4+) -->
<script setup lang="ts">
const model = defineModel<string>();
</script>

<template>
  <input v-model="model" />
</template>

<!-- 親 -->
<script setup>
const text = ref("");
</script>

<template>
  <MyInput v-model="text" />
  <p>{{ text }}</p>
</template>

子の input で打つと親の text が更新される。

合格基準

  • 親子間で v-model 動く
  • defineModel が defineProps + defineEmits を集約していることを確認

提出

submission.md に3問の回答を。