WEBOPIXEL

Vue.jsでTableをソートしたり検索したりしてみる

Posted: 2017.01.16 / Category: javascript 

Vue.js 2.0 を使用してTableで並べたリストの検索フィルターとかソートなどをやってみます。

Sponsored Link

データをテーブルに表示する

最初に表示するデータを作成して、tableにシンプルに表示してみましょう。
大まかにデータを入れる(data)、ボタンクリック時などに実行される(methods)、フィルター処理を記述する(computed)を作成していきます。

JavaScript

var app = new Vue({
    el: '#app',
    data: {
    },
    methods: {
    },
    computed: {
    }
});

ではdataに表示するデータを記述していきましょう。
今回はタスクリストを想定して作成してきます。

JavaScript

data: function () {
    var columns = {
        id: 'ID',
        subject: '件名',
        category: 'カテゴリ',
        date: '日付'
    };
    return {
        columns: columns,
        tasks: [
            {
                id: 1,
                subject: 'タスク1',
                category: 'カテゴリー1',
                date: '2016-12-01'
            },
            ...
        ]
    }
},

columnsはタイトル部分の配列で、tasksが実際のデータになります。
直接オブジェクトを返さないでfunctionにすることでデータを入れる前に処理を入れることができます。
これだけだと意味がないですが、後々ソートするときに簡単にできるよになります。

次にHTMLを作成します。
作成したcolumnsとtasksをループさせてるだけですね。

HTML

<div id="app">
	<table>
		<thead>
		<tr>
			<th v-for="(value, key) in columns">
				{{ value }}
			</th>
		</tr>
		</thead>
		<tbody>
		<tr v-for="task in tasks">
			<td v-for="(value, key) in columns">
				{{ task[key] }}
			</td>
		</tr>
		</tbody>
	</table>
</div>

ソート(並び順)の変更

テーブルのカラム(th)をクリックすると、その列がソートされるという動作をさせてみます。

data: function () {
    var columns = {...};
    var sortOrders = {};
    Object.keys(columns).forEach(function (key) {
        sortOrders[key] = 1
    });

    return {
        columns: columns,
        tasks: [...],
        sortKey: '',
        sortOrders: sortOrders
    }
},

data部分はsortKey(現在のソートしているキー)とsortOrders(各項目のソート方向)を追加しています。sortOrdersはcolumnsを元に作成しています。

あとは実際のソート処理の部分です。

methods: {
    sortBy: function(key) {
        this.sortKey = key;
        this.sortOrders[key] = this.sortOrders[key] * -1;
    }
},
computed: {
    filteredTasks: function () {
        var data = this.tasks;

        var sortKey = this.sortKey;
        var order = this.sortOrders[sortKey] || 1;

        if (sortKey) {
            data = data.slice().sort(function(a, b){
                a = a[sortKey];
                b = b[sortKey];
                return (a === b ? 0 : a > b ? 1 : -1) * order;
            });
        }
        return data;
    }
}

filteredTasksをtd部分のv-forに指定して、thをクリックしたときsortByが実行されるようにすればソート部分は完成です。

HTML

<div id="app">
	<table>
		<thead>
		<tr>
			<th v-for="(value, key) in columns" @click="sortBy(key)">
				{{ value }}
				<span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'"></span>
			</th>
		</tr>
		</thead>
		<tbody>
		<tr v-for="task in filteredTasks">
			<td v-for="(value, key) in columns">
				{{task[key]}}
			</td>
		</tr>
		</tbody>
	</table>
</div>

入力した文字列検索で絞り込む

#app内にinputタグを作成します。

HTML

<input type="text" v-model="searchWord" placeholder="キーワード検索">

v-modelで指定した同じ文字列の変数をdataに記述します。

data: function () {
    ...
    return {
        // 検索文字列
        searchWord: '',
    }
},

実際の絞り込みの処理はfilteredTasksに記述します。

computed: {
    filteredTasks: function () {
        var data = this.tasks;

        var filterWord = this.searchWord && this.searchWord.toLowerCase();
        if(filterWord) {
            data = data.filter(function (row) {
                return Object.keys(row).some(function (key) {
                    return String(row[key]).toLowerCase().indexOf(filterWord) > -1
                })
            })
        }
        return data;
    }
}

セレクトボックスで絞り込む

カテゴリーの部分をセレクトボックスから選択して検索できるようにしてみましょう。
dataに新たにカテゴリーのオブジェクト(categories)と選択したとき格納する変数(selectCategory)を作成します。

selectCategory: '',
categories: {
    1:'カテゴリー1',
    2:'カテゴリー2',
    3:'カテゴリー3'
},

tasks配列はカテゴリー名が直接入ってましたが、idで入っていた方がいいかもしれません。

tasks: [
{
	id: 1,
	subject: 'タスク1',
	category: 1,
	date: '2016-12-01'
},
...
]

次にselectボックスを作成します。
v-modelにselectCategoryを記述して、categoriesの内容でoptionを出力します。

<select v-model="selectCategory">
	<option value="">カテゴリー</option>
	<option v-for="(value,key) in categories" v-bind:value="key">
		{{ value }}
	</option>
</select>

最後にfilteredTasksにカテゴリー絞り込みの処理を追記します。

computed: {
    filteredTasks: function () {
        var data = this.tasks;

        var filterWord = this.searchWord && this.searchWord.toLowerCase();
        var filterCategory = this.selectCategory && Number(this.selectCategory);

        if(filterWord || this.selectCategory) {
            data = data.filter(function (row) {

                // カテゴリー絞り込み
                if (filterCategory) {
                    if (row['category'] !== filterCategory) {
                        return false;
                    }
                }

                // キーワード絞り込み
                if (filterWord) {
                    return Object.keys(row).some(function (key) {
                        if (String(row[key]).toLowerCase().indexOf(filterWord) > -1) {
                            return true;
                        }
                    });
                }
                return row;
            })
        }
        return data;
    }
}

以上、ほぼ公式にあるサンプルでした。