FlexGrid入門

はじめに

FlexGridをAngularJSアプリケーションで使用する手順は以下のとおりです。

  1. AngularJS、Wijmo、およびWijmoのAngularJSディレクティブへの参照を追加します。
  2. appモジュールにWijmo 5ディレクティブを含めます。
    var app = angular.module('app', ['wj']);
  3. データとロジックを提供するコントローラーを追加します。
  4. FlexGridをページに追加してデータにバインドします。
  5. グリッドの外観をカスタマイズするCSSを追加します。

これはFlexGridの自動列生成、列ソート、編集、クリップボードのサポートが含まれるデフォルト動作を作成します。

<html> <head> <link rel="stylesheet" href="css/bootstrap.css"/> <link rel="stylesheet" href="css/wijmo.css" /> <link href="css/app.css" rel="stylesheet" /> <script src="scripts/angular.js"></script> <script src="scripts/wijmo.js"></script> <script src="scripts/wijmo.grid.js"></script> <script src="scripts/wijmo.angular.js"></script> <script src="scripts/app.js"></script> </head> <body ng-app="app" ng-controller="appCtrl"> <!-- this is the grid --> <wj-flex-grid items-source="data"> </wj-flex-grid> </body> </html>
// declare app module var app = angular.module('app', ['wj']); // app controller provides data app.controller('appCtrl', function appCtrl($scope) { // generate some random data var countries = 'US,Germany,UK,Japan,Italy,Greece'.split(','), data = []; for (var i = 0; i < 100; i++) { data.push({ id: i, country: countries[i % countries.length], date: new Date(2014, i % 12, i % 28), amount: Math.random() * 10000, active: i % 4 == 0 }); } // add data array to scope $scope.data = data; });
/* set default grid style */ .wj-flexgrid { height: 300px; background-color: white; box-shadow: 4px 4px 10px 0px rgba(50, 50, 50, 0.75); margin-bottom: 12px; }

結果(ライブ):

列定義

「はじめに」の例では列を定義していなかったため、FlexGridが自動的に列を生成しました。

この例では、HTMLマークアップを使用して列を定義する方法を示します。これをコードで実行することもできますが、マークアップを使用した方がコントローラーとビューの分離が向上します。

列を指定すると、表示する列とその順序を設定できます。 また、各列の幅、見出し、書式設定、配置などのプロパティも制御できます。

このケースでは、スターサイズ指定を使用して[Country]列の幅を設定しています。これにより、[Country]列が拡張されてグリッドの使用可能な幅が満たされるため、空スペースがなくなります。[Revenue]列ではformatプロパティが"n0"に設定されており、この列の数値が桁区切り文字付き、小数点なしになっています。

<wj-flex-grid items-source="data"> <wj-flex-grid-column header="Country" binding="country" width="*"> </wj-flex-grid-column> <wj-flex-grid-column header="Date" binding="date"> </wj-flex-grid-column> <wj-flex-grid-column header="Revenue" binding="amount" format="n0" > </wj-flex-grid-column> <wj-flex-grid-column header="Active" binding="active"> </wj-flex-grid-column> </wj-flex-grid>

結果(ライブ):

選択モード

デフォルトでは、FlexGridではExcelと同様にマウスまたはキーボードを使用してセルの範囲を選択できます。selectionModeプロパティを使用してこのデフォルトの動作を変更し、行、行の範囲、隣接していない行(リストボックスと同様)、または単一セルを選択できるようにするか、あるいはまったく選択できないようにするかを指定できます。

この例では、Wijmo MenuコントロールからselectionModeを選択できます。

<wj-flex-grid items-source="data" selection-mode="{​{selectionMode}}"> </wj-flex-grid> <wj-menu value="selectionMode" header="Selection Mode" > <wj-menu-item value="'None'">None</wj-menu-item> <wj-menu-item value="'Cell'">Cell</wj-menu-item> <wj-menu-item value="'CellRange'">CellRange</wj-menu-item> <wj-menu-item value="'Row'">Row</wj-menu-item> <wj-menu-item value="'RowRange'">RowRange</wj-menu-item> <wj-menu-item value="'ListBox'">ListBox</wj-menu-item> </wj-menu>
// initialize selection mode $scope.selectionMode = 'CellRange';

結果(ライブ):

なし セル 隣接するセル範囲 隣接する行 隣接しない行

行列固定

FlexGrid はExcelのようにスクロール時に行列を固定することができます。固定されているセルは通常セルと同様に選択や編集をすることができます。

このサンプルでは最初の2行と列が固定されています。

<wj-flex-grid control="frozenFlex" items-source="data" frozen-rows="2" frozen-columns="2"> </wj-flex-grid> <button class="btn btn-default" ng-click="toggleFreeze()"> {​{ frozenFlex.frozenRows == 0 ? '列固定設定' : '列固定解除'}} </button>
// toggle frozen rows/columns $scope.toggleFreeze = function () { var flex = $scope.frozenFlex; if (flex) { var frozenCount = flex.frozenRows == 0 ? 2 : 0; flex.frozenRows = frozenCount; flex.frozenColumns = frozenCount; } }
/* frozen cells */ .wj-frozen:not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #f8ffd6; } .wj-frozen.wj-alt:not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #f8ffd6; } /* frozen area edges */ .wj-frozen-row { border-bottom: 1px solid rgba(0,0,0,.5); } .wj-frozen-col { border-right: 1px solid rgba(0,0,0,.5); }

結果(ライブ):

編集

FlexGridは、Excelに似た迅速なセル内編集を組み込みでサポートしています。表示モードと編集モードを切り替える[編集]ボタンを含む列を追加する必要はありません。

ユーザーはセルに文字を入力することによって編集を開始できます。この場合、セルはクイック編集モードになります。このモードでは、方向キーを押すと編集が終了し、選択が別のセルに移動します。

また、[F2]を押すかセルをダブルクリックすることによって編集を開始することもできます。この場合、セルはフル編集モードになります。このモードでは、方向キーを押すとセルテキスト内でキャレットが移動します。編集を終了して別のセルに移動するためには、[Enter]、[Tab]、[Esc]のいずれかのキーを押す必要があります。

編集を終了するとき、データは適切な型に自動的に型変換されます。ユーザーが無効なデータを入力した場合、編集はキャンセルされ、元のデータに戻ります。

グリッド、列、または行レベルで編集を無効にするには、グリッド、列、または行オブジェクトのisReadOnlyプロパティを使用します。この例では、[ID]列が読み取り専用になっています。

<wj-flex-grid items-source="data"> <wj-flex-grid-column header="ID" binding="id" is-read-only="true"> </wj-flex-grid-column> <wj-flex-grid-column header="Country" binding="country" width="*"> </wj-flex-grid-column> <wj-flex-grid-column header="Date" binding="date"> </wj-flex-grid-column> <wj-flex-grid-column header="Revenue" binding="amount" format="n0" > </wj-flex-grid-column> <wj-flex-grid-column header="Active" binding="active"> </wj-flex-grid-column> </wj-flex-grid>

結果(ライブ):

グループ化

FlexGridは、ICollectionViewインタフェース(これは.NETのICollectionViewインタフェースと同一です)によってグループ化をサポートします。グループ化を有効にするには、1つ以上のGroupDescriptionオブジェクトをCollectionView.groupDescriptionsプロパティに追加し、グリッドのshowGroupsプロパティをtrue(デフォルト値)に設定します。

GroupDescriptionオブジェクトは柔軟性が高く、値またはグループ化関数に基づいてデータをグループ化できます。この例では、日付(年別)、金額(5,000超、500~5,000、500未満の3つの範囲別)、およびその他の項目(値別)でグループ化できます。メニューを使用して各グループ化の効果を確認してください。

[Revenue]列にはグループ行の合計が表示されます。このようにするには、列のaggregateプロパティを"Sum"に設定します。列の値を編集すると、集計は自動的に更新されます。

<wj-flex-grid items-source="cvGroup"> <wj-flex-grid-column header="Country" binding="country" width="*"> </wj-flex-grid-column> <wj-flex-grid-column header="Date" binding="date"> </wj-flex-grid-column> <wj-flex-grid-column header="Revenue" binding="amount" format="n0" aggregate="Sum"> </wj-flex-grid-column> </wj-flex-grid> <wj-menu value="groupBy" header="グループ化" > <wj-menu-item value="''">(グループ化なし)</wj-menu-item> <wj-menu-item value="'country'">Country</wj-menu-item> <wj-menu-item value="'amount'">Revenue</wj-menu-item> <wj-menu-item value="'date'">Date</wj-menu-item> <wj-menu-item value="'country,date'">Country、Date</wj-menu-item> <wj-menu-item value="'country,amount'">Country、Revenue</wj-menu-item> <wj-menu-item value="'country,date,amount'">Country、Date、Revenue</wj-menu-item> </wj-menu>
// expose the data as a CollectionView to show grouping $scope.cvGroup = new wijmo.collections.CollectionView(data); $scope.groupBy = ''; // update CollectionView group descriptions when groupBy changes $scope.$watch('groupBy', function () { var cv = $scope.cvGroup; cv.groupDescriptions.clear(); // clear current groups if ($scope.groupBy) { var groupNames = $scope.groupBy.split(','); for (var i = 0; i < groupNames.length; i++) { var groupName = groupNames[i]; if (groupName == 'date') { // ** group dates by year var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName, function (item, prop) { return item.date.getFullYear(); }); cv.groupDescriptions.push(groupDesc); } else if (groupName == 'amount') { // ** group amounts in ranges var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName, function (item, prop) { return item.amount >= 5000 ? '> 5,000' : item.amount >= 500 ? '500 to 5,000' : '< 500'; }); cv.groupDescriptions.push(groupDesc); } else { // ** group everything else by value var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName); cv.groupDescriptions.push(groupDesc); } } } });

結果(ライブ):

(グループ化なし) Country Revenue Date Country、Date Country、Revenue Country、Date、Revenue

フィルタリング

FlexGridは、ICollectionViewインタフェース(これは.NETのICollectionViewインタフェースと同一です)によってフィルタリングをサポートします。フィルタリングを有効にするには、ビューに含めるオブジェクトを決定する関数をCollectionView.filterプロパティに設定します。

この例では、国のフィルタを作成し、入力コントロールからフィルタ値を取得します。

<wj-flex-grid items-source="cvFilter"> </wj-flex-grid> <div class="input-group"> <span class="input-group-addon"> <span class="glyphicon glyphicon-filter"></span> </span> <input type="text" ng-model="filter" class="form-control" placeholder="フィルタリング"/> </div>
// expose the data as a CollectionView to show filtering $scope.filter = ''; var toFilter, lcFilter; $scope.cvFilter = new wijmo.collections.CollectionView(data); $scope.cvFilter.filter = function (item) { // ** filter function if (!$scope.filter) { return true; } return item.country.toLowerCase().indexOf(lcFilter) > -1; }; $scope.$watch('filter', function () { // ** refresh view when filter changes if (toFilter) { clearTimeout(toFilter); } toFilter = setTimeout(function () { lcFilter = $scope.filter.toLowerCase(); $scope.cvFilter.refresh(); }, 500); });

結果(ライブ):

ページング

FlexGridは、IPagedCollectionViewインタフェース(これは.NETのIPagedCollectionViewインタフェースと同一です)によってページングをサポートします。ページングを有効にするには、各ページに表示する項目数をIPagedCollectionView.pageSizeプロパティに設定し、ページを移動するためのUIを提供します。

この例では、JavaScriptを使用して1ページに10項目を表示しています。ナビゲーションボタンが用意されており、ボタンをクリックするとIPagedCollectionViewメソッドが呼び出されます。また、pageIndexプロパティとpageCountプロパティを使用して現在のページと総ページ数を表示しています。

<wj-flex-grid items-source="cvPaging" style="height:auto"> </wj-flex-grid> <div class="btn-group"> <button type="button" class="btn" ng-click="cvPaging.moveToFirstPage()"> <span class="glyphicon glyphicon-fast-backward"></span> </button> <button type="button" class="btn" ng-click="cvPaging.moveToPreviousPage()"> <span class="glyphicon glyphicon-step-backward"></span> </button> <button type="button" class="btn" disabled style="width:100px"> {​{cvPaging.pageIndex + 1 | number}} / {​{cvPaging.pageCount | number}} </button> <button type="button" class="btn" ng-click="cvPaging.moveToNextPage()"> <span class="glyphicon glyphicon-step-forward"></span> </button> <button type="button" class="btn" ng-click="cvPaging.moveToLastPage()"> <span class="glyphicon glyphicon-fast-forward"></span> </button> </div>
// expose the data as a CollectionView to show paging $scope.cvPaging = new wijmo.collections.CollectionView(data); $scope.cvPaging.pageSize = 10;

結果(ライブ):

マスター/詳細

ICollectionViewインタフェースはカレンシー(現在の項目)を組み込みでサポートしているため、FlexGridでマスター/詳細シナリオを実装できます。currentItemを参照し、それをページの要素のバインディングソースとして使用できます。

Note that you have to tell AngularJS when the current item changes. To do that, attach a handler to the ICollectionView.currentChanged event and call $scope.$apply as shown in the JS tab of this sample.

<wj-flex-grid items-source="cvFilter" is-read-only="true"> <wj-flex-grid-column header="Country" binding="country" width="*"> </wj-flex-grid-column> <wj-flex-grid-column header="Date" binding="date"> </wj-flex-grid-column> </wj-flex-grid> <dl class="dl-horizontal"> <dt>ID</dt> <dd>{​{cvFilter.currentItem.id}}</dd> <dt>Country</dt> <dd>{​{cvFilter.currentItem.country}}</dd> <dt>Date</dt> <dd>{​{cvFilter.currentItem.date | date}}</dd> <dt>Revenue</dt> <dd>{​{cvFilter.currentItem.amount | number:2}}</dd> </dl>
// tell scope when current item changes $scope.cvFilter.currentChanged.addHandler(function () { $scope.$apply('cvFilter.currentItem'); });

結果(ライブ):

ID
{{cvFilter.currentItem.id}}
Country
{{cvFilter.currentItem.country}}
Date
{{cvFilter.currentItem.date | date}}
Revenue
{{cvFilter.currentItem.amount | number:2}}

セルテンプレート

FlexGridでは、itemFormatterプロパティを使用してセルの内容を完全に制御できます。グリッド用に提供されているAngularJSディレクティブはこれを使用してインラインセルテンプレートをサポートしているため、プレーンHTMLを使用してセルの外観を定義できます。

To define a cell template for a column, add the HTML to display in each cell to the column definition. Use the $item variable to access the data item from within the template.

<wj-flex-grid items-source="data"> <wj-flex-grid-column header="Country" binding="country" width="*" is-read-only="true"> <img ng-src="resources/{​{$item.country}}.png" /> {​{$item.country}} </wj-flex-grid-column> <wj-flex-grid-column header="Date" binding="date"> </wj-flex-grid-column> <wj-flex-grid-column header="Revenue" binding="amount" format="n0" > </wj-flex-grid-column> <wj-flex-grid-column header="Active" binding="active"> </wj-flex-grid-column> </wj-flex-grid>

結果(ライブ):

{{$item.country}}

条件付きスタイル設定

wj-flex-flex-grid-columnディレクティブはng-styleディレクティブをサポートします。これにより、各セルのデータの表示に使用されるスタイルをその値に基づいてカスタマイズできます。

This example uses a JavaScript function to create value ranges that return named colors. We then call this function in the Revenue column inside the ng-style directive and use the $item variable to pass in the data and set the color.

<wj-flex-grid items-source="data"> <wj-flex-grid-column header="Country" binding="country" width="*"> </wj-flex-grid-column> <wj-flex-grid-column header="Date" binding="date"> </wj-flex-grid-column> <wj-flex-grid-column header="Revenue" binding="amount" format="n0" ng-style="{color:getAmountColor($item.amount)}"> </wj-flex-grid-column> <wj-flex-grid-column header="Active" binding="active"> </wj-flex-grid-column> </wj-flex-grid>
// get the color to use to display the amount $scope.getAmountColor = function (amount) { if (amount < 500) return 'darkred'; if (amount < 2500) return 'black'; return 'darkgreen'; }

結果(ライブ):

テーマ

FlexGridの外観はCSSで定義されます。デフォルトテーマに加えてプロフェッショナルなデザインのテーマが多数用意されており、すべてのWijmoコントロールの外観をカスタマイズして一貫性のある魅力的な見た目を提供できます。

グリッドの外観はCSSを使用してカスタマイズできます。そのためには、デフォルトテーマから新しいCSSファイルにCSSルールをコピーし、必要なスタイル属性を変更します。

この例では、"custom-flex-grid"クラスをグリッド要素に追加し、"custom-flex-grid"クラスを持つグリッド用に白黒および罫線なしのシンプルなテーマを作成するCSSルールを定義しています。

また、列のソート方向、およびグループ化されたグリッドのアウトラインノードを示すために使用されるグリフの外観もカスタマイズしています。カスタムグリフを表示するには、列見出しセルをクリックします。

<wj-flex-grid items-source="data" class="custom-flex-grid"> </wj-flex-grid>
/* create a 'custom-flex-grid' theme for the FlexGrid */ .custom-flex-grid .wj-header.wj-cell { background-color: #000; color: #fff; font-weight: bold; border-right: solid 1px #404040; border-bottom: solid 1px #404040; } .custom-flex-grid .wj-cell { border: none; background-color: #fff; } .custom-flex-grid .wj-alt:not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #fff; } .custom-flex-grid .wj-state-selected { background: #000; color: #fff; } .custom-flex-grid .wj-state-multi-selected { background: #222222; color: #fff; } /* override the glyphs used to show sorting and grouping */ .custom-flex-grid .wj-glyph-up { background-image:url('../resources/ascending.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; opacity: 1; } .custom-flex-grid .wj-glyph-down { background-image:url('../resources/descending.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; opacity: 1; } .custom-flex-grid .wj-glyph-right { background-image:url('../resources/collapsed.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; } .custom-flex-grid .wj-glyph-down-right { background-image:url('../resources/expanded.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; }

結果(ライブ):

ツリーと階層データ

グループ化に加えて、FlexGridは階層データ(サブ項目のリストを持つ項目で構成されたデータ)もサポートします。この種の階層構造は非常に一般的で、通常はツリー表示コントロールで表示されます。

FlexGridで階層データソースを使用するには、子要素を含むデータ要素の名前をchildItemsPathプロパティに設定します。グリッドがデータを自動的にスキャンしてツリーを構築します。

<wj-flex-grid class="custom-flex-grid" items-source="treeData" child-items-path="items" allow-resizing="None" selection-mode="ListBox" headers-visibility="None"> <wj-flex-grid-column binding="name" width="*"> </wj-flex-grid-column> <wj-flex-grid-column binding="length" width="80" align="center"> </wj-flex-grid-column> </wj-flex-grid>
// hierarchical data $scope.treeData = [ { name: '\u266B Adriane Simione', items: [ { name: '\u266A Intelligible Sky', items: [ { name: 'Theories', length: '2:02' }, { name: 'Giant Eyes', length: '3:29' }, { name: 'Jovian Moons', length: '1:02' }, { name: 'Open Minds', length: '2:41' }, { name: 'Spacetronic Eyes', length: '3:41' }] } ]}, { name: '\u266B Amy Winehouse', items: [ { name: '\u266A Back to Black', items: [ { name: 'Addicted', length: '1:34' }, { name: 'He Can Only Hold Her', length: '2:22' }, { name: 'Some Unholy War', length: '2:21' }, { name: 'Wake Up Alone', length: '3:43' }, { name: 'Tears Dry On Their Own', length: '1:25' }] }, // more hierarchical data...
/* override the glyphs used to show sorting and grouping */ .custom-flex-grid .wj-glyph-up { background-image:url('../resources/ascending.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; opacity: 1; } .custom-flex-grid .wj-glyph-down { background-image:url('../resources/descending.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; opacity: 1; } .custom-flex-grid .wj-glyph-right { background-image:url('../resources/collapsed.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; } .custom-flex-grid .wj-glyph-down-right { background-image:url('../resources/expanded.png'); background-repeat: no-repeat; background-position: bottom right; width: 1em; height: 1em; border-top: 0px; border-bottom: 0px; border-left: 0px; border-right: 0px; }

結果(ライブ):

ツリーのソート

既定では、階層データを含むグリッドでは最上位階層の項目しかソートできません。これは、childItemsPathプロパティがCollectionViewではなくグリッドに属していて、CollectionViewがデータの階層を知ることができないためです。

グリッドの子項目をソートするためには、グリッドのsortedColumnイベントをハンドルして、項目を列挙して子項目のソートを独自に構成する必要があります。

この例では、最上位階層の項目と同じ順番で子項目をソートします。この方法では、CollectionView_sortItemメソッドを使用して項目を比較して、子項目配列でsortメソッドを実行することができます。これはCollectionViewが内部で行っている処理と同じです。

<wj-flex-grid class="custom-flex-grid" items-source="treeData" child-items-path="items" selection-mode="ListBox" headers-visibility="Column" sorted-column="sortedColumn(s,e)"> <wj-flex-grid-column binding="name" width="*"></wj-flex-grid-column> <wj-flex-grid-column binding="length" width="80" align="center"></wj-flex-grid-column> </wj-flex-grid>
$scope.sortedColumn = function (s, e) { var view = s.collectionView; if (view && s.childItemsPath) { for (var i = 0; i < view.items.length; i++) { sortItem(view.items[i], view, s.childItemsPath); } view.refresh(); } } function sortItem(item, view, childItemsPath) { var children = item[childItemsPath]; if (children && wijmo.isArray(children)) { children.sort(view._compareItems()); for (var i = 0; i < children.length; i++) { sortItem(children[i], view, childItemsPath); } } }

結果(ライブ):

null値の扱い

デフォルトでは、FlexGridは文字列型の列に空文字を入力することができ、他の列型では空文字やnull値を許容しません。

isRequiredプロパティをグリッドの列に設定することでこの動作を変更することができます。isRequiredプロパティをfalseに設定すると、列型に関わらず空文字の入力を許容することができます。逆にtrueに設定すると、文字列型であっても空文字の入力を許容しません。

isRequiredにnullを設定すると、デフォルト動作(文字列型のみ空文字の入力を許容する)に戻ります。

グリッドは以下のデフォルト動作に戻します。最初の列にisRequiredをtrueに設定し、他の列はfalseに設定します。空文字を入力する、またはDeleteキーによってコンテンツを削除することができます。

<wj-flex-grid items-source="data"> <wj-flex-grid-column header="Country" binding="country" width="*" is-required="true"> </wj-flex-grid-column> <wj-flex-grid-column header="Date" binding="date" is-required="false"> </wj-flex-grid-column> <wj-flex-grid-column header="Revenue" binding="amount" format="n0" is-required="false"> </wj-flex-grid-column> <wj-flex-grid-column header="Active" binding="active" is-required="false"> </wj-flex-grid-column> </wj-flex-grid>

結果(ライブ):