去年までグリッドテーブル表示はw2uiでしたが、tabulatorに完全乗り換えたmajirouです。
tabulator はセルの値に対して、ルックアップするような形でそのセルに表示する値をformatter
で指定できます。
例えば、formatter: 'money'
とすれば、セルの値1000
を、表示上1,000
と整形してくれます。
規定値以外に関数を定義することができるので、今回はそれを行います。
完成
先に出来上がったものをCodePenに記述しました。
See the Pen tabulator sample by majirou (@majirou) on CodePen.
解説
データについて
今回の例では、以下のような「ウシ」や「ブタ」などの名前と共に、categoryという値をもつデータを扱います。
categoryには、1~5の数値が入っており、それぞれ該当する名称が別途データで提供されています。
const data = [
{ id: 1, name: 'ウシ' , category: 5},
{ id: 2, name: 'ブタ' , category: 5},
{ id: 3, name: 'トリ' , category: 4},
{ id: 4, name: 'ヒツジ' , category: 5},
{ id: 5, name: 'マグロ' , category: 1}
]
const categoryList = [
{ id: 1, name: '魚類'},
{ id: 2, name: '両生類'},
{ id: 3, name: '爬虫類'},
{ id: 4, name: '鳥類'},
{ id: 5, name: '哺乳類'},
]
tabulator本体
これをtabulatorで表示していきます。
高さやレイアウトを適当に定めて、headerFilter
にてヘッダーフィルターを有効にします。
const table = new Tabulator( '#table' , {
height: 300,
layout:"fitColumns",
data,
columns: [
{ field: 'name', title:'Name', headeFilter: true } ,
{ field: 'category', title:'Category', headeFilter: true }
]
}
headerFilter: true
を指定したので、各列のヘッダーにフィルターが付与されたデータテーブルが作成されました。
セルのフォーマット
このままではカテゴリー列は数値そのままなので、一体何のカテゴリーなのかわかりません。
1なら「魚類」、4なら「鳥類」と表示するために、「該当する値を探し、その名前を返す関数」をformatter
に定義します。
columns: [
{ field: 'name', title:'Name', headeFilter: true } ,
- { field: 'category', title:'Category', headeFilter: true }
+ { field: 'category', title:'Category', headerFilter: true, formatter: cell => {
+ const categoryIndex = Object.keys(categoryList).find(f => { return categoryList[f].id === cell.getValue() })
+ return (categoryList[categoryIndex] != null) ? categoryList[categoryIndex].name : cell.getValue()
+ }}
]
すると、先ほど数値が表示されていた部分が、該当する「哺乳類」や「鳥類」に変わって表示されます。
動作しないヘッダーフィルター
ところが、このヘッダーフィルターにて「哺乳類」と入力しても、期待する絞り込み結果にはなりません。
実際のセル値は、1や4といった値であり、見かけ上だけがフォーマットされ「何類」か表示されているだけだからです。
これを解決するためには、headerFilterFunc
に入力した値で絞り込める処理関数を定義します。
ヘッダーフィルター関数のカスタマイズ
公式ドキュメントのFilter Comparison Types項を参照すると、以下のようにサンプルがありますので、これを真似て「該当する値を探し、その名前を返す関数」を定義します。
function customHeaderFilter(headerValue, rowValue, rowData, filterParams){
//headerValue - the value of the header filter element
//rowValue - the value of the column in this row
//rowData - the data for the row being filtered
//filterParams - params object passed to the headerFilterFuncParams property
return rowData.name == filterParams.name && rowValue < headerValue; //must return a boolean, true if it passes the filter.
}
//column definition object in table constructor
{title:"Age", field:"age", headerFilter:"input", headerFilterPlaceholder:"Max Age", headerFilterFunc:customHeaderFilter, headerFilterFuncParams:{name:"bob"}}
## ポイント
* 引数 `headerValue`が、ヘッダーフィルターに入力した値
* 引数 `rowValue`が、セルの値
つまり、今回の例でいうと、
* `headerValue` が、入力した 「哺乳類」
* `rowValue`が、1,4,5などの値
となりますので、入力した値にマッチするかを正規表現で判定し、true/falseを返すことで、フィルタリング関数として動作します。
```diff
columns:[
{ title:"Name", field:"name" ,headerFilter: true},
{ title:'Category', field: 'category', headerFilter: true, formatter: cell => {
const categoryIndex = Object.keys(categoryList).find(f => { return categoryList[f].id === cell.getValue() })
return (categoryList[categoryIndex] != null) ? categoryList[categoryIndex].name : cell.getValue()
- }
+ }, headerFilterFunc: (headerValue, rowValue, rowData, filterParams) => {
+ const regexp = new RegExp(headerValue)
+ const categoryIndex = Object.keys(categoryList).find(f => { return categoryList[f].id === rowValue })
+ return regexp.test(categoryList[categoryIndex].name)
}}
以上で、冒頭の内容が出来上がります。
セル値の置換について
わざわざ自前で関数を用意しなければ、表示に則したヘッダーフィルターが有効にならないと記載しました。
これに関して、formatter
で表示上を変えるのではなく、mutator
を使えばセルの値自体を置換することができます。
従って、headerFilterFunc
を定義しなくても上述のようなヘッダーフィルターは可能になります。
ただし、表示するだけなら問題ないのですが、例えばその行のデータを使って別途フォームを表示し、サーバーとやりとりするなどが発生しうる場合、
mutator
を行ってしまうと、値そのものが変わってしまうので、サーバーに送る際に置換した値を元の値に戻す処理などが必要になります。
まとめ
-
formatter
した値を絞り込むには、headerFilterFunc
に入力値で絞り込む関数を定義する - 絞り込むだけなら
mutator
でセル値を変えてしまう方が楽。ただし、データそのものが置き換わるので注意