Vue.js②・フォーム処理

Image from Gyazo

フォーム処理

Webアプリでは,テキストフィールドラジオボタン などのフォームを利用して,ユーザの入力を受け付けることになります. これらのフォームに入力された値も,データバインディングの対象となります. ここでは,英単語のクイズ問題を作成して,クイズに対するユーザの回答を処理してみましょう. クイズの回答には,ユーザが文字列を入力するためのテキストフィールドや,選択肢から選ぶためのラジオボックスを利用します. また,ユーザに情報を伝達するために便利な ダイアログ の使い方も確認します.

CodePenの準備

CodePenにアクセスして,Penを作成し,タイトルを設定しましょう. Penのタイトルは「Chapter9」に設定しましょう.

Image from Gyazo

前回と同様にVue.jsのライブラリをペンに追加してください. Vue.jsのバージョンは2.6.11を採用します.

Image from Gyazo

また,Vue.jsの基本構造も記述しておきます. Vueクラスのインスタンスには,eldatamethodsを指定します. Webアプリのタイトルと説明文も記載しておきましょう.

<!-- HTML -->
<div id="app">
  <h1> {{ title }} </h1>
  <p> {{ description }} </p>
<div>
// JavaScript
let app = new Vue({
  el: "#app",
  data: {
    title: "英単語クイズアプリ",
    description: "英単語に関するクイズを出題します.",
  },
  methods: {
  },
})

Image from Gyazo

クイズ問題の作成

「英単語のスペル(spell)」と「英単語の意味(meaning)」を回答するクイズ問題を配列で作成します. スペルの問題は,typequestionimageanswerのキーを持つ連想配列です. typeはspellに設定します. imageいらすとやで提供されている動物の画像のURLです. 同様に,意味の問題は,typequestionoptionsanswerのキーを持つ連想配列です. 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>

Image from Gyazo

テキストフィールド

英単語のスペルを答える問題を実装します. 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>

Image from Gyazo

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>

Image from Gyazo

キーポイント

  • テキストフィールドのデータバインディングには 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>

Image from Gyazo

キーポイント

  • ラジオボタンのデータバインディングには 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(); // 不正解用のダイアログを表示
    }
  }
}

Image from Gyazo

キーポイント

  • 参照したいタグに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.

課題

ユーザがクイズに正解した回数と間違えた回数を表示してください.

Image from Gyazo

課題を完成させたら,Penの ZIPファイルリンク を提出してください. 提出方法は初回のWebアプリの開発を参考にしてください.

参考書籍

愛知県名古屋市にある椙山女学園大学 文化情報学部 向研究室の公式サイトです. 専門は情報科学であり,人工知能やデータベースなどの技術要素を指導しています. この公式サイトでは,授業で使用している教材を公開すると共に, ベールに包まれた女子大教員のミステリアスな日常を4コマ漫画でお伝えしていきます. サイトに関するご意見やご質問はFacebookまたはTwitterでお問い合わせください.