Vue.jsを使ってすべてを選択するチェックボックスを作ってみる

2018.11.03 Vue.jsjavascript

こんにちは。るかです。

せっかくブログ作ったのでなにか書かないともったいないので早速Vue.jsネタを書いておきますよ。 最近あんまり見ない気もしますがたまーにフォームとかで見る、チェックすると配下のチェックボックス全部にチェックが入るやーつをVue.jsでどう作るのかメモ!

早速基本のコードから

メソッドで実装する場合

<template>
  <div>
    <!-- すべて選択 -->
    <input type="checkbox" @click="toggleCheckAll" v-model="checkAll"> すべて選択
    <hr>
    <!-- チェックボックスたち -->
    <div v-for="item in items">
      <input type="checkbox" v-bind:value="item.value" v-model="checkedItems" @change="refreshCheckAll">{{ item.label }}
    </div>
  </div>
</template>

<script>
export default {
  data:function () {
    return {
      checkAll: false,
      items: [
        {label:'チェックボックス1', value:'1'},
        {label:'チェックボックス2', value:'2'},
        {label:'チェックボックス3', value:'3'},
        {label:'チェックボックス4', value:'4'},
      ],
      checkedItems:[]
    }
  },
  methods: {
    toggleCheckAll: function(){
      this.checkAll = !this.checkAll;
      this.checkedItems = [];
      var _self = this;
      if(this.checkAll){
        this.items.forEach(function(item){
          _self.checkedItems.push(item.value);
        });
      }
    },
    refreshCheckAll: function(){
      if(this.checkedItems.length == this.items.length){
         this.checkAll = true;
      }else{
         this.checkAll = false;
      }
    }
  }
};
</script>

”すべて選択”チェックボックスにチェックが入ったらitemsの中のvalueをcheckedItemsにpushします。 そうするとチェックボックス1~5はcheckedItemsとバインドされているので全てにチェックが入ります。

チェックボックス1~5ではchangeイベントが発火するたびにrefreshCheckAllメソッドが実行されるようになっていてitemsの要素数とcheckedItemsに入っている要素数をが同じなら”すべて選択”にチェックを入れ、そうでない場合は外すような感じになっています。

computedを使う場合

<template>
  <div>
    <!-- すべて選択 -->
    <input type="checkbox" v-model="checkAll"> すべて選択
    <hr>
    <!-- チェックボックスたち -->
    <div v-for="item in items">
      <input type="checkbox" v-bind:value="item.value" v-model="checkedItems">{{ item.label }}
    </div>
  </div>
</template>

<script>
export default {
  data:function () {
    return {
      items: [
        {label:'チェックボックス1', value:'1'},
        {label:'チェックボックス2', value:'2'},
        {label:'チェックボックス3', value:'3'},
        {label:'チェックボックス4', value:'4'},
      ],
      checkedItems:[]
    }
  },
  computed: {
    checkAll: {
      get: function () {
        return this.items ? this.checkedItems.length == this.items.length : false;
      },
      set: function (value) {
        var selected = [];
        if (value) {
          this.items.forEach(function (item) {
            selected.push(item.value);
           });
        }
        this.checkedItems = selected;
      }
    }
  },
};
</script>

やってることはほとんど同じですが、computed使ったほうがシンプルですね!

複数の場合

1つだけじゃなくて複数つくる場合もあると思います。 もっといい方法があるかもしれませんが、以下のように作ってみました。

<template>
  <div>
    <div v-for="(checkbox, index) in checkboxs" class="boxOuter">
      <!-- すべて選択 -->
      <input type="checkbox" @click="toggleCheckAll(index)" v-model="checkboxs[index].checkAll"> すべて選択
      <hr>
      <!-- チェックボックスたち -->
      <div v-for="item in checkboxs[index].items">
        <input type="checkbox" v-bind:value="item.value" v-model="checkboxs[index].checkedItems" @change="refreshCheckAll(index)">{{ item.label }}
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data:function () {
    return {
      checkboxs: [
        {
          checkAll: false,
          items: [
            {label:'チェックボックス1', value:'1'},
            {label:'チェックボックス2', value:'2'},
            {label:'チェックボックス3', value:'3'},
            {label:'チェックボックス4', value:'4'},
          ],
          checkedItems:[]
        },
        {
          checkAll: false,
          items: [
            {label:'チェックボックス1-2', value:'1'},
            {label:'チェックボックス2-2', value:'2'},
            {label:'チェックボックス3-2', value:'3'},
            {label:'チェックボックス4-2', value:'4'},
          ],
          checkedItems:[]
        }
      ]
    }
  },
  methods: {
    toggleCheckAll: function(index){
      var target = this.checkboxs[index];
      target.checkAll = !target.checkAll;
      target.checkedItems = [];
      if(target.checkAll){
        target.items.forEach(function(item){
          target.checkedItems.push(item.value);
        });
      }
    },

    refreshCheckAll: function(index){
      var target = this.checkboxs[index];
      if(target.checkedItems.length == target.items.length){
         target.checkAll = true;
      }else{
         target.checkAll = false;
      }
    }
  }
};
</script>

<style>
.boxOuter {
  margin-bottom: 30px;
}
</style>

コンポーネント化したほうがいい気がしますね…

まとめ

jQueryと違ってVue.jsはデータをどう扱ってバインドして行くかと言うのがキモな気がします… もうちょっといい書き方があったらおしえてください!