Vue.jsでアプリケーションを構築する時に入力フォームを設置する場合はVue.jsを使って入力フォームの要素を制御する必要があります。input, select, textareaなど要素によって設定方法が異なるので、使いこなせるようにしっかりと理解しておきましょう。

Vueのバージョン3からはコードの記述方法が2つ存在します。Vueのバージョン2までの記述方法と同じOptions APIとVue3から新たに利用できるComposition APIです。両方の方法で設定方法を確認していくので記述方法の違いも同時に理解することができます。

入力フォームの使い方がわかったらinput要素やselect要素のコンポーネント化をすることをおすすめします。一度それらの要素をコンポーネント化するとプロジェクトを超えて再利用することができるのでフォームのデザインを統一することができます。

プロジェクトの作成

動作確認を行うために”npm create vue@latest”コマンドでプロジェクトの作成を行います。コマンドを実行するとProject name, TypeScriptなどの選択を行いますが、ここではProject Nameに”vue-input-form”を設定して残りの選択はすべでデフォルトの”No”を選択しています。


 % npm create vue@latest

> npx
> create-vue


Vue.js - The Progressive JavaScript Framework

✔ Project name: … vue-input-form
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … No / Yes
✔ Add Vue DevTools 7 extension for debugging? (experimental) … No / Yes

プロジェクトの作成が完了したらプロジェクトディレクトリに移動してnpm installコマンドを実行します。


 % cd vue-input-form
 % npm install

main.jsファイルでデフォルトのスタイルのimport文をコメントします。


// import './assets/main.css'

import { createApp } from 'vue';
import App from './App.vue';

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

動作確認用の環境構築は完了です。

v-modelディレクティブ

入力フォームを構成する要素とVue.jsで定義したデータプロパティを結びつけるためにv-modelディレクティブを利用します。v-modelを利用することでinput要素、textarea要素、select要素に入力した値をVue.jsで定義したデータに反映させることができます。また定義したデータを更新することでinput要素に表示されている文字列も更新することができます。本文書では各要素でのv-modelの使用方法を確認しながら理解を深めていきます。

Vue.jsではv-を接頭語にしたディレクティブという特別な属性が多数存在します。Vue.jsはそのディレクティブを識別してどのような動作を行うか判断します。入力フォームの値とVue.jsのデータを結びつける場合はv-modelを利用します。
fukidashi

テキストボックスの場合

テキストボックスでv-modelディレクティブを使用する場合は下記のように記述します。


<input type="text" v-model="プロパティ名">

実際にテキストボックスを使って動作確認を行います。App.vueファイルを開いてinput要素にv-modelディレクティブを追加してtextInputを設定します。textInputはVue.jsでデータプロパティとして定義する必要があります。


<template>
  <input type="text" v-model="textInput" />
</template>

【Composition APIの場合】

ref関数をimportしてtextInput変数を定義します。ref関数の引数には初期値を設定することができますがここでは初期値には”(ブランク)としています。


<script setup>
import { ref } from 'vue';

const textInput = ref('');
</script>	

【Options APIの場合】

Vue.jsのdata関数の中に戻り値にtextInputプロパティを追加し、初期値は”(ブランク)としています。


<script>
export default {
  data() {
    return {
      textInput: '',
    };
  },
};
</script>

画面上には、input要素によるテキストボックスの入力エリアが表示されます。この状態ではテキストボックスに何か文字列を入力してもテキストボックスに入力した文字が表示されるだけでそれ以外は何も変化がありません。

input テキスト入力フォーム
input テキスト入力フォーム

次に入力した内容を見るためにはVue.jsのデータプロパティであるtextInputプロパティを画面に表示させます。{{ textInput }}を使ってhtmlの中に埋め込みます。


<template>
  <input type="text" v-model="textInput" />
  <p>{{ textInput }}</p>
</template>

テキストボックスに文字列を打ち込むと入力した内容がそのままテキストボックスの下に表示されます。このようにv-modelを使用することで入力したデータが即座にVue.jsのtextInputに反映され、そのデータがブラウザ上に表示されることがわかります。

inputフォームの下に表示
inputフォームの下に表示

通常のテキストボックスの場合はvalue属性に値(<input text=”type” value=”初期値”>)を設定するとその値がテキストボックスに入った状態で表示されます。しかし、v-modelを使っている場合はvalue値を事前に入力してもvalueに設定した値はブラウザには表示されません。

【Composition APIの場合】

input要素に初期値を設定するためにref関数の引数を利用します。


<script setup>
import { ref } from 'vue';

const textInput = ref('初期値はここに入れておく');
</script>

【options APIの場合】

input要素に初期値を設定するためにtextInputプロパティに値を設定します。


<script>
export default {
  data() {
    return {
      textInput: '初期値はここに入れておく',
    };
  },
};
</script>

ブラウザで確認すると初期値が入力された状態で表示されます。

初期値を入力
初期値を入力

v-modelを使った記述方法をsyntax sugar(糖衣構文)といい、書き方をシンプルでわかりやすくしています。実際はv-bindとv-on:input(=@input)を使って以下のように記述することが可能です。


<input v-bind:value="textInput" v-on:input="textInput = $event.target.value" />

少し上級者向けの内容になるかもしれませんが、syntax sugarについては下記の文書でも説明を行っています。

双方向バインディングとは

テキストボックスのv-modelがどのようなものかは理解できたと思いますので、用語の確認を行なっておきます。

v-modelの説明を調べていくと双方向バインディングやTwo wayバインディングという用語をよく目にします。それらがどういう意味なのか確認していきましょう。

バインディングやバインド?

バインディング(binding)とバインド(bind)は英語ではどちらも同じ意味で何かと何かを結びつけることを意味します。

Composition APIの場合

Composition APIではref関数またはreactive関数を利用して変数を定義し、htmlの要素(下記ではp要素)にv-textを設定することで定義した変数の値をhtmlの要素の中に表示させることができます。このようにVueで定義した変数と要素が結びついているのでバインドまたはバインディングといいます。


<script setup>
import { ref } from 'vue';

const textInput = ref('バインディングとは');
</script>

<template>
  <p v-text="textInput"></p>
</template>

Options APIの場合

Options APIではdataプロパティを設定し、htmlの要素(下記ではp要素)にv-textを設定することでdataプロパティの値をhtmlの要素の中に表示させることができます。このようにdataプロパティと要素が結びついているのでバインドまたはバインディングといいます。


<script>
export default {
  data() {
    return {
      textInput: 'バインディングとは',
    };
  },
};
</script>
<template>
  <p v-text="textInput"></p>
</template>

双方向(two way)バインディング

Options APIのコードを利用して双方向バインディングを説明しています。

双方向(Two Way)ということは2方向のバインディングが存在することを表しています。

一つ目の方向は、Vueインスタンスの中で設定したdataプロパティの値がinput要素と結びついてブラウザ上に表示されるバインドです。二つ目の方向はinput要素に入力した値がVueインスンスのdataプロパティの値に反映されるバインドです。このように2つの方向を持っているので双方向(two way)バインディングと呼ばれます。

双方向バインディング
Vue バージョン2のOptions APIでの双方向バインディングの図

先ほど出てきたv-modelの別の記述方法(Syntax Sugar)を確認することで双方向バインディングはより明確になります。v-bindディレクティブによりtextInputに設定した値がテキストブロックに表示され、v-onディレクティブによりテキストブロックに入力した値がtextInputに反映されます。


<script>
export default {
  data() {
    return {
      textInput: 'バインディングとは',
    };
  },
};
</script>
<template>
  <input
    v-bind:value="textInput"
    v-on:input="textInput = $event.target.value"
  />
  <p>{{ textInput }}</p>
</template>

テキストエリアの場合

テキストボックスでは改行を行うことができないので長い文章を入力することができません。改行が必要な長い文章を入力する場合はテキストエリアtextarea要素を使用します。テキストエリアでv-modelディレクティブを使用する場合は下記のように記述します。テキストエリアで入力した値が設定したプロパティに保存されます。


<textarea v-model="プロパティ名"></textarea>

App.vueファイルでは下記のように設定することができます。

【Compositions APIの場合】


<script setup>
import { ref } from 'vue';

const textInput = ref('TextAreaでの設定方法');
</script>
<template>
  <textarea v-model="textInput"></textarea>
  <p>{{ textInput }}</p>
</template>

【Options APIの場合】


<script>
export default {
  data() {
    return {
      textInput: 'TextAreaでの設定方法',
    };
  },
};
</script>
<template>
  <textarea v-model="textInput"></textarea>
  <p>{{ textInput }}</p>
</template>

テキストエリアなので値はtextareaタグの中に入れてしまいそうですが、下記のように記述しても双方向ではないので中身を更新してもVuejsのdataプロパティには値の反映は行われません。


<textarea>{{ textInput }}</textarea>

チェックボックスの場合

1つのチェックボックスの場合

v-modelを使用したチェックボックスの書式は下記となります。


<input type="checkbox" v-model="プロパティ名">

checkboxのプロパティはtrue, falseの真偽値のみ設定することが可能です。

チェックボックスを準備してプロパティはcheckValueとします。現在の設定値がわかるように{{ checkValue }}を使って値も表示させます。


<template>
  <input type="checkbox" v-model="checkValue" />
  <p>{{ checkValue }}</p>
</template>

チェックボックスではプロパティは真偽値をとるので、初期値はtrueとします。

【composition APIの場合】


<script setup>
import { ref } from 'vue';

const checkValue = ref(true);
</script>

【options APIの場合】


<script>
export default {
  data() {
    return {
      checkValue: true,
    };
  },
};
</script>

ブラウザで確認すると下記のように表示されます。初期値がtrueなので、ボックスが選択された形で表示されます。

チェックボックスの値をtrue
チェックボックスの値をtrue

チェックを外すと値はtrueからfalseに変わります。

チェックを外した場合
チェックを外した場合

true、falseしかとれないと説明しましたが、true-value、false-valueという属性を利用するとtrue, false以外の値をとることができます。

下記ではtrue-valueとfalse-valueを使用して”はい”と”いいえ”になるように変更を行なっています。


<template>
  <input
    type="checkbox"
    v-model="checkValue"
    true-value="はい"
    false-value="いいえ"
  />
  <p>{{ checkValue }}</p>
</template>

checkValueプロパティの初期値は”はい”とします。

【Composition APIの場合】


<script setup>
import { ref } from 'vue';

const checkValue = ref('はい');
</script>

【Options APIの場合】


<script>
export default {
  data() {
    return {
      checkValue: 'はい',
    };
  },
};
</script>

初期値が”はい”になっているので、チェックした状態で値は”はい”となります。

checkboxをチェックした状態
checkboxをチェックした状態

チェックを外すと”いいえ”となります。

checkboxを外した場合
checkboxを外した場合

このようにtrue-value, false-valueを設定することでtrue, falseの真偽値から任意の値を取れるようになることが確認できました。

複数のチェックボックスの場合

複数のチェックボックスを利用する場合の書式は下記の通りです。


<input type="checkbox" v-model="プロパティ名" value="値1">
<input type="checkbox" v-model="プロパティ名" value="値2">
<input type="checkbox" v-model="プロパティ名" value="値3">

1つのチェックボックスではデフォルトでは真偽値のtrue, falseの2つをとることができますが、複数のチェックボックスの場合は、各チェックボックスで設定したvalueの値を配列で取得します。

実際に確認してみましょう。

同じグループの場合は、v-modelには同じプロパティを設定し、valueにはそのチェックボックスが選択された時の値を設定します。checkValueプロパティの値をブラウザに表示できるように{{ checkValue }}も追加します。


<template>
  <input type="checkbox" v-model="checkValue" value="ラグビー" /> ラグビー
  <input type="checkbox" v-model="checkValue" value="サッカー" /> サッカー
  <input type="checkbox" v-model="checkValue" value="バスケットボール" />
  バスケットボール
  <p>{{ checkValue }}</p>
</template>

checkValueは配列なので、初期値は空の配列を設定します。

【Composition APIの場合】


<script setup>
import { ref } from 'vue';

const checkValue = ref([]);
</script>

【Options APIの場合】


<script>
export default {
  data() {
    return {
      checkValue: [],
    };
  },
};
</script>

デフォルトは何も選択していない状態なので、下記のようになります。

何も選択していない状態
何も選択していない状態

サッカーのみを選択すると配列にサッカーが入ることがわかります。

サッカーのみ選択
サッカーのみ選択

サッカーの次にバスケットボール、ラグビーと選択すると選択した順に値は配列に入っていくことがわかります。

すべてをチェックした状態
すべてをチェックした状態

このように複数のチェックボックスでは各チェックボックスに設定した値が配列としてVue.jsのdataプロパティの中に保存されることがわかります。

初期値を設定しておきたい時は下記のようにcheckValueの配列に値を入れておきます。

【Composition APIの場合】


<script setup>
import { ref } from 'vue';

const checkValue = ref(['サッカー']);
</script>

【options APIの場合】


<script>
export default {
  data() {
    return {
      checkValue: ['サッカー', '野球'],
    };
  },
};
</script>

チェックボックスに選択肢にないものを入れることも可能ですが、チェックボックスの選択からの影響は受けず配列に入ったままの状態となります。

【Composition APIの場合】


<script>
import { ref } from 'vue';

const checkValue = ref(['サッカー', '野球']);)
</script>

【Options APIの場合】


<script>
export default {
  data() {
    return {
      checkValue: ['サッカー', '野球'],
    };
  },
};
</script>

チェックボックスに選択する値がなくても野球は配列には入った状態になります。

チェックボックスに影響を受けない値
チェックボックスに影響を受けない値

すべてのチェックボックスを外しても配列に残った状態となり、チェックボックスからの影響は全く受けません。

チェックボックスに影響を受けない
チェックボックスに影響を受けない

ラジオボタンの場合

ラジオボタンでは複数の選択肢から1つを選択させたい場合に利用します。複数のチェックボックスと設定方法はほとんど同じですが、ラジオボタンでは値は配列ではなく文字列として取得します。

ラジオボタンの書式は下記の通りです。


<input type="radio" v-model="プロパティ名" value="値1">
<input type="radio" v-model="プロパティ名" value="値2">
<input type="radio" v-model="プロパティ名" value="値3">

実際にラジオボタンを使って動作確認を行なってみましょう。

同じグループの場合は、v-modelには同じプロパティを設定し、valueにはそのラジオボタンが選択された時の値を設定します。radioValueプロパティの値をブラウザに表示できるように{{ radioValue }}も追加します。


<template>
  <input type="radio" v-model="radioValue" value="ラグビー" /> ラグビー
  <input type="radio" v-model="radioValue" value="サッカー" /> サッカー
  <input type="radio" v-model="radioValue" value="バスケットボール" />
  バスケットボール
  <p>{{ radioValue }}</p>
</template>

radioValueは文字列なので、初期値は”(ブランク)を設定します。

【Composition APIの場合】


<script setup>
import { ref } from 'vue';

const radioValue = ref('');
</script>	

【Options APIの場合】


<script>
export default {
  data() {
    return {
      radioValue: '',
    };
  },
};
</script>	

ラジオボタンを選択していない状態です。

ラジオボタンを選択していない状態
ラジオボタンを選択していない状態

ラジオボタンを選択すると選択した値が{{ radioValue}}に表示されます。

ラジオボタンを選択した状態
ラジオボタンを選択した状態

ラジオボタンは複数の選択を行うことができないので、別のボタンに変更すると別の選択した値がブラウザ上に表示されます。

セレクトボックスの場合

1つを選択する場合

プルダウンメニューを使って選択肢の中から1つだけ選択する場合のセレクトボックスの設定方法を確認します。1つだけ選択を行うので値は文字列として取得できます。

セレクトボックスの書式は下記の通りです。


<select v-model="selectValue">
	<option>値1</option>
	<option>値2</option>
	<option>値3</option>
</select>
<p>{{ selectValue }}</p>

実際にセレクトボックスを使って動作確認を行なってみましょう。

select要素にv-modelでselectValueプロパティを設定します。プルダウンメニューで選択した値はこのselectValueプロパティの値に入ります。selectValueの値は{{ selectValue }}でブラウザに表示されます。

下記のコードではoptionにdisabledが設定されていますが、disabledを設定した項目は選択することができません。ページを開いた直後はこの選択項目が表示されていますが一度選択を開始するとdisabledの項目を選択することができなくなります。
fukidashi

<template>
  <select v-model="selectValue">
    <option disabled value="">興味のあるスポーツを選択</option>
    <option>ラグビー</option>
    <option>サッカー</option>
    <option>バスケットボール</option>
  </select>
  <p>{{ selectValue }}</p>
</template>

selectValueプロパティの値は文字列なので””(ブランク)を設定しています。

【Composition APIの場合】


<script setup>
import { ref } from 'vue';

const selectValue = ref('');
</script>	

【Options APIの場合】


<script>
export default {
  data() {
    return {
      selectValue: '',
    };
  },
};
</script>	

ページを開いた直後は、disabledの項目が表示されています。

プルダウンメニューで何も選択されていない状態
プルダウンメニューで何も選択されていない状態

プルダウンメニューで選択すると下記のように選択した値がブラウザに表示されます。

プルダウンメニューで選択
プルダウンメニューで選択

プルダウンメニューを選択している状態です。disabledに設定した項目は選択することができません。

プルダウンメニューを選択している状態
プルダウンメニューを選択している状態

selectValueにデフォルト値を選択するとプルダウンメニューで選択された状態で表示されます。

下記のようにdesign_usersがオブジェクトの場合ブラウザ上の選択では名前を表示させ、保存する値がidの場合は:value=”design_user.id”と設定することで名前ではなくidを保存することができます。


<script setup>
import { ref } from 'vue';

const design_users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' },
];

const selectValue = ref('');
</script>
<template>
  <select v-model="selectValue">
    <option disabled value="">デザイナーの選択</option>
    <option
      v-for="design_user in design_users"
      :key="design_user.id"
      :value="design_user.id"
    >
      {{ design_user.name }}
    </option>
  </select>
  <p>{{ selectValue }}</p>
</template>

デザイナーを選択すると名前ではなくidがブラウザ上に表示されます。

複数を選択する場合

一つの選択の場合は、文字列で値を取得できましたが、複数の場合は配列で値を取得することができます。

複数を選択可能にするためには、select要素にmultipleを設定する必要があります。html部分の記述に関しては1つを選択する場合と複数を選択する場合の違いはmultipleを追加するかどうかの違いです。


<template>
  <select v-model="selectValues" multiple>
    <option>ラグビー</option>
    <option>サッカー</option>
    <option>バスケットボール</option>
  </select>
  <p>{{ selectValues }}</p>
</template>

値を複数選択することができるので、select_valuesプロパティの初期値は配列を設定します。

【Composition APIの場合】


import { ref } from 'vue';

const selectValues = ref([]);

【Options APIの場合】


<script>
export default {
  data() {
    return {
      selectValues: [],
    };
  },
};
</script>

selectにmultipleを設定するとプルダウンメニューではなく複数の要素を選択する画面になります。

select要素による複数選択画面
select要素による複数選択画面

複数選択を行うと選択した値が配列で表示されます。

複数要素を選択した画面
複数要素を選択した画面

ファイルの場合

HTMLの入力フォームではinput要素のtypeをfileに設定することでファイル情報を取得することができます。しかし、v-modelではファイルタイプを扱うことはできません。その代わり、changeイベントを利用してファイル情報を取得することができます。

これまではv-modelを使用していましたがファイルの場合はchangeイベントを利用します。changeイベントを設定しているので、ファイルの選択が行われれるとchangeイベントに設定されているhandleFileメソッドが実行されます。

changeイベントはv-onディレクティブを使用して設定を行います。
fukidashi

<template>
  <input type="file" v-on:change="handleFile" />
  {{ fileData }}
</template>

handleFileメソッドでeventからファイルの情報を取得してfileDataプロパティにその値を設定します。

【Composition APIの場合】


<script setup>
import { ref } from 'vue';

const fileData = ref('');
const handleFile = (event) => {
  const file = event.target.files[0];
  fileData.value = file;
};
</script>

【options APIの場合】


<script>
export default {
  data() {
    return {
      fileData: '',
    };
  },
  methods: {
    handleFile(event) {
      const file = event.target.files[0];
      this.fileData = file;
    },
  },
};</script>	
ファイル選択画面
ファイル選択画面

ファイルを選択するとchangeイベントにより、filehandleメソッドが実行され、画面にはFileオブジェクトが表示されます。

ファイルを選択後の画面
ファイルを選択後の画面

Fileオブジェクトだけだとわかりにくいので、取得したファイル情報から名前を取得します。画面にはアップロードしたファイル名が表示されます。


//composition API
fileData.value = file.name;

//options API
this.fileData = file.name;

まとめ

入力フォームに必要な一つ一つの要素を確認していくとVue.jsでの設定がそれほど難しくないことが理解できたかと思います。ぜひ本文書の知識を生かしてVue.jsでの入力フォームの作成にチャレンジしてください。

入力フォームを作成した後はバリデーションライブラリが必要になります。Vue.jsのバリデーションライブラリも何種類かあるのでそれらの中からいくつかピックアップしています。