Vue.js②・フォーム処理
フォーム処理
Webアプリでは,テキストフィールド や ラジオボタン などのフォームを利用して,ユーザの入力を受け付けることになります. これらのフォームに入力された値も,データバインディングの対象となります. ここでは,英単語のクイズ問題を作成して,クイズに対するユーザの回答を処理してみましょう. クイズの回答には,ユーザが文字列を入力するためのテキストフィールドや,選択肢から選ぶためのラジオボックスを利用します. また,ユーザに情報を伝達するために便利な ダイアログ の使い方も確認します.
CodePenの準備
CodePenにアクセスして,Penを作成し,タイトルを設定しましょう. Penのタイトルは「Chapter9」に設定しましょう.
前回と同様にVue.jsのライブラリをペンに追加してください. Vue.jsのバージョンは2.6.11を採用します.
また,Vue.jsの基本構造も記述しておきます.
Vueクラスのインスタンスには,el
,data
,methods
を指定します.
Webアプリのタイトルと説明文も記載しておきましょう.
<!-- HTML -->
<div id="app">
<h1> {{ title }} </h1>
<p> {{ description }} </p>
<div>
// JavaScript
let app = new Vue({
el: "#app",
data: {
title: "英単語クイズアプリ",
description: "英単語に関するクイズを出題します.",
},
methods: {
},
})
クイズ問題の作成
「英単語のスペル(spell)」と「英単語の意味(meaning)」を回答するクイズ問題を配列で作成します.
スペルの問題は,type
,question
,image
,answer
のキーを持つ連想配列です.
type
はspellに設定します.
image
はいらすとやで提供されている動物の画像のURLです.
同様に,意味の問題は,type
,question
,options
,answer
のキーを持つ連想配列です.
type
はmeaningに設定します.
options
は回答の選択肢であり,配列で表現することにします.
また,data
オプションに,クイズ問題を表すquizzes
,
回答中のクイズの問題番号を表すquiz_number
,
ユーザの回答を表すquiz_answer
を定義しておきます.
// JavaScript
quizzes = [
{
"type": "spell",
"question": "犬",
"image": "https://assets.codepen.io/4660782/dog.png",
"answer": "dog"
},
{
"type": "spell",
"question": "猫",
"image": "https://assets.codepen.io/4660782/cat.png",
"answer": "cat"
},
{
"type": "meaning",
"question": "house",
"options": ["家", "空", "水", "海"],
"answer": "家"
},
{
"type": "meaning",
"question": "school",
"options": ["学校", "消防署", "区役所", "交番"],
"answer": "学校"
},
]
let app = new Vue({
el: "#app",
data: {
title: "英単語クイズアプリ",
description: "英単語に関するクイズを出題します.",
quizzes: quizzes, // クイズ問題
quiz_number: 0, // クイズの問題番号
quiz_answer: "", // クイズの回答
},
methods: {
},
})
次に,HTMLでクイズの問題番号をマスタッシュ記法で表示します. 問題番号は0から始まるため,1だけ加算してから表示しています.
<!-- HTML -->
<div id="app">
<h1> {{ title }} </h1>
<p> {{ description }} </p>
<h3>問題{{quiz_number + 1}}</h3>
</div>
テキストフィールド
英単語のスペルを答える問題を実装します.
v-if
ディレクティブを利用して,クイズのタイプがspell
のとき,問題文question
と画像image
を表示します.
imgタグのsrc属性には,v-bind
ディレクティブを利用して,画像のURLを設定しています.
<!-- HTML -->
<div id="app">
<h1> {{ title }} </h1>
<p> {{ description }} </p>
<h3>問題{{quiz_number + 1}}</h3>
<!-- スペルを答える問題 -->
<div v-if="quizzes[quiz_number].type == `spell`">
<p>「{{ quizzes[quiz_number].question }}」 のスペルを答えなさい</p>
<div><img v-bind:src="quizzes[quiz_number].image"></div>
</div>
<div>
methods
オプションに,ユーザの入力した回答の正解を判定するためのcheck()
を定義します.
ここでは,alert()
を利用して,「正解」または「不正解」を表示することにします.
// JavaScript
methods: {
check(){
if(this.quiz_answer == this.quizzes[this.quiz_number].answer){
alert("正解");
}
else{
alert("不正解");
}
}
}
HTMLにテキストフィールド(input)とボタン(button)を設置します.
テキストフィールドのデータバインディングには,v-model
ディレクティブを利用します.
ここでは,テキストフィールドに入力された値が自動的にquiz_answer
に代入されます.
また,v-on
ディレクティブを利用して,ボタンをクリックしたときにcheck()
を実行しています.
<!-- HTML -->
<div id="app">
<h1> {{ title }} </h1>
<p> {{ description }} </p>
<h3>問題{{quiz_number + 1}}</h3>
<div v-if="quizzes[quiz_number].type == `spell`">
<p>「{{ quizzes[quiz_number].question }}」 のスペルを答えなさい</p>
<div><img v-bind:src="quizzes[quiz_number].image"></div>
<input v-model="quiz_answer"> // テキストフィールド
<button v-on:click="check">送信</button> // ボタン
</div>
<div>
キーポイント
- テキストフィールドのデータバインディングには v-modelディレクティブ
ラジオボタン
英単語の意味を答える問題を実装します.
v-if
ディレクティブを利用して,クイズのタイプがmeaning
のとき,問題文question
と選択肢options
を表示します.
options
は配列であるため,v-for
ディレクティブを利用して,要素option
と要素番号index
を一つずつ取り出しています.
選択肢はラジオボタン(input)で表現し,テキストフィールドと同様にv-model
でデータバインディングします.
データバインディングされたquiz_answer
には,ラジオボタンのvalue属性が代入されることに注意してください.
問題番号を表すquiz_number
を2に設定してから,実行結果を確認しましょう.
<!-- HTML -->
<div id="app">
<h1> {{ title }} </h1>
<p> {{ description }} </p>
<h3>問題{{quiz_number + 1}}</h3>
<div v-if="quizzes[quiz_number].type == `spell`">
<p>「{{ quizzes[quiz_number].question }}」 のスペルを答えなさい</p>
<div><img v-bind:src="quizzes[quiz_number].image"></div>
<input v-model="quiz_answer">
<button v-on:click="check">送信</button>
</div>
<!-- 意味を答える問題 -->
<div v-if="quizzes[quiz_number].type == `meaning`">
<p>「{{ quizzes[quiz_number].question }}」 の意味を答えなさい</p>
<div v-for="(option, index) in quizzes[quiz_number].options">
<label>
<input type="radio" v-bind:value="option" v-model="quiz_answer"> {{ option }}
</input>
</label>
</div>
<button v-on:click="check">送信</button>
</div>
<div>
キーポイント
- ラジオボタンのデータバインディングには v-modelディレクティブ
- inputタグのvalue属性が代入される
要素の参照
正解・不正解の表示にalert()
は不適切なので,dialogタグを代わりに利用しましょう.
次に示すように,正解用のダイアログと,不正解用のダイアログを作成します.
このとき,それぞれ異なるref
属性を設定します.
ref
属性はVue.jsでDOM要素を直接参照するために用いられます.
<!-- HTML -->
<dialog ref="correct_dialog">
<h3>正解</h3>
</dialog>
<dialog ref="wrong_dialog">
<h3>不正解</h3>
</dialog>
check()
を修正して,ダイアログを表示させます.
ダイアログのDOM要素を参照するには$refs
を利用します.
正解のダイアログはthis.$refs.correct_dialog
,不正解のダイアログはthis.$refs.wrong_dialog
で参照できます.
ダイアログのshowModal()
を実行すると,ダイアログが表示されます.
// JavaScript
methods: {
check(){
if(this.quiz_answer == this.quizzes[this.quiz_number].answer){
this.$refs.correct_dialog.showModal(); // 正解用のダイアログを表示
}
else{
this.$refs.wrong_dialog.showModal(); // 不正解用のダイアログを表示
}
}
}
キーポイント
- 参照したいタグにref属性を設定
- Vueでは$refsで参照
最後にクイズに答えたら自動的に次の問題に進むように修正します.
ダイアログにボタン(button)を設置し,v-on
ディレクティブでnextQuiz()
を実行します.
<!-- HTML -->
<dialog ref="correct_dialog">
<h3>正解</h3>
<button v-on:click="nextQuiz()">次の問題へ</button>
</dialog>
<dialog ref="wrong_dialog">
<h3>不正解</h3>
<button v-on:click="nextQuiz()">次の問題へ</button>
</dialog>
methods
オプションで,nextQuiz()
を定義します.
クイズの問題番号quiz_number
に1を加算して,ユーザの入力した回答を初期値""
にします.
また,ダイアログをclose()
で非表示にします.
// JavaScript
methods: {
check(){
if(this.quiz_answer == this.quizzes[this.quiz_number].answer){
this.$refs.correct_dialog.showModal(); // 正解用のダイアログを表示
}
else{
this.$refs.wrong_dialog.showModal(); // 不正解用のダイアログを表示
}
},
nextQuiz(){
// クイズの問題番号を1だけ加算する
this.quiz_number += 1;
this.quiz_answer = "";
this.$refs.correct_dialog.close(); // 正解用のダイアログを非表示
this.$refs.wrong_dialog.close(); // 不正解用のダイアログを非表示
}
}
アプリの確認
See the Pen Chapter9 by Naoto Mukai (@nmukai) on CodePen.
課題
ユーザがクイズに正解した回数と間違えた回数を表示してください.
課題を完成させたら,Penの ZIPファイル と リンク を提出してください. 提出方法は初回のWebアプリの開発を参考にしてください.