クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【Vue.js】イベント(v-on)、分岐(v-show、v-if)、ループ(v-for)をやってみた!

Hellow Vue.js」の続きです。前回はVue.jsを読み込んで、「こんにちは びゅう!」という文字を表示しました。しかしながら、これだけならjQueryや生JSでもできるわけなので、もう少し進めてみる。
基本編として、チュートリアルでまず目についた「イベント」「分岐」「ループ」をやってみた。それでは、どうぞ!

※目次:

※参考:前回の記事
三大フレームワークとかSPAとか仮想DOMとかわかりにくかったのでHellow Vue.jsしてみた! - クモのようにコツコツと

Vue.jsのディレクティブ

Vue.jsには「ディレクティブ」という独自の属性があります。頭にv-が付くのが特徴です。

<!--書式-->
<div v-hoge="属性値">コンテンツ<div>

<!--例-->
<div v-show="age >= 20">大人ですね</div>

チュートリアルによると…

ディレクティブの仕事は、属性値の式が変化したときに、リアクティブに副作用を DOM に適用することです。

※参考:テンプレート構文 — Vue.js

前回の記事で触れたように「リアクト」は反応する、という意味。属性値の式の結果に反応してDOMの内容を変化させます。
代表的な例としてはイベント、分岐、ループがあります。「JSの基本-後編」で触れたイベントと分岐、ループと似たディレクティブ名が付いているのでイメージしやすいと思います。

※参考:JSのイベント、分岐、ループ
【JSの基本-後編】書ける前に読む!HTML、CSS、JSの書式-5 - クモのようにコツコツと

イベント(v-on)

Vue.jsのイベントのディレクティブはv-onです。 チュートリアルを参考にいくつか作ってみました。

※参考:イベントハンドリング — Vue.js

こちらはチュートリアルの一番最初にあるカウントアップの事例を改造しています。

属性値が式

「3 年目の浮気ぐらい大目に見てよ」というテキストの下に「馬鹿いってんじゃないわ」というボタンがありますね。これをクリックすると「3 年目」の数字がカウントアップしていきます。ほんと、馬鹿いってんじゃないですよね。

※参考:ちなみに元ネタはコチラ…
https://youtu.be/-TuFqVLDxZU

さて、このカウントアップはどのような仕組みになっているのか見ていきましょう。まずはHTML

<div id="payappa">
  <p> {{ counter }} 年目の浮気ぐらい大目に見てよ</p>
  <button v-on:click="counter += 1">馬鹿いってんじゃないわ</button>
</div>
  • 一番外側がid#payappaのdiv要素。
  • 中にp要素があり、「年目」の前には前回のHellow Vueと同じく波括弧2つ{{ }}で囲ったcounterがある。
  • その下のbutton要素の中にはv-on:click="counter += 1"がある。

v-onは「イベント」のディレクティブです。続く:hogeは「キー修飾子」といい:clickはクリックイベントです。属性値はcounterに1加算するという式が書いてあります。
次はJSを見ていきましょう。

var payappa = new Vue({
  el: '#payappa',
  data: {
    counter: 3
  }
})
  • 変数payappaVueインスタンスを作成。引数のに連想配列が入っている。(波括弧{ }ブロック)
  • elキーの値でid#payappaの要素と紐づいている。
  • dataキーの値が入れ子の連想配列になっている
  • 入れ子の中のcounterキーの値に3が入る。

counter3が入ることで「3年目」になり、v-on:clickのbutton要素をクリックするたびにcounterの数値が1ずつカウントアップするわけです。

メソッドイベントハンドラ

v-on:clickの属性値の中に毎回式を書くのは再利用性が低く、複雑なコンテンツを作ることができません。
そこで、属性値にはイベントハンドラのメソッド名のみを書くほうが多くなってきそうです。こんな感じです。

「押してちょ」というボタンを押すと「こんにちは びゅう!」というアラートが出ました。

コードを見ていきましょう。HTMLはこちら

<div id="btn">
  <button v-on:click="aisatsu">押してちょ</button>
</div>
  • 外側はid名#btnのdiv要素
  • button要素の属性はv-on:clickで属性値はaisatsu

HTMLはシンプルですね!属性値のaisatsuがイベントハンドラのメソッド名です。

では、JSを見ていきましょう。

var btn = new Vue({
  el: '#btn',
  data: {
    name: 'びゅう'
  },
  methods: {
    aisatsu: function () {
      alert("こんにちは " + this.name + '!')
    }
  }
  • 変数btnVueインスタンスを作成。中は連想配列{ }
  • elキーで#btnの要素と紐づく
  • dataキーの値は入れ子の連想配列
  • 入れ子のnameキーの値はびゅう
  • methodsキーの値は連想配列
  • 入れ子のaisatsuキーの中に無名関数
  • 無名関数の中にはアラートで「こんにちは」とnameの値と「!」を合わせる

dataキーまでは最初の事例と同じ構成ですね。次のmethodsキーでメソッドを作ります。aisatuがメソッド名です。アラートの引数の中でnameキーの文字列を呼び出しています。

インラインメソッドハンドラ

次は「インラインメソッドハンドラ」という事例です。

「いま何時(なんどき)でっか?」と質問していますので、今の時間帯に該当する「朝」「昼」「夜」のボタンを押してください。朝は「おはよう」、昼は「こんにちは」、夜は「こんばんは」という挨拶が頭につきます。

コードを見ていきます。まずHTML

<p>今何時(なんどき)でっか?</p>
<div id="btn">
  <button v-on:click="say('おはようございます')">朝だ</button>
  <button v-on:click="say('こんにちは')">昼だ</button>
  <button v-on:click="say('こんばんは')">夜だ</button>
</div>
  • ボタンが3つに増えました。v-on:clickの属性値はsay()メソッド。
  • 「朝」ボタンのsay()メソッドの引数は「おはようございます」。
  • 「昼」ボタンのsay()メソッドの引数は「こんにちは」。
  • 「夜」ボタンのsay()メソッドの引数は「こんばんは」。

アラートの頭の挨拶はきっとこの引数を読み込んでいるに違いない、と想像できますね。

ではJSを見てみましょう。

var btn = new Vue({
  el: '#btn',
  data: {
    name: ' びゅう'
  },
  methods: {
    say: function (message) {
      alert(message + this.name + '!')
    }
  }
})

先ほどのメソッドイベントハンドラととてもよく似ていますね。違うのはmethodsキーの中のsayキーの部分です。

  • methodsキーの値が連想配列でメソッドを設定する。
  • sayキーの値が無名関数で引数はmessege
  • アラートの中でmessegeと上のdataキーのnameを呼び出して「!」と合わせている。

これでHTMLのsay()メソッドの引数の「おはようございます」などの文字列を読み込んでアラート表示しているわけです。

分岐(v-show、v-if)

次は「分岐」です。Vue.jsの分岐のディレクティブはv-showv-ifがあります。 こちらもチュートリアルを参考にいくつか作ってみました。

※参考:条件付きレンダリング — Vue.js

v-show

v-show ディレクティブの事例です。v-showは条件によって表示する、しないを判定します。

一見ただのテキストが表示されているだけですね。なんて書いてありますか?実はこのテキスト、見る時間帯によって内容が変わります。 日中は「こんにちは。びゅう!」、18時より遅い時間に見ると「こんにちは。いや、こんばんは。びゅう!」となります。(0時過ぎると「こんにちは」に戻るんですけどね。。) どんな仕掛けになっているのでしょう。まずはHTML

<p id="app">
  こんにちは。
  <span v-show="Hours >= 18">いや、こんばんわ。</span>
  {{ message }}
</p>
  • id#appのdiv要素の中にspan要素がある。
  • span要素にはディレクティブv-show属性があり、属性値はHours >= 18という条件式。 *二重波括弧{{ }}massageを読み込む。

JSを見ていきましょう。

//日時取得
var Date = new Date();
var Hours = Date.getHours();
  • 変数DateDate()インスタンスを生成
  • 変数HoursgetHours()メソッドによってDateの時間を取得

この部分はJSにあらかじめ用意されている組み込み関数なのでVue.jsではありません。
先ほどのHTMLのv-show属性の条件で時間が読み込まれていますね。「時間が18以上であれば表示する」という意味になります。

//本文
var app = new Vue({
  el: '#app',
  data: {
    message: ' びゅう!'
  }
})

続く変数appVueインスタンスはこれまでと同じ構成ですね。 HTMLの{{massage}}に文字列が表示されます。

v-showの表示はCSSのdisplayプロパティで切り替えられます。デベロッパーツールなどで見ていただくとspan要素は0〜17時台まではdisplay: none;になっていることがわかります。

v-if

次はv-ifディレクティブの事例です。v-ifは条件と合わない場合はdisplay: none;の表示切り替えではなくオブジェクト自体が生成されません。また、v-showよりもelseなどの複雑な分岐を設定できます。

一見単なるテキストが一行あるように見えます。しかしこのテキストの頭の挨拶部分は見る時間帯によって「おはようございます」「こんにちは」など表示が変わります。

<p id="app">
  <span v-if="Hours <= 11">おはようございます</span>
  <span v-else-if="Hours <= 17">こんにちは</span>
  <span v-else-if="Hours <= 23">こんばんは</span>
  <span v-else-if="Hours <= 5">おやすみなさい</span>
  {{ message }}
</p>
  • id名appのp要素の中にspan要素が4つ入っている。
  • 1つ目のspan要素はv-ifディレクティブで条件はHours <= 11
  • 2つ目のspan要素はv-else-ifディレクティブで条件はHours <= 17
  • 3つ目のspan要素はv-else-ifディレクティブで条件はHours <= 23
  • 4つ目のspan要素はv-else-ifディレクティブで条件はHours <= 5
  • 二重波括弧{{ }}messageを読み込む

JSのif文と似ているので想像がつくと思いますがv-else-ifはif文のelse ifと同様「さもなくは、もし」という意味。これ以外に「さもなくば」のelseと同じv-elseディレクティブもあります。
条件は上からHours(時間)が11時台以下(<=)なら、17時台以下なら、23時台以下なら、5時台以下なら、という意味。

それではJSを見ていきましょう。

//日時取得
var Date = new Date();
var Hours = Date.getHours();

//本文
var app = new Vue({
  el: '#app',
  data: {
    message: ' びゅう!'
  }
})

先ほどと全く同じですね。上で変数Hoursに時間を取得し、下の変数appmessageに文字列「びゅう!」を代入。

HTMLの書き方を変えるだけで同じJSでも挙動が変わりました。時間によって挨拶がかわるわけです。
デベロッパーツール で見ると、元のHTMLでは4つあったspan要素が時間帯に該当する一つしか描画されていないことがわかります。これがv-showとの違いです。

ループ(v-for)

最後にVue.jsの「ループ」のディレクティブ、v-forです。 チュートリアルはこちら。

※参考:リストレンダリング — Vue.js

配列数分ループ

なんだか一年中理由をつけて酒を飲んでいるようですね。「日本全国酒飲み音頭」の歌詞です。

※参考:元ネタはこちら
https://youtu.be/I9kBzcfnsyM

コードを見ていきましょう。HTMLはこちら

<h1 id="title">{{ title }}</h1>
<ul id="sake">
    <li v-for="value in sake">
    {{ value }}
  </li>
</ul>
  • id名#titleのh1要素、二重波括弧{{ }}titleを読み込む。
  • id名#sakeのul要素の中にはli要素が一つだけある。
  • li要素にはv-forディレクティブがあり、属性値はvalue in sake
  • 二重波括弧{{ }}valueを読み込む。

具体的な文字列が全くなく、li要素も一つだけでかなりシンプルですね。JSはどうなっているんでせう?

var title = new Vue({
  el: '#title',
  data: {
    title: '日本全国酒飲み音頭'
  }
})
  • 変数titelでVueインスタンスを生成し
  • elキーで#titleと紐付け
  • dataキーに入れ子のtitleキーで文字列「日本全国酒飲み音頭」読み込み。

前半は見慣れた構成です。#titleのh1要素に曲のタイトルを読み込ませます。後半を見てみましょう。

var sake = new Vue({
  el: '#sake',
  data: {
    sake: [
      "1月は正月で酒が飲めぞ",
      "2月は豆まきで酒が飲めるぞ",
      "3月はひな祭りで酒が飲めるぞ",
      "4月は花見で酒が飲めるぞ",
      "5月は子供の日で酒が飲めるぞ",
      "6月は田植えで酒が飲めるぞ",
      "7月は七夕で酒が飲めるぞ",
      "8月は暑いから酒が飲めるぞ",
      "9月は台風で酒が飲めるぞ",
      "10月は運動会で酒がめるぞ",
      "11月は何でもないけど酒が飲めるぞ",
      "12月はドサクサで酒が飲めるぞ",
    ]
  }
})
  • 変数sakeVueインスタンス生成
  • elキーで#sakeと紐付け
  • dataキーに入れ子のsakeキーの値は配列で曲の歌詞が12個入っている。

これで#sakeのul要素内のli要素がv-forディレクティブの条件sakeキーの値の数量分ループするわけです。実行されるのはvalueキーへのsakeキーの値の読み込みです。

ループ回数を読み込む

さて、sakeキーの値を見て見ると酒が飲めるぞの部分が重複していて冗長に感じますね。また、「●月は」の部分の月の数値もループの回数を入れられると一致しそうです。こんな感じです。

見た目のアウトプットは全く同じなんですけどね。コードはどのように変わったのでしょうか。

<h1 id="title">{{ title }}</h1>
<ul id="sake">
    <li v-for="(value, index) in sake">
    {{ index +1 }}月は{{ value }}酒が飲めるぞ
  </li>
</ul>

li要素の部分が変わりました。

  • li要素のv-forディレクトレィブの属性値が(value, index) in sake"に。
  • 二重波括弧{{ }}indexを読み込み1を加算。同じく二重波括弧{{ }}valueも読み込む。その他の共通部分はそのまま書く。

JSを見てみましょう。

var title = new Vue({
  el: '#title',
  data: {
    title: '日本全国酒飲み音頭'
  }
})

前半は先ほどと同じですね。曲のタイトル読み込み。後半はこちら。

var sake = new Vue({
  el: '#sake',
  data: {
    sake: [
      "正月で",
      "豆まきで",
      "ひな祭りで",
      "花見で",
      "子供の日で",
      "田植えで",
      "七夕で",
      "暑いから",
      "台風で",
      "運動会で",
      "何でもないけど",
      "ドサクサで",
    ]
  }
})

全体の構造は先ほどと同じなのですがsakeキーの値から重複する文字列が消えてユニークな部分のみになりました。スッキリ!

HTMLのv-forディレクティブの第二引数にindexを加えると、配列のループ回数を読み込むことができます。0からカウントするので1加算します。
これで「1月は」「2月は」とループ回数をカウントアップして読み込むことができるようになりました。

最後に

まずはシンプルなことしかやっていないので、まだ生JSやjQueryでも実現できる範囲です。基本的にはセレクタ名と紐付けるので、DOMの親子関係はそんなに意識しないで済みそうです。書式も配列っぽくてシンプル。

また、「ループ」でやったことが、なんとなく前回の「JSONファイル読み込み」に通じるそうに思えたので、次回ははっぴいえんどのアルバム情報のJSONファイルをVue.jsで読み込めないかトライしたく思います!


※参考:Vue.jsの習得のためにやったことまとめ
qiita.com