meieiのブログ

仕事や日々の備忘録。

AngularJSのUI-Grid自分用まとめ

久々の投稿。
UI-Gridには日本語ドキュメントがないし日本語記事も少ない、どころか英語でも記事が少ないので公式ドキュメントを翻訳機能に頼りながら(いい加減英語学べよって話だけど)読み解いていくしかなく、めちゃくちゃ苦しめられました。
そのためいつかやったことをまとめて書きたいと思いながら半年くらい経ちました。(もっとかもしれない)

結果色々忘れてしまったので、そんなにまとめられないけども、覚えてる限りをまとめておきます。
間違いがあるかもしれません。何かあればお知らせください(コメント誰でもできるんでしたっけ?) 

通常の使い方

まずは基本。

HTML

<div ui-grid="myGrid" ui-grid-pagination ui-grid-edit ui-grid-cellnav></div>

js

angular.module('myApp', [ 'ui.grid', 'ui.grid.pagination', 'ui.grid.edit', 'ui.grid.cellNav'])
  .controller('MyController', ['$scope', 'uiGridConstants', function($scope, uiGridConstants) {
    $scope.myGrid = {
      columnDefs: [
        {
          field: 'name',
          displayName: '名前'
        },
        {
          field: 'age',
          displayName: '年齢'
        },
        {
          field: 'gender',
          displayName: '性別'
        },
        {
          field: 'birthday',
          displayName: '誕生日'
        }
      ],
      data: [
        {
          'name': ありさ,
          'age': 23,
          'gender': female,
          'birthday': 19971018,
        },
        (略)
      ]
    };
  }
]);

リンク埋め込み

cellTemplateに直接HTMLを記述する(変数に入れてからでも)ことでリンクに限らず様々な表現が出来る。
 この時、データのカラム名をnameに設定しているのを前提として、
{{COL_FIELD}}はまさに表示しているカラムの情報になり、
{{row.entity.カラム名}}とすると、同じ行の別のカラムの情報を表示できる。  

cellTemplate: `<div class="ui-grid-cell-contents">
              <a href="#/URL_PREFIX/{{ COL_FIELD }}">{{ COL_FIELD }}</a>
            </div>` 

ボタン埋め込み

埋め込み方はリンクと同じ。
クリックイベントを設定したいときは$scope.hogehogeに関数を定義してng-click="grid.appScope.hogehoge(row.entity)"のように渡す。
(AngularJSでHTMLにクリックイベントを記述する際にvm.hogehoge関数を定義する例が多い気がするがそれだと渡せない)
通常のController.jsとui-gridのスコープが異なり、grid内になると別スコープになるため、vm.などで定義している変数に直接はアクセスできないらしい。

フォーカスインのイベント

フォーカスインではなく「フォーカスが移動したら」というイベントを拾ってその中で判断することで実現できる。
以下を$scope.gridOptions.onRegisterApiに渡しているFunctionの中に入れる。

// cell移動時に処理を行う
gridApi.cellNav.on.navigate($scope, function(newRowcol, oldRowCol) {
  console.log('cellNav.on.navigate'); 
  console.log('new col', newRowcol);
  if (newRowcol.col.colDef.name === 'カラム名') {
    console.log('name is ', newRowcol.col.colDef.name);
    // ここでさらに値の判定とその後の処理
  }
}); 

バリデーションを任意のタイミングで実行

UI-GRIDではバリデーションを設定しているとセルのフォーカスアウトのタイミングでバリデーションが動作するが、フォーカスを当てずにプログラムで自動で番号を振ったり、などしたときはバリデーションが動作しない。そのため、任意のタイミングでバリデーションを動作させたいときは以下のような記述が必要。

var rowsRendred = $scope.gridApi.grid.renderContainers.body.renderedRows;
rowsRendred.forEach(function (row) {
  row.grid.options.columnDefs.forEach(function (colDef) {
    $scope.gridApi.grid.validate.runValidators(row.entity, colDef, row.entity[colDef.field], NaN, $scope.gridApi.grid); 
  });
});  

オリジナルのバリデーション

UI-Gridはバリデーションをいくつか用意しているけど、オリジナルのバリデーションを使いたい場面はよくあるはず。
例えば数値チェック。

uiGridValidateService.setValidator('numValidate', validateFunction, messageFunction);
// uiGridValidateServiceはController.jsに注入が必要

// Validator用の関数
function validateFunction(argument) {
  // 正規表現:実数
  var regExpStr = '^[-]?\\d+(?:\\.\\d+)?$'; 

  // 小数は不許可
  if (argument && !argument.decimal) {
    regExpStr = '^[-]?\\d+$';
  }
   var regexp = new RegExp(regExpStr);
   return function(newValue, oldValue, rowEntity, colDef) {
    if (!newValue) {
      rowEntity.errors[colDef.name] = false;
      return true;
    } else {
      var result = regexp.test(newValue);
      rowEntity.errors[colDef.name] = !result;
      return result;
    }
  };
}

function messageFunction(argument) {
  // エラーメッセージ
  var message = '数値を入力してください';
   if (argument && !argument.decimal) {
    message = '整数を入力してください';
  }
  return message;
}

これでオリジナルのバリデーターができたのでColumnDefsのvalidatorsに設定。

columnDefs: [ 
  {
    name: 'data1',
    displayName: 'データ1',
    validators: {
      numValidate: { decimal: true }
    }
  },
  ...
],

セルのテンプレートを編集時と参照時で分けたい

今回は時刻を扱いたかったが、UI-GRIDはcellTemplateにnumberだったりdateだったりいろいろとあるものの、timeだけ、というのはなさそう。
調べたところ基本的にcellTemplateにHTML書けば動くよ~な情報ばかりで、
cellTemplate: '<div><input type="time"><div>'
のように試したところ、ui-gridの機能で編集用のテキストボックスが出てきてしまいinputでの入力がうまくいかず…   さんざん調べたところ、editableCellTemplateを指定できる!!!

editableCellTemplate: '<div><input type="time"><div>'として、
表示するときにはcellTemplateやcellFilterを用いて形式を指定することで入力時にはinput要素、表示時には文字だけ、とできました。

行のヘッダーをつける

HTMLのテーブルで行のヘッダーを付ける場合、

<tr ng-repeat="data in vm.tableData">
  <th>{{data.title}}</th>
  <td>{{data.data1}}</td>
  <td>{{data.data2}}</td>
  <td>{{data.data3}}</td>
</tr>

でうまいこと出来るやつをUI-Gridでもやりたい。

UI-GRIDの場合はcolumnDefsの中に他のカラムと同様に行ヘッダーも入れ込むのではなく
$scope.gridApi.core.addRowHeaderColumnで行ヘッダーのカラムを追加してあげる必要があるらしい。

$scope.gridOptions = {
  data : data,
  columnDefs: [ 
    { name: 'data1', displayName: 'データ1' }{ name: 'data2', displayName: 'データ2' },
    { name: 'data3', displayName: 'データ3' }
  ],
  onRegisterApi: function( gridApi ) {
    $scope.gridApi = gridApi;
    $scope.gridApi.core.addRowHeaderColumn({
      name: 'rowHeaderCol',
      displayName: '',
      cellTemplate: `<div class="ui-grid-row-header-cell ui-grid-disable-selection">
                      <div class="ui-grid-cell-contents">{{row.entity['rowHeader']}}</div>
                  </div>`
    });
  }
}

以上。

参考:公式ドキュメント http://ui-grid.info