Vue.js で要素をフィルタリングしてみた

投稿日:2017年07月25日

Tags: html vue JavaScript

お疲れ様です、ナガです。

ブログ記事などをカテゴリでフィルタリングして、チェックボックスでチェックしたカテゴリに紐付いた記事を表示させたい…みたい要望がウェブサイト制作では結構あったりします。

いつもは htmldata-hoge="value" を持たせて jQuery でゴニョゴニョするみたいに書いていました。

Vue.js のナレッジを高める為にもここは一度 Vue.js で実装してみようかと思います。

Vueインスタンスを作成

JS

Vueをマウントする要素と、ダミーの記事データを準備します。

本来なら JSONで返ってくるAPIから、ajax で記事を取得して、データ(今回のコードだと posts)に突っ込むのかなぁ思いますが、今回は直書きします。

カテゴリリストもAPIで取ってこれたらよいのですが、取ってこれない場合は、ajax で取得した記事データからカテゴリを持ってきて、重複してたら処理をスキップして〜…みたいな事をきっとやることになるとは思いますが、今回は直書きします。

チェックしているカテゴリを格納する preview という空の配列をデータに用意します。

var vm = new Vue({
  el: '#main',
  data: {
    posts: [
      {
        name: 'title1',		// 記事のタイトル
        url : '//www.example.com',	// 記事のパーマリンク
        categories: ['php'],	// 紐付いてるカテゴリ(複数化)
        display: true	// 表示するか否かの boolean値
      },
      {
        // ~省略~
      }
    ],
    category_lists: ['html','js','css','php'],	// 全カテゴリ
    preview: []		// チェックボックスでチェックしたカテゴリを格納する
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

html

category_listsfor で回して、カテゴリとチェックボックスを表示させます。

チェックしたカテゴリを preview に入れるために v-model でデータと紐付けます。

posts に格納された記事データを for で回して、表示させます。

v-showpost.display をバインドさせて、値が true な記事のみ表示させます。

カテゴリリスト(categories)は配列なので、更に for で回して表示させます。

<div id="main">
  <h1>カテゴリでフィルタリングするUI DEMO</h1>
  <div>
    <ul class="category_list">
      <li v-for="category in category_lists">
        <input type="checkbox" 
          v-bind:id="category" 
          v-bind:value="category" 
          v-model="preview">
        <label v-bind:for="category">{{ category }}</label>
      </li>
    </ul>
  </div>
  <p>選択しているカテゴリ:{{ preview }}</p>
  <ul class="entry_list">
    <li v-for="post in posts" v-show="post.display">
      <p><a v-bind:href="post.url">{{ post.name }}</a></p>
      <ul><li v-for="category in post.categories">{{ category }}</li></ul>
    </li>
  </ul>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

これで、カテゴリ一覧のチェックボックスと記事の一覧が表示されました。

現状ではチェックボックスを選択してもフィルタリングされないので、フィルタリング用のメソッドを追加します。

フィルタリングさせる

JS

先ほどのJSに選択したカテゴリを記事から探してをフィルタリングする find_categories というメソッドを追加します。

このメソッドでやっていることは、チェックしているカテゴリが1つ以上なら、フィルタリングする為の処理が発火します。

発火したら記事数だけ回します。

そして、チェックしたカテゴリの個数だけ再び回して、記事内にチェックしたカテゴリが含まれていたら表示(display:true)、カテゴリが含まれていなかったら非表示(display:false)にします。

チェックボックスが一つもチェックされていない時は全てを表示(display:true)させます。

var vm = new Vue({
  // ~省略~
  methods: {
    find_categories: function(){
      var posts = this.posts;
      var preview = this.preview;

      if(preview.length > 0) {
        for (var i = 0; i < posts.length; i++) {
          var categories = posts[i].categories;
          for (var j = 0; j < preview.length; j++) {
            if(categories.indexOf(preview[j]) >= 0){
              posts[i].display = true;
              break;
            } else {
              posts[i].display = false;
            }
          }
        }
      } else {
        for (var i = 0; i < posts.length; i++) {
          var categories = posts[i].categories;
          posts[i].display = true;
        }
      }
    }
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

html

先ほどの find_categories をチェックボックスが押下された時に発火させたいので、htmlを修正します。

v-on:click@click でも可) に find_categories を設定します。

これでチェックボックスを押下すると find_categories が発火し、記事をフィルタリングしてくれます。

<div id="main">
  <h1>カテゴリでフィルタリングするUI DEMO</h1>
  <div>
    <ul class="category_list">
      <li v-for="category in category_lists">
        <input type="checkbox" 
          v-bind:id="category"
          v-bind:value="category"
          v-model="preview"
          v-on:click="find_categories">
        <label v-bind:for="category">{{ category }}</label>
      </li>
    </ul>
  </div>
  <!-- 省略 -->
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

DEMO

デモページを作ったのでご確認ください。

見た目もほとんどブラウザが持つデフォルトのスタイルですが…。

カテゴリでフィルタリングするUI DEMO

最後に

今まで jQuery で頑張っていたことも、Vue.js を使うことによって、結構簡潔に書けるようになったのかなぁと思います。

htmlに直接テンプレートを書いて、データと切り分けができるのが、僕が感じる Vue.js の魅力の一つです。

しかし、今のままだと見た目がダサすぎるし、動きもないのであまり実用的ではありません…。

今度はフィルタリングにトランジション効果を付けて見栄えを良くして少しでも実用的なモノにします。

もっと勉強して Vue.js を実務レベルで扱えるように頑張りまーす。

  • シェア:

Last Updated: 1/29/2021, 12:59:28 PM