Vue.jsが面白くなってきた tomita@atuweb です。

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

完成イメージ

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

環境

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

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

jQuery実装

<div class="form-group">
  <label class="col-lg-2 control-label">Input select</label>
  <div class="col-lg-4">
    <div class="radio">
      <label>
        <input type="radio" name="selector" value="1" checked="">
        Option one is this
      </label>
    </div>
    <div class="radio">
      <label>
        <input type="radio" name="selector" value="2">
        Option two can be something else
      </label>
    </div>
  </div>
</div>

<div class="form-group" id="input1wrap">
  <label for="input1" class="col-lg-2 control-label">Input 1</label>
  <div class="col-lg-4">
    <input type="text" class="form-control" id="input1" placeholder="foo bar">
  </div>
</div>

<div class="form-group" id="input2wrap" style="display:none;">
  <label for="input2" class="col-lg-2 control-label">Input 2</label>
  <div class="col-lg-4">
    <input type="text" class="form-control" id="input2" placeholder="bar buzz">
  </div>
</div>
(function($) {
  $('[name=selector]').change(function() {
    if ($('[name=selector]:checked').val() == 1) {
      $('#input1wrap').show();
      $('#input2wrap').hide();
    } else {
      $('#input1wrap').hide();
      $('#input2wrap').show();
    }
  });
})($);

解説

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

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

NGポイント

画面遷移時の状態

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

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

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

選択肢が増えた場合

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

$('[name=selector]').change(function() {
  var checked = $('[name=selector]:checked').val();
  if (checked == 1) {
    $('#input1wrap').show();
    $('#input2wrap').hide();
    $('#input3wrap').hide();
    $('#input4wrap').hide();
  } else if (checked == 2) {
    $('#input1wrap').hide();
    $('#input2wrap').show();
    $('#input3wrap').hide();
    $('#input4wrap').hide();
  } else if ($checked == 3) {
    $('#input1wrap').hide();
    $('#input2wrap').hide();
    $('#input3wrap').show();
    $('#input4wrap').hide();
  } else {
    $('#input1wrap').hide();
    $('#input2wrap').hide();
    $('#input3wrap').hide();
    $('#input4wrap').show();
  }
});

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

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

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

Vue.js実装

<template>
  <form class="form-horizontal">
    <fieldset>
      <div class="form-group">
        <label class="col-lg-2 control-label">Input select</label>
        <div class="col-lg-4">
          <div class="radio">
            <label>
              <input v-model="selector" type="radio" value="1">
              Option one is this
            </label>
          </div>
          <div class="radio">
            <label>
              <input v-model="selector" type="radio" value="2">
              Option two can be something else
            </label>
          </div>
        </div>
      </div>
      <div v-if="selector == 1" class="form-group">
        <label for="input1" class="col-lg-2 control-label">Input 1</label>
        <div class="col-lg-4">
          <input v-model="input1" type="text" class="form-control" id="input1" placeholder="foo bar">
        </div>
      </div>
      <div v-if="selector == 2" class="form-group">
        <label for="input2" class="col-lg-2 control-label">Input 2</label>
        <div class="col-lg-4">
          <input v-model="input2" type="text" class="form-control" id="input2" placeholder="bar buzz">
        </div>
      </div>
    </fieldset>
  </form>
</template>
<script>
export default {
  data() {
    return {
      selector: 1
    }
  }
}
</script>

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

解説

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

ラジオボタンの選択値

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

フォームの切り替え

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

画面遷移時の状態

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

選択肢が増えた場合

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

<form class="form-horizontal">
  <fieldset>
    <div class="form-group">
      <label class="col-lg-2 control-label">Input select</label>
      <div class="col-lg-4">
        <div class="radio">
          <label>
            <input v-model="selector" type="radio" value="1">
            Option one is this
          </label>
        </div>
        <div class="radio">
          <label>
            <input v-model="selector" type="radio" value="2">
            Option two can be something else
          </label>
        </div>
        <div class="radio">
          <label>
            <input v-model="selector" type="radio" value="3">
            Option tree can be something else
          </label>
        </div>
      </div>
    </div>
    <div v-if="selector == 1" class="form-group">
      <label for="input1" class="col-lg-2 control-label">Input 1</label>
      <div class="col-lg-4">
        <input v-model="input1" type="text" class="form-control" id="input1" placeholder="foo bar">
      </div>
    </div>
    <div v-if="selector == 2" class="form-group">
      <label for="input2" class="col-lg-2 control-label">Input 2</label>
      <div class="col-lg-4">
        <input v-model="input2" type="text" class="form-control" id="input2" placeholder="bar buzz">
      </div>
    </div>
    <div v-if="selector == 3" class="form-group">
      <label for="input3" class="col-lg-2 control-label">Input 3</label>
      <div class="col-lg-4">
        <input v-model="input3" type="text" class="form-control" id="input3" placeholder="hoge">
      </div>
    </div>
  </fieldset>
</form>

入力欄の値を取る

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

<script>
new Vue({
  data() {
    return {
      selector: 1,
      input1: "",
      input2: "",
    }
  },
  methods: {
    hoge() {
        this.input1
        this.input2
    }
  }
});
</script>

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

$('#input1').val();
$('#input2').val();

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

おわりに

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

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

jQueryで頑張ってしまったことを猛省してLaravel MixでVue.jsを勉強した
atuweb.net

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

WEB+DB PRESS Vol.97

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

Amazonで詳細を見る

JavaScript フレームワーク入門

掌田津耶乃
出版社:秀和システム  発売日:2016-09-16

Amazonで詳細を見る

2017年10月07日:タイトルを『[Vue.js]ラジオボタンと連動するフォームの実装比較』より変更、文章を整形