作成: @kiriem_
Vue.js は JavaScript のライブラリで、比較的簡単にWebアプリケーションを開発することができます。今回は、Vue.jsを使って簡単なクイズアプリを作ってみましょう。
詳しく学びたい方は、こちらの公式ドキュメントにあるチュートリアルを見てください。
アプリ開発を始める前に、開発環境を整えましょう。
Visual Studio Code をインストールする
GitHub からリポジトリをクローン(ダウンロード)する。
VSCode を開く。
Google Chrome をインストールする(インストールされていない人向け)
これから開発していくアプリの具体的なイメージを持つために、まずはデモ版を触ってみましょう。
index.html
を Google Chrome で開く。アプリケーションを開発するために必要となるプログラムの基本的な事柄を理解しましょう。
今回使う言語は HTML
, CSS
, JavaScript
です。それぞれ別々の役割を持っています。
HTML
: ページの骨子を作るためのマークアップ言語。CSS
: ページのデザイン(色や配置など)をするための言語。JavaScript
: ページの動き(ボタンを押したときにすることや、何を表示するかを決めるなど)をするための言語。それぞれの言語をそのまま使ってプログラムを書くこともできますが、覚えることが膨大で大変になるので、今回はかんたんに使うためにフレームワーク
を利用します。
サンプルファイルには事前に Bootstrap と Vue.js が使えるようになっています。特に意識せずプログラムを開発していきましょう。
プログラミングは頭で理解するより手を動かしたほうが身につきます。さっそく作っていきましょう。
base.html
を開き、ソースコードを全選択してコピーし、 app.html
にペーストする。app.html
を保存する。Windows では Ctrl+S キーを、 macOS の場合は Cmd+S
キーを押す。ファイル名を app.html
にして、 base.html
と同じフォルダに保存する。htmlファイルの解説をします。まずは前半部分です。 一般的なHTMLファイルは次のような構成になっています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ページのタイトル</title>
</head>
<body>
ページの表示部分
</body>
</html>
次に app.html を見てください。 <head> </head>
タグの部分だけを抜粋しました。
<!DOCTYPE html>
<html lang="ja">
<head>
<!-- サイトのメタ情報(いじらない) -->
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- fabicon (Webアイコン) の設定(いじらない) -->
<link rel="apple-touch-icon" type="image/png" href="images/favicon/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="images/favicon/icon-192x192.png">
<!-- OGP 設定(いじらない) -->
<meta property="og:title" content="Quiz App">
<meta property="og:description" content="クイズアプリ">
<meta property="og:url" content="https://inpw.jp/app/quizapp">
<meta property="og:image" content="https://inpw.jp/app/quizapp/images/ogp.png">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Quiz App">
<meta name="twitter:card" content="summary_large_image">
<!-- Bootstrap の読み込み(いじらない) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+JP" rel="stylesheet">
<!-- CSSの読み込み(いじらない) -->
<link rel="stylesheet" href="main.css">
<!-- サイトのタイトル -->
<title>クイズアプリ</title>
</head>
<head></head>
で囲まれた部分は、基本的には目に見えるものではなくファイルの設定などをする場所です。今回は Bootstrap を読み込んだり、サイトのアイコン(ブラウザのタブに表示されるもの)やOGP(SNSでシェアしたときに表示される画像)の設定をしています。基本的にはいじらないようにしましょう。
OGPの例:
一箇所だけ修正してほしいタグがあります。サイトのタイトルを変えましょう。35行目にある <title>クイズアプリ</title>
の部分を好きなタイトルに修正してください。
<!-- サイトのタイトル -->
<title>クイズアプリ</title> //ここを修正する
修正したら保存しましょう。保存は Ctrl+S / Cmd+S
などショートカットキーを使うようにしましょう。編集し終わったらこまめに保存しておくと、もし VSCode が落ちてしまったときなどに痛い目に合わずに済みます。
次に、Google Chrome で app.html
ファイルを開きます。エディタで修正したらブラウザを開き更新(再読み込み)する癖をつけてください。編集した部分が変わっていない場合は、VSCode側で保存するのを忘れているか、再読み込みしていない可能性があります。
タブに書いてある名前が変更できていれば成功です。
タブの名前を変えただけではあまり感動しないかもしれません。次は画面に表示されているクイズのタイトルと説明を修正しましょう。VSCodeに戻り、次のコードを探してください。
<!-- クイズのタイトルを表示する領域 -->
<div class="row my-3">
<div class="col-12">
<h1 class="text-center main-title">クイズタイトル</h1>
<p class="text-center">クイズの説明がはいります</p>
</div>
</div>
見つけたら、「クイズタイトル」の部分と「クイズの説明がはいります」の部分を、好きな言葉に変更してください。変更し終わったら必ず保存して、ブラウザで確認してみましょう。
3.3 で修正したプログラムをもう一度見てください。ここには3つのタグが出てきてます。
<div></div> //まとまりを作るタグ
<h1></h1> //タイトルタグ
<p></p> //本文タグ
これらはすべてHTMLのタグです。中学校や高校の技術や情報の授業でやったことがある方もいらっしゃるかもしれません。基本的にはその当時やったものと変わっていないはずです。
しかし、div タグや h1 タグの中になにやらいろいろと書いてあります。class=""
の中に書かれているのが Bootstrap の命令とCSS の命令です。h1タグを例に見てみましょう。
<h1 class="text-center main-title">クイズタイトル</h1>
ここでは、 text-center
と main-title
というクラスがつけられています。このうち、text-center
は Bootstrap の命令、 main-title
は CSS の命令です。
Bootstrap は本来であれば CSS で書かねばならない命令を事前にまとめてくれているフレームワークです。例えば text-center
をCSSで書くと次のようになります。
h1{
text-align: cetner;
}
これをいちいちCSSで定義するのは面倒なので、Bootstrap は HTML の中に直接に書けるようにしてくれています。text-center のように一行の場合はまだかんたんですが、もっと複雑で長いプログラムを1つにまとめてくれている命令もあります。
では、 main-title
のほうはどうでしょうか。一見すると Bootstrap と同じように見えますが、これは自分で定義した命令になります。VSCodeで main.css を開いて、22行目を見てください。
.main-title{
font-size: 28px; //フォントサイズを28pxにする
font-weight: bold; //フォントを太くする
}
main-titleという命令は、フォントサイズの変更と太さの変更によってできていることがわかります。このように、CSSではいくつかの命令のまとまりをHTMLのタグに適用していくことでデザインをしていきます。
それでは試しに、 font-size を大きくしてみましょう。好きなサイズに変えてみてください。
修正したら CSS ファイルを保存してブラウザを再読み込みしましょう。もし変わらなかった場合は、強制再読み込みをしてください。Shift+Cmd/Ctrl+R
を同時に押すと強制再読み込みされます。フォントサイズが変わっているでしょうか?
このように、Bootstrap で定義されている命令もあれば自分で定義することもできます。自分で定義をする場合には、 main.css に書き込んでください。
Bootstrap はCSSの様々な命令をまとめてくれているフレームワークですが、最も恩恵を受けるのがグリッドシステムです。グリッドシステムをつかえば、ボタンや画像、タイトルなどのパーツの配置がかんたんにある程度のクオリティを持ってすることができます。例えば、次のHTMLを実行するとこのようなレイアウトを作ることができます。
<div class="container">
<div class="row">
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
<div class="col-sm">
One of three columns
</div>
</div>
</div>
あるいは、このようなレイアウトも作ることができます。
<div class="container">
<div class="row">
<div class="col">
1 of 3
</div>
<div class="col-6">
2 of 3 (wider)
</div>
<div class="col">
3 of 3
</div>
</div>
<div class="row">
<div class="col">
1 of 3
</div>
<div class="col-5">
2 of 3 (wider)
</div>
<div class="col">
3 of 3
</div>
</div>
</div>
Bootstrap のグリッドシステでは、画面全体を12分割したうちの何個分の領域を使うかによってパーツの幅を決めることができます。実際のアプリの画面とコードを見ながら理解していきましょう。
例えば、この画面では次のようなプログラムをしています。
<!-- 選択肢を表示する領域 -->
<div class="col-md-8 offset-md-2 bg-light p-3">
<p class="font-bold">【次の3つから選ぼう】</p>
<ol>
<li v-for="answer in questions[currentQuestionNumber].answers" class="my-3">
{{answer}}
</li>
</ol>
</div>
2行目の <div></div>
で col-md-8
を設定しています。これが8個分の幅を使うという命令です。また、さらに offset-md-2
を設定することで、左右に2つ分の余白を設定しています。
左の余白2つ分 + 中央の表示領域8個分 + 右側の余白2つ分 を合計すると全体で12になります。これで8個分の領域を画面の中央に表示させることができるのです。
さらに、Bootstrap のグリッドシステムを使うことで、レスポンシブ対応をすることができます。最近ではスマートフォンでホームページを見ることが当たり前になりつつありますが、スマホの画面は基本的に縦長です。それに対して、コンピュータやタブレットでは横長で見ることが一般的でしょう。その際に、コンピュータを前提として作ってしまうとスマートフォンの表示にあっていなかったり、その逆もまた然りといったことが起こってしまいます。
それに対応するのがレスポンシブデザインです。レスポンシブ対応をしておけば、どんな画面サイズのデバイスで見ようと、ある程度きれいな形で表示してくれるようになります。
Bootstrap のグリッドシステムはとても優秀なので、どんどん使っていきましょう。基本的には次のような構成になっています。
<div class="container"> //グリッドの全体
<div class="row"> //1つの行のまとまり
<div class="col"> //1つのカラム
//ここにいろいろなものを追加していく
</div>
</div>
</div>
基本的に container は1つのページにつき1つあればよいでしょう。row は1つのまとまりごとにつくるとよいです。
1つの row は12等分され、その中に含まれている col が指定する数によって幅は変動していきます。それでは、もし1つのrowに含まれている col の合計数が12を超えてしまった場合はどうなるでしょうか。アプリの画面を見ながら考えてみましょう。
index.html にはクイズの一覧が表示されていますが、それぞれの幅は col-md-3 で設定されています。このカラムを複数コピーすると、次の写真のように表示されます。
全体の個数が12を上回ると、下の段に追加されていくのがわかるかと思います。よって、一覧表示の場合どでは、1つのrowの中に複数のカラムを追加していくことが一般的です。
これでなんとなくグリッドシステムについて知ることができたでしょうか。さらに詳しく知りたい方は、公式ドキュメントを御覧ください。
https://getbootstrap.jp/docs/4.5/layout/grid/
なんとなくアプリの構造が解ってきたところで、早速アプリの機能を作っていきましょう。その際に、このテキストで紹介されているプログラムをコピペするのではなく、自分の手で入力すると力がついていきますので、プログラミングスキルを習得したい方は、ぜひ頑張って挑戦してみてください。
まずはHTMLとBootstrap を使ってアプリの見た目を完成させていきましょう。頭で理解するより前に手を動かして慣れることを目標にしましょう。
app.html の中に「問題数を表示する領域」と書かれている部分があります。その下に、次のプログラムを追加してください。
<div class="col-md-8 offset-md-2">
<p>第1問 / 全8問</p>
</div>
このように表示されれば成功です。
次に問題文を表示する領域を作りましょう。「問題文を表示する領域」と書かれている行の下に、次のプログラムを追加しましょう。
<!-- 問題文を表示する領域 -->
<div class="col-md-8 offset-md-2">
<div class="questionBox">
<span class="box-title">問題</span>
<p>ここに問題文が入る</p>
</div>
</div>
このプログラムの4行目のdivタグで指定している questionBox
と box-title
はmain.cssで定義しています。「問題」というテキストや枠線の色を変えるには、main.cssを編集してください。
※ 参考 main.css で定義されているCSS。すでに定義されているので書かない。
/*== 問題文を表示するテキストボックスのデザイン ==*/
.questionBox {
position: relative;
margin: 2em 0;
padding: 0.5em 1em;
border: solid 3px #049DBF;
border-radius: 8px;
}
.questionBox .box-title {
position: absolute;
display: inline-block;
top: -13px;
left: 10px;
padding: 0 9px;
line-height: 1;
font-size: 20px;
background: #FFF;
color: #049DBF;
font-weight: bold;
}
.questionBox p {
margin: 0;
padding: 5px;
font-size: 18px;
}
つづいて、クイズの選択肢を表示する領域を作りましょう。
<!-- 選択肢を表示する領域 -->
<div class="col-md-8 offset-md-2 bg-light p-3">
<p class="font-bold">【次の3つから選ぼう】</p>
<ol>
<li class="my-3">選択肢A</li>
<li class="my-3">選択肢B</li>
<li class="my-3">選択肢C</li>
</ol>
</div>
<ol></ol>
タグで数字のリストを作っています。<li></li>
タグのclassについている my-3
は、上下に少しずつマージン(余白)を取るための Bootstrap 命令です。試しに my-3 をとってみるとすこし窮屈に見えるのがわかるかと思います。
今回つくるアプリは3択クイズなので、解凍ボタンを3つ作ります。次のコードを書き写してください。
<!-- 回答ボタンを表示する領域 -->
<div class="col-md-8 offset-md-2 text-center mt-5">
<div class="mb-4">
<a href="#" class="answer-btn answer-btn-bg1 mx-3">1</a>
<a href="#" class="answer-btn answer-btn-bg2 mx-3">2</a>
<a href="#" class="answer-btn answer-btn-bg3 mx-3">3</a>
</div>
<p class="text-center">正解だと思う番号のボタンをクリックしよう</p>
</div>
CSSクラスの解説をします。aタグを見てください。
<a href="#" class="answer-btn answer-btn-bg1 mx-3">1</a>
answer-btn
は main.css で定義されています。円と影を作っている CSS です。
answer-btn-bg-1
では、ボタンの色を設定しています。3つ同じにすると分かりづらいので、今回は bg-1 から bg-3 まで別々の色を指定しています。
/* ボタンの背景色 */
.answer-btn-bg1{
background: #f2902e;
}
.answer-btn-bg2{
background: #f2c12e;
}
.answer-btn-bg3{
background: #f2e22e;
}
#
から始まる6桁のアルファベットと数字の組み合わせは、カラーコードと呼ばれるもので、色を16進数で表したものになります。詳しく知りたい方はこちらのサイトを御覧ください。
さて、これでアプリの骨子は完成しました!ここまでお疲れさまでした。しかし、これだけではただ表示されているだけなので、ここから Vue.js を使って動きを作っていきます。g
app.html の下の方に <script></script>
タグに囲まれたプログラムがあります。この部分が JavaScript のプログラムです。事前にいくつか書いてありますが、一旦気にせず作っていきましょう。
Vue.js を理解するまえに、まずは JavaScript の基本的な構文を理解しましょう。以下では、ざっくりとした最低限度の解説しかしていません。さらに詳しく動画で知りたい方は、dotinstallで無料公開されているこちらの動画を見てください。
https://dotinstall.com/lessons/basic_javascript_v5
練習するためのファイルとして、javascript-practice.html
を用意しました。VSCodeでこのファイルを開き、<script></script>
の中にこれから下のプログラムを書き写していってください。
このあと出てくる console.log() は、ブラウザのコンソールに文字や数字などを出力するための命令です。Google Chrome で Windows の方は Ctrl+Shift+i を、macOSの方は Cmd+Option+i を押すと開発者ツールが開きます。さらに、下の画像で赤丸で囲まれている部分(Console)をクリックするとコンソールが開きます。
変数(へんすう)をとは文字通り「変わる数」のことです。ただし数と言っているものの、文字やオブジェクトなどいろいろなものを格納することができます。まずは、JavaScript で変数を定義する方法を紹介します。
//文字を入れる場合
var name = "Taro";
console.log(name); // -> "Taro"
//数字を入れる場合
var score = 10;
console.log(score); // -> 10
文字列には必ず "ダブルクォーテーション" をつけましょう。数字にはいりません。逆に "10" と表現すると、文字列として認識されます。
変数は変わる数なので、一度定義した変数の中身を変えることができます。次のコードを実行すると、次のような結果になります。
var score = 10;
score = 20;
console.log(score); // -> 20
例えば、クイズの正解数を保存する変数を考えてみましょう。始めは正解数は0なので、次のように定義できます。
var score = 0;
正解したときに1ずつ増やすためには次のように増やします。
//パターン1
score = score + 1;
// パターン2
score += 1;
変数には真偽値(true / false)を格納することもできます。真偽値は true または false のうちどちらかしかありません。例えば、質問に正解しているかどうかを判定するプログラムは次のように書きます。
var isCorrect = false;
プログラミングの世界で「○○かどうか」といった条件を保存する変数名は is を始めにつけることが多いです。
変数は変わる数でしたが、不必要なところで変えられてしまうと困るような値を保存することもあります。例えば、ユーザ名など一度定義したらそれ以上変えたくありません。そこで登場するのが定数です。 JavaScript では次のように定数を定義します。
const userName = "Taro Yamada";
変数と定数は必要なときに使い分けると良いでしょう。基本的に定義したあとから変えたいものは変数、一度定義したらそれ以上変えないもの(参照だけするもの)は定数として定義することが一般的です。
変数と定数はどちらも1つの値しか保存することができません。しかし、例えば学級の子どもたちの名前を保存したいときにはどうすればよいでしょう。
そこで登場するのが配列です。配列は、次のように定義します。
const classKids = ["Taro", "Yuto", "Mari", "Kaede"];
配列を使えばこのように複数の要素を1つの変数(または定数)に保存することができます。
配列に保存されたデータは次のように使います。
const classKids = ["Taro", "Yuto", "Mari", "Kaede"];
console.log(classKids[0]); // -> "Taro"
console.log(classKids[2]); // -> "Mari"
配列は追加された順に0始まりで数字(これを添字といいます)が付きます。1からではないことに注意してください。
たとえば、上の classKids 配列で Yuto さんのデータを取得したいときには次のように書きます。
const classKids = ["Taro", "Yuto", "Mari", "Kaede"];
console.log(classKids[1]); // -> Yuto
2.4.4で紹介した配列はただ名前を保存するだけの配列でした。しかし、これよりもっと複雑な情報(例えば、それぞれの出身地や性別、メールアドレスなど)を1つの配列で管理したい場合どうすればいいでしょう。
JavaScript ではオブジェクトと呼ばれる配列に似たものが用意されています。次のコードを見てください。
const taro = {
name: "Taro Yamada",
age: 12,
mail: "xxxxx@gmail.com",
address: "東京都港区xxxxx"
}
taroオブジェクトに保存されているデータを取得する場合には次のようなプログラムを書きます。
const taro = {
name: "Taro Yamada",
age: 12,
mail: "xxxxx@gmail.com",
address: "東京都港区xxxxx"
}
console.log(taro.name) // -> "Taro Yamada"
console.log(taro.mail) // -> "xxxxx@gmail.com"
さらに、オブジェクトを配列に格納することもできます。次のコードを見てください。
const classKids = [
0:{
name: "Taro Yamada",
age: 12,
mail: "xxxxx@gmail.com",
address: "東京都"
},
1:{
name: "Yuto Takada",
age: 12,
mail: "yyyyyy@gmail.com",
address: "千葉県"
},
2:{
name: "Mari Sakamoto",
age: 12,
mail: "aaaaaaaa@gmail.com",
address: "埼玉県"
},
3:{
name: "Kaede Akita",
age: 12,
mail: "oooooooo@gmail.com",
address: "神奈川県"
}
];
このオブジェクトのデータを参照するためには次のように書きます。
const classKids = [
0:{
name: "Taro Yamada",
age: 12,
mail: "xxxxx@gmail.com",
address: "東京都"
},
1:{
name: "Yuto Takada",
age: 10,
mail: "yyyyyy@gmail.com",
address: "千葉県"
},
2:{
name: "Mari Sakamoto",
age: 15,
mail: "aaaaaaaa@gmail.com",
address: "埼玉県"
},
3:{
name: "Kaede Akita",
age: 13,
mail: "oooooooo@gmail.com",
address: "神奈川県"
}
];
console.log(classKids[0].age); // -> 12
console.log(classKids[1].name); // -> "Yuto Takada"
console.log(classKids[2].address); // -> "埼玉県"
console.log(classKids[3].mail); // -> "oooooooo@gmail.com"
今回作るクイズアプリでは、問題文をこのように保存しています。具体的な使い方については後ほど確認しますので、ここではさらっと確認しておきましょう。
var questions = [
0:{
title: "神奈川県の県庁所在地はどこ?",
hint: "赤レンガ倉庫がある街!",
answers: [
"横浜市", "横須賀市", "町田市"
],
answerNum: 0,
description: ""
},
1:{
title: "千葉県の県庁所在地はどこ?",
hint: "政令指定都市だよ!",
answers: [
"柏市", "銚子市", "千葉市"
],
answerNum: 2,
description: ""
},
];
ここまではデータの保存について解説してきました。次に変数の使い方を紹介します。
例えば、得点に応じて表示する言葉を変えるプログラムは次のように書きます。
var score = 5;
if(score < 10){
console.log("もう少し頑張りましょう...");
}else{
console.log("すごい!");
}
if の中には条件式が入ります。条件式には次のようなものがあります。
score < 10 // 「変数 score が10より小さい」 が true
score <= 10 // 「変数 score が10以下」 が true
score > 10 // 「変数 score が10より大きい」 が true
score >= 10 // 「変数 score が10以上」 が true
score == 19 // 「変数 score が10と等しい」 が true
score !== 10 // 「変数 score が10と等しい」 が false
数学では「 = 」は左辺と右辺が等しいことを表していますが、プログラミングの世界では、変数の中に代入する記号として使われています。その代わり、「 == 」と書いて、等しいかどうかを判別しています。
if文を使えば、クイズの答えがあっているかどうかを判定することもできます。
var userAnswer = "新宿区";
if(userAnswer == "新宿区"){
console.log("正解!");
}else{
console.log("不正解...");
}
// -> "正解!"
ちなみに、細かい話ですが次のif文の結果は false になります。
if("新宿区" == "新宿 区"){
console.log(true);
}else{
console.log(false);
}
// -> false
これは、「新宿」と「区」の間にスペースが入っているかどうかが違うだけです。しかし、プログラミングではこの2つを別物と解釈します。実際にプログラムで使うときにはこのあたりに注意してください。
プログラミングでは「繰り返し」を使うことによって、同じような作業をかんたんにすることができます。例えば、次の上のプログラムと下のプログラムは同じ結果になります。
console.log(0);
console.log(1);
console.log(2);
console.log(3);
console.log(4);
//-> 0 1 2 3 4 5
for(var i=0; i<5; i++){
console.log(i);
}
//-> 0 1 2 3 4 5
変数 i は1つの繰り返し処理が終わると1つずつ増えていきます。iに0以外の数を設定すれば、その数からループが始まります。
for文は配列と組み合わせるととても便利に使うことができます。例えば次の prefectures 配列の中身を順番に表示するプログラムはこのように書くことができます。
const prefectures = ["東京", "千葉", "埼玉", "神奈川"];
for(var i=0; i<prefectures.length; i++){
console.log(prefectures[i]);
}
// -> "東京"
// -> "千葉"
// -> "埼玉"
// -> "神奈川"
今回つくっているアプリでも別の書き方(Vue.jsの書き方)ではありますが、この繰り返しの概念を使います。覚えておいてください。
関数とは、複数の処理をまとめて1つにしてくれるものです。一般的には次のように記述します。
//関数を定義する
function goodMorning(){
console.log("Good morning!");
console.log("おはようございます!");
}
//関数を実行する
goodMorning();
//-> "Good morning!"
//-> "おはようございます!"
これだけだとおもしろくありませんが、引数(ひきすう)という機能を使うとつぎのような処理をすることができます。
function sayHello(userName){
console.log("Hello, " + userName + "!!");
}
sayHello("Taro");
//-> "Hello, Taro!!"
引数は変数と似たようなもので、その関数の中だけで使えます。引数はカンマで区切ることで複数使うこともできますし、配列を入れることもできます。このあたりは使いながら慣れていきましょう。
ここまで JavaScript の書き方について紹介してきました。ざっと紹介したのでまだ理解できていないところもあるかもしれませんが、まずは簡単な理解で構いません。何度も繰り返してやることで自然と覚えていきます。
さて、ここまでやってようやく Vue.js にたどり着きました。Vue.js は何度も紹介している通り、JavaScript のフレームワークです。よって、生の JavaScript とは一部書き方が異なります。その違いだけ注意してください。
一般的な Vue.js の構成は次のようになっています。
var app = new Vue({
el: "#app", // Vue.jsを適用する範囲の設定
data:{ // アプリ全体で使う変数の定義
},
mounted(){ // ページが読み込まれたとき最初に一度だけ呼び出される関数
},
methods:{ // 関数を定義するところ
}
})
先に学んだ JavaScript の記法と少し異なる点がいくつかあります。まず、アプリ全体でつかう 変数は var
で宣言するのではなく、 data:{}
の中に変数名だけ定義して、=ではなく:を使います。Vue.js で変数を定義するには次のようにします。
var app = new Vue({
el: "#app",
data: {
userName: "Taro Yamada", //変数1
score: 0 //変数2
}
})
また、関数を定義するときは methods:{}
の中で function を使わずに次のように定義します。
var app = new Vue({
el: "#app",
data: {
userName: "Taro Yamada", //変数1
score: 0 //変数2
},
methods:{
sayHello(name){
console.log("Hello, " + name + "!");
}
}
})
app.html には予め Vue.js が次のようにかかれています。
var app = new Vue({
el: "#app", //Vueを適用する範囲を定義(今回は <div id="app"></div>の中に適用)
data: { //使いたい変数や配列などを定義する
questions:{ //問題文のデータ
0:{
title: "神奈川県の県庁所在地はどこ?", //クイズの問題文
hint: "赤レンガ倉庫がある街!", //ヒント
answers: [ //答えの選択肢
"横浜市", "横須賀市", "町田市"
],
answerNum: 0, //正解の問題番号(0から始まる)
description: "" //答えたあとに表示する説明など
},
},
//必要となる変数を定義する
currentQuestionNumber: 0, //現在の問題番号
currentQuestionData: [], //現在の問題データ
correctAnswerNum: 0, //正解した数
isCorrect: true, //正解しているかどうか
questionsLength: 0 //出題数を管理する変数
},
mounted() { //ページが読み込まれたとき最初に呼ばれる関数
//問題の長さを自動的に設定する
this.questionsLength = Object.keys(this.questions).length;
},
methods: { //関数を登録する場所
//現在の問題番号(1から始まる)を求める関数
getCurrentQuestionNumber(){
},
//答えをチェックする関数
answerCheck(userChoiceNumber){
},
//次の問題に行く処理(いじらない)
nextQuestion(){
if(this.currentQuestionNumber < Object.keys(this.questions).length - 1){
this.currentQuestionNumber += 1; //最後の問題ではないので問題番号を1ずつ増やす
$('#answerCheckModal').modal('hide'); //モーダルを隠す
}else{
//最後の問題なので問題番号は増やさない
$('#answerCheckModal').modal('hide'); //モーダルを隠す
//すべての問題が解き終わったので、最終結果モーダルを表示する
$('#answerEndedModal').modal({
keyboard: false,
backdrop: "static"
});
}
},
//画面を再読み込みする関数(いじらない)
reload(){
location.reload();
}
}
})
すでに書かれている部分については深く理解する必要はありません。まずはここにこう書くんだなくらいのことを理解してくれればOKです。
まずは、問題を管理するオブジェクトを作成しましょう。app.htmlにはすでに問題文の一部が定義されています。
questions:{ //問題文のデータ
0:{
title: "神奈川県の県庁所在地はどこ?", //クイズの問題文
hint: "赤レンガ倉庫がある街!", //ヒント
answers: [ //答えの選択肢
"横浜市", "横須賀市", "町田市"
],
answerNum: 0, //正解の問題番号(0から始まる)
description: "" //答えたあとに表示する説明など
},
},
1つ1つのオブジェクトに問題のタイトル,ヒント,答えの選択肢,正解の番号,説明を保存しています。まずは、0番目のオブジェクトをコピーして、0を1に変えてタイトルなどの情報も変更しましょう。
questions:{ //問題文のデータ
0:{
title: "神奈川県の県庁所在地はどこ?", //クイズの問題文
hint: "赤レンガ倉庫がある街!", //ヒント
answers: [ //答えの選択肢
"横浜市", "横須賀市", "町田市"
],
answerNum: 0, //正解の問題番号(0から始まる)
description: "" //答えたあとに表示する説明など
},
1:{ //ここの数字を1ずつ変えていく
title: "千葉県の県庁所在地はどこ?",
hint: "政令指定都市だよ!",
answers: [
"柏市", "銚子市", "千葉市"
],
answerNum: 2,
description: ""
},
},
最終的に問題を7個つくりました。ここまで作ってみてください。
//問題文のデータ
questions:{
0:{
title: "神奈川県の県庁所在地はどこ?",
hint: "赤レンガ倉庫がある街!",
answers: [
"横浜市", "横須賀市", "町田市"
],
answerNum: 0,
description: ""
},
1:{
title: "千葉県の県庁所在地はどこ?",
hint: "政令指定都市だよ!",
answers: [
"柏市", "銚子市", "千葉市"
],
answerNum: 2,
description: ""
},
2:{
title: "東京都の県庁所在地はどこ?",
hint: "世界で一番多くの人が乗り降りする駅があるよ!",
answers: [
"港区", "新宿区", "六本木"
],
answerNum: 1,
description: ""
},
3:{
title: "埼玉県の県庁所在地はどこ?",
hint: "大きなアリーナがある場所だね!",
answers: [
"所沢市", "浦和市", "さいたま市"
],
answerNum: 2,
description: ""
},
4:{
title: "群馬県の県庁所在地はどこ?",
hint: "豚肉の生産量がトップクラス!",
answers: [
"前橋市", "高崎市", "館林市"
],
answerNum: 0,
description: ""
},
5:{
title: "茨城県の県庁所在地はどこ?",
hint: "納豆が有名!",
answers: [
"水戸市", "つくば市", "大洗町"
],
answerNum: 0,
description: ""
},
6:{
title: "栃木県の県庁所在地はどこ?",
hint: "ぎょうざが美味しい!",
answers: [
"日光市", "宇都宮市", "那須塩原市"
],
answerNum: 1,
description: ""
}
},
これで問題文のデータを作ることができました。次は問題文を表示するプログラムを作ります。
問題文を表示するには、HTMLの部分に Vue.js を適用していきます。問題文を表示する領域にある <p></p>
の中身を次のように編集していください。
<!-- 問題文を表示する領域 -->
<div class="col-md-8 offset-md-2">
<div class="questionBox">
<span class="box-title">問題</span>
<p>{{questions[currentQuestionNumber].title}}</p> //ここを変える
</div>
</div>
Vue.js では、 {{ }}
のように中括弧を2つ並べたところに変数を入力すると、変数に保存されている中身を表示してくれます。このカッコのことをマスタッシュといいます。さて、ここではどのような言葉を表示しているのでしょうか。
まず、 questions
というのは先程作った問題文のオブジェクトが保存されている配列です。配列の要素を取り出すには 4.2.4 で学習したとおり、数字(添字)が必要となります。今回の場合、必要となる数字は現在の問題番号です。ここでは、 currentQuestionNumber
という変数で問題番号を管理しています。 questions[currentQuestionNumber]
とすることで、個々の問題オブジェクトにたどり着きました。その中の title
にアクセスしたいため、最終的には questions[currentQuestionNumber].title
となります。
ここまでできたら保存してブラウザを再読み込みしてみましょう。一問目の問題文が表示されているでしょうか。
次に、回答の選択肢を表示しましょう。「回答の選択肢を表示する」領域を次のように書き換えてください。
<div class="col-md-8 offset-md-2 bg-light p-3">
<p class="font-bold">【次の3つから選ぼう】</p>
<ol>
<li v-for="answer in questions[currentQuestionNumber].answers" class="my-3">
{{answer}}
</li>
</ol>
</div>
>li> </li>
タグに注目してください。ここに v-for
という命令が出てきています。Vue.js を使ってHTMLの中で for 文 を使うためにはこのように記述します。
この命令は他の言語で言う foreach 文に近い構造をしています。このfor文は questions 配列の中に保存されている問題文オブジェクトに含まれている answer 配列の個数だけ繰り返されます。そして、1回のループごとに answer という疑似変数の中に answers の要素が代入されています。上のプログラムの5行目で {{answer}}
と書いていますが、これは answers の中身の1つ1つだと思ってください。
この概念は少し難しいので、何度か繰り返しやってみるといいかと思います。
ここまでできたら保存し、ブラウザをリロードしてください。回答の選択肢が表示されているでしょうか。
問題文を表示する領域を次のように書き換えてください。
<!-- 問題文を表示する領域 -->
<div class="col-md-8 offset-md-2">
第{{getCurrentQuestionNumber()}}問 / 全{{questionsLength}}問
</div>
保存してブラウザを再読み込みしても、現在の問題番号が表示されていないと思います。現在の問題番号を表示するために使っているのは getCurrentQuestionNumber()
という関数です。この関数は定義はされているものの、中身がまだ書かれていません。 app.html の下の方にある Vue.js の methods の中に getCurrentQuestionNumber() 関数がありますので、次のように修正してください。
//現在の問題番号(1から始まる)を求める関数
getCurrentQuestionNumber(){
return this.currentQuestionNumber + 1;
},
currentQuestionNumber
で管理している問題番号は、配列で使う都合上0から始まっています。しかし、「第0問」とは言わないので1足した数を表示するようにしています。関数では return という機能を使って、関数の中で行われた計算結果を外に出す仕組みがあります。これについても、とりあえずこのようなものがあるということを知っておけば十分です。
現在の表示されているボタンはクリックされてもなにもおこりません。ボタンを動かすためには次のようにプログラムを改造します。
<!-- 回答ボタンを表示する領域 -->
<div class="col-md-8 offset-md-2 text-center mt-5">
<div class="mb-4">
<a href="#" class="answer-btn answer-btn-bg1 mx-3" @click="answerCheck(0)">1</a>
<a href="#" class="answer-btn answer-btn-bg2 mx-3" @click="answerCheck(1)">2</a>
<a href="#" class="answer-btn answer-btn-bg3 mx-3" @click="answerCheck(2)">3</a>
</div>
<p class="text-center">正解だと思う番号のボタンをクリックしよう</p>
</div>
それぞれのaタグの要素に @click="answerCheck(0)
を指定しました。@click=
はVue.js の書き方で、HTML要素から Vue.js の関数を呼び出すときに使われます。これは、このaタグをクリックしたときにanswerCheckという関数を実行してくださいという命令です。では、answerCheckという関数はどうなっているのでしょうか。現時点ではまだ定義されているだけで中身がないので、次のように書き加えてください。
//答えをチェックする処理
answerCheck(userChoiceNumber){
if(userChoiceNumber == this.questions[this.currentQuestionNumber].answerNum){
//正解!
this.isCorrect = true;
this.correctAnswerNum += 1;
}else{
this.isCorrect = false;
}
//結果を表示するモーダルを表示する
$('#answerCheckModal').modal({
keyboard: false,
backdrop: "static"
});
},
answerCheck関数はユーザが選択した問題番号が引数になっています。それが userChoiceNumber
です。この引数が問題を保存している questions 配列の問題番号番目で定義されている答え(answerNum)と同じかどうかを if文 でチェックしています。もし正しかったら、答えがあっているかを管理している変数 isCorrect
をtrueにして、正解数を管理している変数 correctAnswerNum
を1ずつ増やしています。
逆に不正解だった場合には、isCorrect
変数の値をfalseにしています。
先程書いたプログラムの最後に「結果を表示するモーダルを表示する」という部分がありました。モーダルとは、ポップアップされて出てくるウィンドウのことで、このアプリでは正解/不正解の表記に使っています。
正解したときと不正解のときによって表示される内容を変えたいので、app.html の「答えを表示するモーダル」の下にかかれているプログラムを次のように修正してください。ソースコードは長いですが、修正する箇所はそこまで多くありません。どこが変わっているかじっくり見比べてください。
<div class="modal" id="answerCheckModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalCenterTitle">問題{{getCurrentQuestionNumber()}}</h5>
</div>
<div class="modal-body">
<div class="row">
<div class="col-4">
<div class="text-center">
<img src="./images//correct.png" class="answer-img" alt="" v-if="isCorrect">
<img src="./images/miss.png" class="answer-img" alt="" v-if="!isCorrect">
</div>
</div>
<div class="col-8">
<div v-if="isCorrect">
<h3 class="answer-title">正解!</h3>
<p>正解は、{{questions[currentQuestionNumber].answers[questions[currentQuestionNumber].answerNum]}}ですね!</p>
<p></p>
</div>
<div v-if="!isCorrect">
<h3 class="answer-title">残念...!</h3>
<p>正解は、{{questions[currentQuestionNumber].answers[questions[currentQuestionNumber].answerNum]}}でした。</p>
<p></p>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" @click="nextQuestion()">次の問題へ</button>
</div>
</div>
</div>
</div>
まず、上のプログラム5行目にあるh5タグで、問題番号を表示するようにしています。
<h5 class="modal-title" id="exampleModalCenterTitle">問題{{getCurrentQuestionNumber()}}</h5>
次に、23行目の正解したときの説明に答えを表示しています。
<p>正解は、{{questions[currentQuestionNumber].answers[questions[currentQuestionNumber].answerNum]}}ですね!</p>
不正解だったときも同様です。29行目と23行目はほぼ同じですが文末表現を変えています。
<p>正解は、{{questions[currentQuestionNumber].answers[questions[currentQuestionNumber].answerNum]}}でした。</p>
最後に、次の問題にすすめるためのボタンに関数を適用させています。
<button type="button" class="btn btn-primary" @click="nextQuestion()">次の問題へ</button>
これで回答のチェックができるようになりました!完成まであと少しです!
すべてのクイズを解き終わったあとに表示するモーダルを作りましょう。
<!-- すべての問題を回答し終わったときに表示するモーダル -->
<div class="modal fade" id="answerEndedModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
おつかれさまでした!
</div>
<div class="modal-body">
<div class="row">
<div class="col-4">
<div class="text-center">
<img src="./images/trophy.png" class="answer-img">
</div>
</div>
<div class="col-8">
<p>{{questionsLength}} 問中 {{ correctAnswerNum }}問正解しました!</p>
</div>
</div>
</div>
<div class="modal-footer">
<a href="index.html" class="btn btn-success">クイズ一覧にもどる</a>
<a href="#" class="btn btn-primary" @click="reload()">最初から答える</a>
</div>
</div>
</div>
</div>
修正したのは次の点です。
<p>{{questionsLength}} 問中 {{ correctAnswerNum }}問正解しました!</p>
これでアプリ自体は完成です!お疲れさまでした。