handsontableを使った一覧表示

Excel風グリッドライブラリのhandsontableを使って一覧表示をできるようにします。rails tutorialでも一覧表示はできましたが、handsontableを使うと後々一括登録もできるようになります。また、一覧表示にしても並べ替え(行も列も)ができるようになります。

準備(インストール作業)

詳細は公式サイトを見てください。React, Vue, Angularなどに対応したライブラリもありますが、ピュアなjavascriptライブラリ版もあります。私の場合はピュアjavascriptでやります。

なお、バージョンは2019年11月時点で7.2.2が最新ですが、無料版は6.2.2です。7以上は基本的に有料です。非営利などであれば無料でも使えるようですが、google Adsenceなどを考えているなら、6.2.2にしておいたほうが無難だと考えます。

https://github.com/handsontable/handsontable/tree/6.2.2

インストールは単純です。min.jsやmin.cssをvendor/assetsの下に配置し、application.jsやcssに下記のようにパスを追加します。なお、moment.min.jsは、日付関係の演算を行うためのライブラリで、handsontableには関係ありません。

//= require_tree ../../../vendor/assets/javascripts
 *= require_tree ../../../vendor/assets/stylesheets

jqueryやjquery-uiについては、面倒であればgoogleでもどこでもよいのでCDNを使ったほうが楽かもしれません。CDNを使う場合はapplication.htmlなどのアセットにscript srcを通せばOKです。

一覧表示をする

handsontableは大きく分けると下記のように構成されています。

  • データ
  • ヘッダ(見出し)
  • ヘッダ(型定義)

ヘッダは、見出しの部分と、型定義で構成されています。型定義にはidがありますが、この値は、データ上の「キー」と一致することになります。データの設定方法には、ajaxから受け取ったレスポンスこともできますし、初期表示時にグリッド表示をする場合には、hiddenなエレメントを経由して表示することもできます。

var initHours = function(){
    var data = eval($('#man_hours_hidden').data('json'));
    var grid = document.getElementById('man_hours_grid');
    var hot = new Handsontable(grid, {
        data: data,
        colHeaders: ['', '' ,'', '削除', '工数名', '工数確定回数', '開始日', '終了日'],
        columns: [
            {
              data: "id",
              type: 'numeric',
              readOnly: true
            }, {
              data: '_status',
              type: 'text',
              readOnly: true
            }, {
              data: 'status',
              type: 'text',
              readOnly: true
            }, {
              data: "select",
              type: 'checkbox'
            }, {
              data: "man_hour_name",
              type: 'text',
              validator: function (value, callback) {
                callback(true);
              },
              maxLength: 50,
              editor: 'maxlength'
            }, {
              data: "results",
              type: 'numeric',
              readOnly: true
            }, {
              data: "valid_from",
              type: 'time',
              validator: function (value, callback) {
                callback(true);
              },
              editor: 'ymd'
            }, {
              data: "valid_to",
              type: 'time',
              validator: function (value, callback) {
                callback(true);
              },
              editor: 'ymd'
            }
        ],
        width: 710,
        height: 120,
        colWidths: [1, 1, 30, 50, 200,100, 150,150],
        enterMoves: { row: 1, col: 0 },
        outsideClickDeselects: true,
        manualColumnResize: true,
        manualColumnMove: true,
        columnSorting: true
    });
};
<div id="man_hours_hidden" data-json="<%= @form.man_hours.to_json %>">
</div>

つまるところdataとは、マップの配列

下記はcontroller側に書いたレスポンスに組み立て処理です。つまるところhandsontableのdataとして表示するデータは、1エレメントがマップの、配列です。先ほどのヘッダ(型定義)のidと一致する構成になるようにします。

man_hours = []
man_hours_tmp = man_hours(user)
man_hours_tmp.each do |m|
  
  valid_from = toYMD(m["valid_from"])
  valid_to   = toYMD(m["valid_to"])

  man_hours.push(FormManHour.new(
    id: m["id"] ,man_hour_name: m["name"],results: m["results_cnt"] ,
    valid_from: valid_from ,valid_to: valid_to
    ))
end
//中略
@form = FormSettings.new( 
  user_name: user.name
  , email: user.account.email
  , man_hours: man_hours
  , man_hour_results: man_hour_results
  , public_id: public_id 
)

次回→追加、削除、反映ボタンを作る

次回は追加や削除をできるようにします。