Vue.jsが面白くなってきました。

過去にjQueryで実装したフォームを Vue.js で書き直しました。
jQuery 実装が Vue.js によってどう改善したか、比較いたします。

完成イメージ

フォーム実装イメージ
フォーム実装イメージ

ラジオボタンを操作すると、下の補助入力欄が切り替わるフォームです。

環境

  • PHP 7
  • jQuery 3系
  • Node.js 7系
  • Vue.js 2.2.x
  • Bootstrap

フォームは Bootstrap で実装しておりますため、次に掲載いたします HTML は長めなことをご容赦ください。

jQuery実装

 1<div class="form-group">
 2  <label class="col-lg-2 control-label">Input select</label>
 3  <div class="col-lg-4">
 4    <div class="radio">
 5      <label>
 6        <input type="radio" name="selector" value="1" checked="">
 7        Option one is this
 8      </label>
 9    </div>
10    <div class="radio">
11      <label>
12        <input type="radio" name="selector" value="2">
13        Option two can be something else
14      </label>
15    </div>
16  </div>
17</div>
18
19<div class="form-group" id="input1wrap">
20  <label for="input1" class="col-lg-2 control-label">Input 1</label>
21  <div class="col-lg-4">
22    <input type="text" class="form-control" id="input1" placeholder="foo bar">
23  </div>
24</div>
25
26<div class="form-group" id="input2wrap" style="display:none;">
27  <label for="input2" class="col-lg-2 control-label">Input 2</label>
28  <div class="col-lg-4">
29    <input type="text" class="form-control" id="input2" placeholder="bar buzz">
30  </div>
31</div>
 1(function($) {
 2  $('[name=selector]').change(function() {
 3    if ($('[name=selector]:checked').val() == 1) {
 4      $('#input1wrap').show();
 5      $('#input2wrap').hide();
 6    } else {
 7      $('#input1wrap').hide();
 8      $('#input2wrap').show();
 9    }
10  });
11})($);
12

解説

フォームを切り替えるための jQuery 実装です。

ラジオボタンのchangeをウォッチし、イベントが発生したら(ラジオボタンの操作が発生したら)、チェックされているボタンの値に応じてフォームの show、hide を切り替えています。

NGポイント

画面遷移時の状態

画面遷移時に 2つ目 のフォームが表示されないよう、[ style=“display:none” ]を埋め込んでいるのが「古い実装だなー」と感じます。

ラジオボタンの初期値が変化した場合に「どちらのフォームを表示/非表示としておくか」も考えなければなりません。

「フォームを動的に切り替える」という要件に対し、バックエンドも調整しなければならず、 フロントエンドとバックエンドで実装が分離 してしまうというバットケースです。

選択肢が増えた場合

ラジオボタンの選択肢が 2つ ならまだ見通しは良いですが、選択肢が 3つ、4つと増えた場合、こんな実装になってしまうことがあります。

 1$('[name=selector]').change(function() {
 2  var checked = $('[name=selector]:checked').val();
 3  if (checked == 1) {
 4    $('#input1wrap').show();
 5    $('#input2wrap').hide();
 6    $('#input3wrap').hide();
 7    $('#input4wrap').hide();
 8  } else if (checked == 2) {
 9    $('#input1wrap').hide();
10    $('#input2wrap').show();
11    $('#input3wrap').hide();
12    $('#input4wrap').hide();
13  } else if ($checked == 3) {
14    $('#input1wrap').hide();
15    $('#input2wrap').hide();
16    $('#input3wrap').show();
17    $('#input4wrap').hide();
18  } else {
19    $('#input1wrap').hide();
20    $('#input2wrap').hide();
21    $('#input3wrap').hide();
22    $('#input4wrap').show();
23  }
24});
25

選択肢の増加によってフォーム切り替えのための if 分岐が増えてしまいました。 イケていないコードの見本ですね。

上の「画面遷移時の初期状態」の実装にも影響しますから、選択肢が増えることによる複雑度の増加は掛け算です。

このように jQuery 実装は全く変更に強くありません

Vue.js実装

 1<template>
 2  <form class="form-horizontal">
 3    <fieldset>
 4      <div class="form-group">
 5        <label class="col-lg-2 control-label">Input select</label>
 6        <div class="col-lg-4">
 7          <div class="radio">
 8            <label>
 9              <input v-model="selector" type="radio" value="1">
10              Option one is this
11            </label>
12          </div>
13          <div class="radio">
14            <label>
15              <input v-model="selector" type="radio" value="2">
16              Option two can be something else
17            </label>
18          </div>
19        </div>
20      </div>
21      <div v-if="selector == 1" class="form-group">
22        <label for="input1" class="col-lg-2 control-label">Input 1</label>
23        <div class="col-lg-4">
24          <input v-model="input1" type="text" class="form-control" id="input1" placeholder="foo bar">
25        </div>
26      </div>
27      <div v-if="selector == 2" class="form-group">
28        <label for="input2" class="col-lg-2 control-label">Input 2</label>
29        <div class="col-lg-4">
30          <input v-model="input2" type="text" class="form-control" id="input2" placeholder="bar buzz">
31        </div>
32      </div>
33    </fieldset>
34  </form>
35</template>
 1<script>
 2export default {
 3  data() {
 4    return {
 5      selector: 1
 6    }
 7  }
 8}
 9</script>
10

※ 1 コンポーネントの template, script を分けて書いています。

解説

なんということでしょう。
JavaScript 側ソースが恐ろしくシンプルになりましたね。

ラジオボタンの選択値

ラジオボタンに[ v-model=“selector” ]を埋め込みました。
ラジオボタンの操作結果が Vue.js 側のthis.selectorに即反映されます。

フォームの切り替え

input タグをラップする Divタグに に v-if を指定しました。
これだけで「式( v-if =” *** “)が真であれば自身を表示する」という実装が実現できます。

画面遷移時の状態

お任せで OK です。
HTML 側に初期状態を設定したとしても、Vue.js 側が適切に処理してくれます。

選択肢が増えた場合

jQuery と違って HTML に要素を足すだけです。
Script 側のコードに手を加えることはありません。

 1<form class="form-horizontal">
 2  <fieldset>
 3    <div class="form-group">
 4      <label class="col-lg-2 control-label">Input select</label>
 5      <div class="col-lg-4">
 6        <div class="radio">
 7          <label>
 8            <input v-model="selector" type="radio" value="1">
 9            Option one is this
10          </label>
11        </div>
12        <div class="radio">
13          <label>
14            <input v-model="selector" type="radio" value="2">
15            Option two can be something else
16          </label>
17        </div>
18        <div class="radio">
19          <label>
20            <input v-model="selector" type="radio" value="3">
21            Option tree can be something else
22          </label>
23        </div>
24      </div>
25    </div>
26    <div v-if="selector == 1" class="form-group">
27      <label for="input1" class="col-lg-2 control-label">Input 1</label>
28      <div class="col-lg-4">
29        <input v-model="input1" type="text" class="form-control" id="input1" placeholder="foo bar">
30      </div>
31    </div>
32    <div v-if="selector == 2" class="form-group">
33      <label for="input2" class="col-lg-2 control-label">Input 2</label>
34      <div class="col-lg-4">
35        <input v-model="input2" type="text" class="form-control" id="input2" placeholder="bar buzz">
36      </div>
37    </div>
38    <div v-if="selector == 3" class="form-group">
39      <label for="input3" class="col-lg-2 control-label">Input 3</label>
40      <div class="col-lg-4">
41        <input v-model="input3" type="text" class="form-control" id="input3" placeholder="hoge">
42      </div>
43    </div>
44  </fieldset>
45</form>

入力欄の値を取る

各 Input タグにv-modelを埋め込み済みで、これだけで双方向バインディングされます。

 1<script>
 2new Vue({
 3  data() {
 4    return {
 5      selector: 1,
 6      input1: "",
 7      input2: "",
 8    }
 9  },
10  methods: {
11    hoge() {
12        this.input1
13        this.input2
14    }
15  }
16});
17</script>
18

jQuery では都度 DOM アクセスする必要があります。

1$('#input1').val();
2$('#input2').val();
3

この辺りも Vue.js の便利さが際立ちますね。

おわりに

過去の経験からフロントエンドは食わずな状態が嫌いが続いていましたが、Vue.js なら楽勝です。
こんなに便利なら楽しく実装できますね。

「過去の経験」についてはこちらをご覧ください。

jQuery で頑張ってしまったことを猛省して Laravel Mix で Vue.js を勉強した
jQueryは優れたライブラリですが、改修に弱く大規模な開発には不向きでです。LaravelにバンドルされているJavaScriptライブラリVue.jsを勉強し、モダンな開発に挑戦しました。
atuweb 開発ブログ

Vue.js が苦手なこともちょっとわかってきましたため、そういった記事も書ければと思います。

WEB+DB PRESS Vol.97

外村 和仁,小林 徹,古川 陽介,佐藤 歩,yoku0825,是澤 太志,一野瀬 翔吾,加藤 颯史,のざき ひろふみ,うらがみ,水嶋 淳貴,久田 真寛,久保 達彦,伊藤 直也,遠藤 雅伸,ひげぽん,海野 弘成,はまちや2,竹原,倉岡 洋義
出版社:技術評論社  発売日:2017-02-24

Amazonで詳細を見る

Vue.js入門 基礎から実践アプリケーション開発まで

川口 和也,喜多 啓介,野田 陽平,手島 拓也,片山 真也
出版社:技術評論社  発売日:2018-09-22

Amazonで詳細を見る

基礎から学ぶ Vue.js

mio
出版社:シーアンドアール研究所  発売日:2018-05-29

Amazonで詳細を見る