Vue.js

フォームとAPI通信

入力値を検証し、通信の状態をUIへ分かりやすく表示します。

Vue.js入門の講座一覧

01

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は可能なら数値へ変換します。必須入力や範囲などの業務ルールは、送信処理で明示的に確認します。

02

入力を検証して送信する

入力欄の近くにエラーを表示し、エラーがあるときは送信処理を止めます。画面上の検証だけに頼らず、サーバー側でも同じ検証を行ってください。

登録フォーム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、キーボード操作も確認すると利用しやすいフォームになります。

03

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)で初回表示後に取得できます。再読み込みボタンから同じ関数を呼ぶ設計にすると、処理を再利用できます。

04

読み込み中・成功・失敗を表示する

状態ごとの表示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で絞り込んだ配列を作ってみましょう。