[jQuery]DataTablesのデータ書き換え on Local

2016-09-22


テーブル使うならほぼ導入している、ヘビーユーズなjQuery ライブラリ「DataTables」。デフォルトでソート、フィルタ、ページング、件数表示等々、あったら便利な機能が1メソッドで入れられる。さらにはデザイン、機能のカスタマイズもわりと思いのままという優れもの。

今回はそんなDataTablesのカスタマイズ。JavaScriptを使って、ローカルでテーブルの中身を書き換えよう、というもの。いやいや、中身書き換えるだけなら、書き換える要素を持ってきて書き換えるだけじゃん?jQueryなら$("#tdのid").text("hogehoge");で一発じゃん?と思っていた時期がありました。。。

結論から言えば、API「cell().data()」を使って書き換えないと、DataTables内部に情報が届かず、データが壊れるよ?というお話。

最初のきっかけは、DataTablesで作ったテーブルの中にinputをおいて、そこに入力があったものはフィルタしても消さないように、という機能を作るため。イメージとしては、商品一覧な表、入力するのは商品の購入数。フィルタ機能で「みかん」を探して5個、次「りんご」にフィルタして3個、というのが自然な流れだけど、りんごをフィルタした時点でみかんが消えてしまう。現在の入力状況がひと目でわかりにくい、というのが問題。そこできっかけとなった機能を作ることになりました。

デフォルトのままだと実現できそうもないので、APIを使って自作することに。DataTablesの中で、フィルタの機能を司るAPIは「search()」。ここに検索する文字列を放り込んでやればフィルタされるそうな。ただ、特定の行だけ省く、といった機能はない模様。ここから先はいろんな手段があるけど、、、

  1. 消した後、入力された行だけ再表示
    • そんなAPIはぱっと見では、見つからず。「row().child().show()」ってAPIかな、と思ったんだけど、挙動を把握できなかった・・・それに入力された行を保持するのもめんどそう。
  2. 入力されたデータをフィルタ範囲外に移動。例えばヘッダとかフッタとか、別テーブルとか。
    • テーブル構造が崩れそうで怖い。
  3. 入力されたらその行に特定のトークン入れておいて、フィルタするときには必ずそのトークンも含める。いわゆる「キズをつける」方針。一般的には、いわゆらねーか。
    • “入力されたら”はonChangeで行けそう、トークン入れるのも、display: none;なspanタグにhogehoge入れておけばいけそうか。フィルタするときに含めるのも、↑のsearch API、正規表現で渡せるらしいから([検索する単語]|hogehoge)と渡してやればOKか。

ってことで、手段3を採用することに。

ここで本題に入ります。最初は、各行にトークン入れるようのspanをID付で予め作っておいて、onChangeで中身にhogehoge書くという流れ。イメージは↓。

<tbody>
  <tr>
    <td>みかん<div style="display: none;" id="mikan"></div></td>
    <td><input type="number" onChange="$('#mikan').text('hogehoge')"></input></td>
  </tr>
  <tr>
    <td>りんご<div style="display: none;" id="ringo"></div></td>
    <td><input type="number" onChange="$('#ringo').text('hogehoge')"></input></td>
  </tr>
・・・

ところが、jQueryでタグの値を書き換えても、DataTablesにはその情報が届かない!hogehogeをつけて検索しても引っかからないし、ソートのときも無視されている模様。ということは、おそらくは内部で仮想DOM?的なものを持ってて、それを使って動作しているんだろうと推論。さてどうするか?

  1. API「destroy()」を使って再構築→ダメ
    • やってる最中は思いつかなかったけど、DataTables有効なまま書き換えたから、実際のDOMと内部のDOMとがごっちゃになって不可解になってたのかな?ってことはdesroy()してから、ajaxで書き込んで再構築すればよかったかも?
  2. リロードすればってことでAPI「ajax.reload()」→ダメ
    • エラーはく。まぁajaxだし、ソースになるURL設定しないとなー

ということで、先頭に書いた結論、API「cell().data()」でトークンを付与する方法に至りました。

var nowdata = cell("#トークン入れる要素").data(); // 現在の値
var newdata = nowdata + "hogehoge"; // トークンを付与
cell("#トークン入れる要素").data(newdata); // トークン付の値を設定

これにてDataTables内部のDOM?をトークン入りの状態に更新でき、無事search() APIで常時表示できるようになりました。Reactやらなんやらで「仮想DOM」という考え方に触れてたからわりとさくっと推論できたものの、そこに至らなかったらどうなっていたことやら・・・