2017-06-29

tobijibu

w2uiでスプレッドシートのようなものを作る

w2uiにはグリッド表示と、selectTypeeditableというオプションがあります。 これらのオプションと、Ajaxを使ってw2uiのグリッドを使うことで、 GoogleDocsのスプレッドシートのようにデータを取得したり、更新することができます。

今回、実際にw2uiを使ってシームレスにデータの表示、更新が出来るようなグリッドテーブルを作ってみましょう。

動物の名前と、その数(匹数)、更新日時を一覧表示して更新するシートを作ってみます。

説明で使うソースはこちらにあります。

データベース

まずはデータを保存するテーブルを作ります。

今回は以下のように作成しました。cntのセルを編集すると、 DBのcntupdatedを更新するようにします。


zoo
+----+-------+-----+---------+
| id | name  | cnt | updated |
+----+-------+-----+---------+
|  1 | dog   |   1 | NULL    |
|  2 | cat   |   2 | NULL    |
|  3 | tiger |   3 | NULL    |
|  4 | lion  |   4 | NULL    |
|  5 | wolf  |   5 | NULL    |
+----+-------+-----+---------+

このテーブルのデータを取得・表示して、w2uiから編集できるようにします。

HTML

w2uiを表示するHTMLを生成します。

特別な指定はしていません。w2uiを読み込み、グリッド表示する要素のidを付与します。 今回はmygridにしました。

併せて、データ取得用のフォーム(#getData)と、データ保存用のフォーム(#postData)を入れています。

<!DOCTYPE html>
<html>
<head>
<link href="./w2ui-1.5.rc1.min.css" rel="stylesheet">
<script
  src="https://code.jquery.com/jquery-2.2.4.min.js"
  integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
  crossorigin="anonymous"></script>
<script src="./w2ui-1.5.rc1.min.js"></script>
</head>
<body>
    <div id="grid" style="height: 200px"></div>

    <form id="getData">
    <input type="hidden" name="mode" value="select">
    </form>
    <form id="postData">
    <input type="hidden" name="mode" value="update">
    <input type="hidden" name="name">
    <input type="hidden" name="cnt">
    </form>
</body>
<script src="./data.js"></script>
</html>

1点、補足しますと、現在(2017/6/28)時点ではjQuery3系ではw2uiが動きませんでした。 そのため、2.2.4を読み込んでいます。

github上では修正済みとのことですが、私の環境では正常に動きませんでしたので、 今回は3系ではなく2系を使っています。

This has already been adressed and already been fixed.

Related issues: #1378 and #1381

This fiddle uses the Working Example code you're mentioning with JQuery 3.1.1 - looks good to me:

http://jsfiddle.net/octsyvga/1/
引用 - github.com (https://github.com/vitmalina/w2ui/issues/1487)

JavaScript

次にJSでw2uiの処理を作っていきます。

まずはグリッドを生成する処理です。$('#mygrid').w2grid();を使います。

init = function() {
  let get = doAjax('#getData');
  get.done(function() {
    $('#mygrid').w2grid({
      name: 'mygrid',       // --> w2ui.mygridという形で利用できるようにします。
      selectType : 'cell',
      columns : DATA.head,  // --> カラム名
      records : DATA.data,  // --> データ行
      summary : DATA.sum,   // --> 合計行
      onChange: function(event) {
        edit(event);
      },
    });
  });
}
init();

nameにはmygridを指定しました。どんな名前でも構いません。 nameを指定すると、各処理の中でw2ui.name.~といった形でw2uiのオブジェクトにアクセスすることが出来るようになります。

selectTypeにはcellを指定します。 デフォルトでは行選択になるのですが、このプロパティを指定するとセル選択することが出来るようになります。

今回はセルを編集してその内容をPOST送信したいので、onChangeイベントも設定しました。

columnsrecordssummaryの値は次のデータ取得で説明します。


次にAjaxを使ったデータ取得です。

w2uiではjQueryを使いますので、Ajax処理は簡単に書くことができます。

doAjax = function(id) {
  let defer = $.Deferred(),
      formData = $(id).serialize();
  $.ajax({
    type     : 'POST',
    url      : './data.php',
    dataType : 'json',
    data     : formData,
    success: function(json){
      DATA.head = json.head;  // --> カラム名
      DATA.data = json.data;  // --> データ行
      DATA.sum  = json.sum;   // --> 合計行
      defer.resolve();
    }
  });
  return defer.promise();
}

データを取得してからグリッドを生成したいので、$.Deferred()を使っています。 これを使うことで、データを取得してdefer.resolve();を解決した後にget.done(function() {});を実行することができます。

Ajaxは非同期通信なので、$.Deferred()を使わず、そのまま流れに任せてしまうと、 データを取得する前に$('#mygrid').w2grid()を実行してしまい、グリッドを表示することができません。

データを取得したら、DATAオブジェクトの各値にデータを代入します。 headには"カラム名"、dataには"グリッドのレコード"、sumには"合計数レコード"を代入しています。 これらはw2ui生成時点でcolumnsrecordssummaryにそれぞれ代入します。


続いて、編集時の処理です。

w2uiグリッドは編集したセルの情報や、編集情報を保持しています。 その値を使って、編集されたかどうかを判定してデータをPOSTします。

edit = function(event){
  let oldVal = event.value_previous,  // --> 編集前の値
      newVal = event.value_new,       // --> 編集後の値
      row    = event.index;  // --> 編集した行
      name;
  //空のまま変更がなかった場合
  if (! oldVal && ! newVal) return;
  //変更が無かった場合
  if (oldVal == newVal) return;
  //「数」カラムでない場合
  if (event.column != 1) return;

  // 編集した行の"name"
  // "mygrid"はw2grid生成時に指定する、nameプロパティの値です。
  name = w2ui.mygrid.records[row].name;

  //nameとcntをformに設定
  $('#postData input[name=name]').val(name);
  $('#postData input[name=cnt]').val(newVal);

  //データ送信
  let post = doAjax('#postData');
  //グリッドを更新
  post.done(function() {
    //最新のデータを設定して更新します。
    w2ui.mygrid.records = DATA.data;
    w2ui.mygrid.summary = DATA.sum;
    w2ui.mygrid.refresh();
  });
}

ポイントはw2ui.mygrid.records[row].nameです。 グリッドの情報はw2ui.mygrid.~から取得することができます。 w2uiグリッドは編集したセル情報を持っていますが、同じ列の情報は持っていないので、 "編集したセルの行にあるname"を取得する必要があるわけです。

最後に取得したデータを入れ直して、w2ui.mygrid.refresh();でグリッド全体を更新します。

サーバーサイド処理

今回のサンプルでサーバサイド処理はphpを使っています。 どのような処理でも構いませんが、最終的にJSON形式でデータを出力するようにします。

処理の説明は省略しますが、以下のような形式が出力できればOKです。

"data":[
  {"recid":"1","name":"dog","cnt":"4","updated":"2017-06-27 20:57:06"},
  ...,
],
"head":[
  {"field":"name","caption":"\u540d\u524d","size":"30%"},
  {"field":"cnt","caption":"\u6570","size":"30%",
      "editable":{"type":"int","min":0,"max":100}
  },
  {"field":"updated","caption":"\u66f4\u65b0\u65e5\u6642","size":"30%"}
],
"sum":[
  {"recid":9999,"name":"\u5408\u8a08","cnt":18,"updated":null}
]

headfield: cntにはeditableを定義するようにします。 このオプションを指定すると、対象カラムのセルを編集可能します。

サンプルコードではdata.phpを同じディレクトリに格納してあります。 このphpは処理を単純化しており、セキュリティリスクが高いので、ローカル確認用でのみご利用ください。


以上で完了です。

たったこれだけの処理でスプレッドシートのような処理が実装できます。 もちろん実際に使う場合にはエラー処理を追加したり、セキュリティを考慮する等、作り込む必要があります。

実際に同じような作りで40×40ほどのセルで利用しています。 ただ、環境にもよりますが、このサイズになってくると少々もたついてきます。 少し複雑な処理をしているのもあると思いますが、編集の度にリフレッシュしているのが一番の要因だと思われます。

逆にそれほど大きくないシートであれば、ほぼ問題無く運用することができますのでオススメです。

スプレッドシートの方が高機能で便利ですが、 スプレッドシートを使うことができないような状況では良いかもしれませんね。


今回の説明で利用したファイルはこちらにあります。実験用データも含めています。