🗂

Vue3 + Composition API でフォーム操作

7 min read

Vue3 を使ってフォーム操作のサンプルを作ります。

プロジェクトの作成

$ vue create form-handling

? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, TS, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

Semantic UI Vueの導入

見た目を整えるために Semantic UI Vue をインストールします。

$ npm install semantic-ui-css --save
src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import "semantic-ui-css/semantic.min.css";  # add this line.

createApp(App).mount("#app");

@click イベントを発生させる

ボタンをクリックして、console.log にコメントを出力する。

<template>
  <div>
    <button
      class="ui button"
      @click="onSampleClick()"
    >
      Sample
    </button>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "ButtonRow",
  setup(_props) {
    const onSampleClick = () => {
      console.log('The user clicked button-sample');
    }
    return {
      onSampleClick,
    }
  },
});
</script>

</script>

v-modelinputの値を同期する

Submit ボタンをクリックすると、console.loginput に入力した値が表示されます。

<template>
  <div class="input-form">
    <form
      novalidate
      class="ui form"
      @submit.prevent="onSubmit"
    >
      <div class="field">
        <input
          v-model="newItem"
          type="text"
          placeholder="Add an item!"
        >
      </div>
      <button
        type="submit"
        class="ui button"
      >
        Submit
      </button>
    </form>
    <div />
  </div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  name: "SampleInput",
  setup(_props) {
    const newItem = ref<String>('')
    const onSubmit = () => {
      console.log('New Item:', newItem.value);
    }
    return {
      onSubmit,
      newItem
    }
  },
});
</script>

inputの値を配列に追加してリストで表示

テキストフィールドに文字を入れて、 Enter を押すたびにリストが追加されていきます。

<template>
  <div class="input-form">
    <form
      novalidate
      class="ui form"
      @submit.prevent="onSubmit"
    >
      <div class="field">
        <input
          v-model="newItem"
          type="text"
          placeholder="Add an item!"
        >
      </div>
      <button
        type="submit"
        class="ui button"
      >
        Submit
      </button>
    </form>
    <div class="ui segment">
      <h4 class="ui header">
        Items
      </h4> <ul>
        <li
          v-for="item in items"
          :key="item"
          class="item"
        >
          {{ item }}
        </li>
      </ul>
    </div>
    <div />
  </div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  name: "SampleInput",
  setup(_props) {
    const newItem = ref<String>('')
    const items = ref<Array<String>>([])
    const onSubmit = () => {
      console.log('New Item:', newItem.value);
      items.value.push(newItem.value)
      newItem.value = ''
    }
    return {
      onSubmit,
      newItem,
      items
    }
  },
});
</script>

複数のフィールドの値を同期して表示する

以下の複数のタイプのフィールドの値を同期させて画面に表示させてみる。

  • input
  • email
  • select
  • input type="checkbox"

<template>
  <div class="input-form">
    <form
      novalidate
      class="ui form"
      @submit.prevent="onSubmit"
    >
      <div class="field">
        <label>New Item</label>
        <input
          v-model="field.newItem"
          type="text"
          placeholder="Add an item!"
        >
      </div>
      <div class="field">
        <label>Email</label>
        <input
        v-model="field.email"
        type="text"
        placeholder="What's your email?"
        />
      </div>
      <div class="field">
        <label>Urgency</label>
        <select v-model="field.urgency" class="ui fluid search dropdown">
          <option disabled value="">Please select one</option>
          <option>Nonessential</option>
          <option>Moderate</option>
          <option>Urgent</option>
        </select>
      </div>
      <div class="field">
        <div class="ui checkbox">
          <input v-model="field.termsAndConditions" type="checkbox" />
          <label>I accept the terms and conditions</label>
        </div>
      </div>
      <button
        type="submit"
        class="ui button"
      >
        Submit
      </button>
    </form>
    <div class="ui segment">
      newItem: {{ field.newItem }} <br/>
      email: {{ field.email }} <br/>
      urgency: {{ field.urgency }} <br/>
      termsAndConditions: {{ field.termsAndConditions }} <br/>
      <h4 class="ui header">Items</h4>
      <ul>
        <li
          v-for="item in field.items"
          :key="item"
          class="item"
        >
          {{ item }}
        </li>
      </ul>
    </div>
    <div />
  </div>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";

interface Field {
  newItem: string,
  email: string,
  items: string[],
  urgency: string,
  termsAndConditions: boolean
}
export default defineComponent({
  name: "SampleInput",

  setup(_props) {

    const field = ref<Field>({
      newItem: '',
      email: '',
      items: [],
      urgency: '',
      termsAndConditions: false
    })

    const onSubmit = () => {
      field.value.items.push(field.value.newItem)
      field.value.newItem = ''
    }
    return {
      onSubmit,
      field: field.value
    }
  },
});
</script>

参考

Vue JS 3 Composition API → Registration Form Validation [2021]