v-modelで入力を同期する
v-modelは入力要素の値とstateを同期します。テキスト、チェックボックス、ラジオボタン、セレクトなどで使えます。
入力の基本VUE
<script setup>
import { ref } from 'vue'
const name = ref('')
const age = ref(null)
const agree = ref(false)
</script>
<template>
<input v-model.trim="name" placeholder="名前">
<input v-model.number="age" type="number" placeholder="年齢">
<label><input v-model="agree" type="checkbox">規約に同意</label>
<p>{{ name }} / {{ age }}歳 / 同意: {{ agree }}</p>
</template>.trimは前後の空白を取り除き、.numberは可能なら数値へ変換します。必須入力や範囲などの業務ルールは、送信処理で明示的に確認します。
入力を検証して送信する
入力欄の近くにエラーを表示し、エラーがあるときは送信処理を止めます。画面上の検証だけに頼らず、サーバー側でも同じ検証を行ってください。
登録フォームVUE
const title = ref('')
const errors = ref([])
const saved = ref(false)
function submit() {
errors.value = []
saved.value = false
if (title.value.trim() === '') errors.value.push('タイトルは必須です')
if (title.value.length > 50) errors.value.push('タイトルは50文字以内です')
if (errors.value.length > 0) return
saved.value = true
}テンプレートVUE
<form @submit.prevent="submit">
<input v-model="title" aria-describedby="title-error">
<p v-for="error in errors" :key="error" id="title-error">{{ error }}</p>
<button>登録</button>
</form>
<p v-if="saved">登録しました</p>エラーを色だけで伝えず、文章でも伝えます。ラベル、aria-describedby、キーボード操作も確認すると利用しやすいフォームになります。
fetchでAPIを呼び出す
通信処理はコンポーネントの表示処理と分け、レスポンスのステータスを確認します。JSONでないレスポンスやネットワークエラーも起こる前提で処理します。
一覧を取得するVUE
import { ref, onMounted } from 'vue'
const items = ref([])
const status = ref('idle')
const errorMessage = ref('')
async function loadItems() {
status.value = 'loading'
try {
const response = await fetch('/api/items')
if (response.ok === false) throw new Error(`HTTP ${response.status}`)
items.value = await response.json()
status.value = 'success'
} catch (error) {
errorMessage.value = error.message
status.value = 'error'
}
}
onMounted(loadItems)onMounted(loadItems)で初回表示後に取得できます。再読み込みボタンから同じ関数を呼ぶ設計にすると、処理を再利用できます。
読み込み中・成功・失敗を表示する
状態ごとの表示VUE
<p v-if="status === 'loading'">読み込み中です</p>
<p v-else-if="status === 'error'" role="alert">
{{ errorMessage }}
<button @click="loadItems">再試行</button>
</p>
<ul v-else-if="status === 'success'">
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
<p v-else>まだ読み込んでいません</p>空配列を「通信失敗」と表示しないよう、通信状態とデータ件数を分けて考えます。画面を離れても結果が返る可能性がある場合はAbortControllerで中断し、認証情報や個人情報をログへ出さないことも重要です。
練習の発展
取得した一覧に検索語を適用する場合は、元の配列を変更せず、
取得した一覧に検索語を適用する場合は、元の配列を変更せず、
computedで絞り込んだ配列を作ってみましょう。