CollectionView入門

このページではCollectionViewクラスの基本的な使用方法を示します。

Wijmoは、扱いやすい強力なデータ層に基づく堅固なインフラストラクチャを備えています。 主要なデータバインディングインタフェースはICollectionViewです。 Wijmoには、ICollectionViewを実装するクラスがいくつか用意されています。 最も基本的なものはCollectionViewで、これは通常のJavaScript配列をデータソースとして使用します。

CollectionViewクラスは以下のインタフェースを実装しています。

CollectionViewクラスは、データに加えられた変更を追跡できます。 この機能は変更をサーバーに送信する必要がある状況で役立ちます。

はじめに

CollectionViewクラスを使用するには、まずクラスを宣言し、通常の配列をデータソースとして渡します。 次に、itemsプロパティを使用してビューにアクセスします。

ここでは、CollectionViewインスタンスをFlexGridに表示します。

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

  1. Wijmoへの参照を追加します。
  2. FlexGridのホストとして機能するマークアップを追加します。
  3. Javascriptを使用して、CollectionViewインスタンスとFlexGridインスタンスを初期化します。
  4. (オプション)グリッドの外観をカスタマイズするCSSを追加します。
HTML
<div id="gsGrid"></div>
JS
var cvGettingStarted = new wijmo.collections.CollectionView(getData(10)); var gsGrid = new wijmo.grid.FlexGrid('#gsGrid', { itemsSource: cvGettingStarted }); // create some random data function getData(cnt) { var data = [], dt = new Date(), countries = ['US', 'Germany', 'UK', 'Japan', 'Italy', 'Greece'], products = ['Widget', 'Gadget', 'Doohickey'], colors = ['Black', 'White', 'Red', 'Green', 'Blue']; for (var i = 0; i < cnt; i++) { var date = new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60), countryId = Math.floor(Math.random() * countries.length), productId = Math.floor(Math.random() * products.length), colorId = Math.floor(Math.random() * colors.length); data.push({ id: i, start: date, end: date, country: countries[countryId], product: products[productId], color: colors[colorId], amount: Math.random() * 10000 - 5000, active: i % 4 === 0, }); } return data; }
CSS
/* 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; }

結果(ライブ):

現在位置の管理

CollectionViewクラスを使用すると、現在のレコードを管理できます。

この例は、CollectionViewクラスによって提供されるAPIを使用して現在位置を管理する方法を示します。グリッドの行またはグリッドの上のボタンをクリックして現在位置を変更できます。

currentPositionプロパティを使用してコレクション内の現在位置を確認しています。 また、以下のメソッドで現在位置を変更します。

現在位置が変更されると、currentChangingおよびcurrentChangedイベントを使用して位置を追跡することができます。 currentChangingイベントで現在位置の変更をキャンセルすることができます。

[次へ移動]ボタンをクリックすると、現在位置が次の位置に移動します。 [前へ移動]をクリックすると、現在位置が前の位置に移動します。 [4行目で停止]ボタンをクリックすると、現在位置が4行目のとき、位置が変更できなくなります。 [停止の解除]ボタンをクリックすると、現在位置が再び自由に移動できるようになります。

HTML
<div class="row-fluid well btn-group"> <button class="btn btn-default" id="btnCRMMoveNext">次へ移動</button> <button class="btn btn-default" id="btnCRMMovePrev">前へ移動</button> <button class="btn btn-default" id="btnCRMStop4">Stop at the 4th Row</button> <button class="btn btn-default" id="btnCRMReset">停止の解除</button> </div> <div id="crmGrid"></div>
JS
var cvCRM = new wijmo.collections.CollectionView(getData(10)); var crmGrid = new wijmo.grid.FlexGrid('#crmGrid', { isReadOnly: true, selectionMode: 'Row', itemsSource: cvCRM }); // handle prev/next buttons document.getElementById('btnCRMMoveNext').addEventListener('click', function () { cvCRM.moveCurrentToNext(); }); document.getElementById('btnCRMMovePrev').addEventListener('click', function () { cvCRM.moveCurrentToPrevious(); }); // handle the currentChanging event to restrict navigation document.getElementById('btnCRMStop4').addEventListener('click', function () { cvCRM.currentChanging.addHandler(stopCurrentIn4th); }); // remove navigation restriction document.getElementById('btnCRMReset').addEventListener('click', function () { cvCRM.currentChanging.removeHandler(stopCurrentIn4th); }); // restrict navigation at the fourth item function stopCurrentIn4th(sender, e) { e.cancel = sender.currentPosition == 3; }

結果(ライブ):

ソート

CollectionViewクラスは、ICollectionViewインタフェース(これは.NETのICollectionViewインタフェースと同一です)によってソートをサポートします。 ソートを有効にするには、1つ以上のSortDescriptionオブジェクトをCollectionView.sortDescriptionsプロパティに追加します。 ソートされた結果はCollectionView.itemsプロパティから取得できます。

SortDescriptionオブジェクトは柔軟性が高く、値の昇順または降順に基づいてデータをソートできます。 以下のサンプルでは、最初のリストで選択されたフィールド値に基づいてコレクションをソートすることができます。 2番目のリストでソート順を指定することもできます。

HTML
<div class="row-fluid well row"> <div class="col-md-8"> <div id="sortingFieldNameList"></div> </div> <div class="col-md-4"> <div id="sortingOrderList"></div> </div> </div> <div id="sortingGrid"></div>
JS
var cvSorting = new wijmo.collections.CollectionView(getData(10)); var sortingGrid = new wijmo.grid.FlexGrid('#sortingGrid', { isReadOnly: true, allowSorting: false, itemsSource: cvSorting }); // select sort field and direction var sortingFieldNameList = new wijmo.input.ComboBox('#sortingFieldNameList', { itemsSource: getNames(), placeholder: 'ソートするフィールドを選択してください', isRequired: false, selectedIndex: -1, textChanged: applySort }); var sortingOrderList = new wijmo.input.ComboBox('#sortingOrderList', { itemsSource: ['昇順', '降順'], textChanged: applySort }); // apply the sort function applySort() { var sds = cvSorting.sortDescriptions, fieldName = sortingFieldNameList.text, ascending = sortingOrderList.text == '昇順'; // remove old sort sds.splice(0, sds.length); // add new sort if (fieldName) { sds.push(new wijmo.collections.SortDescription(fieldName, ascending)); } }

結果(ライブ):

フィルタリング

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

この例では、国のフィルタを作成し、入力コントロールからフィルタ値を取得します。 フィルタを入力すると、グリッドが更新されてフィルタリングされたデータが表示されます。

HTML
<div class="row-fluid well"> <input id="filteringInput" class="form-control app-pad" placeholder="国名を入力してください" /> </div> <div id="filteringGrid"></div>
JS
var cvFiltering = new wijmo.collections.CollectionView(getData(20)); var filteringGrid = new wijmo.grid.FlexGrid('#filteringGrid', { isReadOnly: true, itemsSource: cvFiltering }); // apply filter when input changes var toFilter = null; document.getElementById('filteringInput').addEventListener('input', function () { if (toFilter) { clearTimeout(toFilter); } toFilter = setTimeout(function () { toFilter = null; if (cvFiltering.filter == filterFunction) { cvFiltering.refresh(); } else { cvFiltering.filter = filterFunction; } }, 500); }); // filter function for the collection view. function filterFunction(item) { var filter = filteringInput.value.toLowerCase(); return filter ? item.country.toLowerCase().indexOf(filter) > -1 : true; }

結果(ライブ):

グループ化

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

GroupDescriptionオブジェクトは柔軟性が高く、値またはグループ化関数に基づいてデータをグループ化できます。

以下の例では、リストから選択したフィールドによってコレクションをグループ化します。 グリッドには項目の内容だけでなくグループ情報(グループ名とグループ内の金額の平均値)も表示されます。 これらのレンダリングコードは、initTBodyメソッドで見つけることができます。

メモ: リストの項目を選択すると、GroupDescriptionの新しいインスタンスが追加されます。 すでに存在するGroupDescriptionを選択した場合は何も起こりません。 グループ設定をクリアするには、リストの最初の項目を選択します。

HTML
<div class="row-fluid well"> <label for="groupingFieldNameList">グループを追加</label> <div id="groupingFieldNameList"></div> <button id="btnClearGroups" class="btn btn-default">グループを初期化</button> </div> <div id="groupingGrid"></div>
JS
var cvGrouping = new wijmo.collections.CollectionView(getData(20)); var groupingGrid = new wijmo.grid.FlexGrid('#groupingGrid', { isReadOnly: true, itemsSource: cvGrouping }); // initialize the field combo and listen to changes var groupingFieldNameList = new wijmo.input.ComboBox('#groupingFieldNameList', { itemsSource: getNames(), placeholder: 'フィールドを選択', isRequired: false, selectedIndex: -1, textChanged: applyGrouping }); // clear groups document.getElementById('btnClearGroups').addEventListener('click', function () { groupingFieldNameList.text = ''; }); // apply the selected grouping function applyGrouping() { var gd = cvGrouping.groupDescriptions, fieldName = groupingFieldNameList.text; // no field? clear grouping if (!fieldName) { gd.splice(0, gd.length); return; } // add group description if not already defined if (!groupDefined(fieldName)) { if (fieldName === 'amount') { // when grouping by amount, use ranges instead of specific values gd.push(new wijmo.collections.PropertyGroupDescription(fieldName, function (item, propName) { var value = item[propName]; // amount if (value > 1000) return 'Large Amounts'; if (value > 100) return 'Medium Amounts'; if (value > 0) return 'Small Amounts'; return 'Negative Amounts'; })); } else { // group by specific property values gd.push(new wijmo.collections.PropertyGroupDescription(fieldName)); } } } // check whether the group with the specified property name already exists. function groupDefined(propName) { var gd = cvGrouping.groupDescriptions; for (var i = 0; i < gd.length; i++) { if (gd[i].propertyName === propName) { return true; } } return false; }

結果(ライブ):

編集

CollectionViewはIEditableCollectionViewインタフェースを実装しているため、編集をサポートします。

以下のサンプルは、コレクション内の特定の項目を更新、追加、削除する方法を示します。

このサンプルでは、グリッドの行を選択して[詳細の編集]ボタンをクリックし、編集を開始できます。 ポップアップダイアログで編集した後、[OK]ボタンをクリックして更新を確定します。 コレクションに新しいレコードを追加する場合は、[追加]ボタンをクリックし、ポップアップダイアログで項目の内容をカスタマイズします。 [OK]ボタンをクリックすると、追加が確定されます。 レコードを更新/追加したくない場合は、ダイアログの[キャンセル]ボタンを押してください。 行を選択して[削除]ボタンをクリックすると、選択したレコードがコレクションから削除されます。

更新、追加および削除した後、グリッドは追跡された項目配列に応じて更新されます。

HTML
<div id="editingGrid"></div> <div class="row-fluid well"> <button class="btn btn-default" id="btnEdit"> 詳細の編集... </button> <button class="btn btn-default" id="btnAdd"> 追加... </button> <button class="btn btn-default" id="btnDelete"> 削除 </button> </div> <!-- dialog for editing item details --> <div id="dlgDetail" class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close wj-hide"> × </button> <h4 class="modal-title">項目の編集</h4> </div> <div class="modal-body"> <dl class="dl-horizontal"> <dt>ID</dt> <dd> <input class="form-control" id="edtID" /> </dd> <dt>Start Date</dt> <dd> <input class="form-control" id="edtStart" /> </dd> <dt>End Start</dt> <dd> <input class="form-control" id="edtEnd" /> </dd> <dt>Country</dt> <dd> <input class="form-control" id="edtCountry" /> </dd> <dt>Product</dt> <dd> <input class="form-control" id="edtProduct" /> </dd> <dt>Color</dt> <dd> <input class="form-control" id="edtColor" /> </dd> <dt>Amount</dt> <dd> <input class="form-control" id="edtAmount" /> </dd> <dt>Active</dt> <dd> <input id="edtActive" type="checkbox" /> </dd> </dl> </div> <div class="modal-footer"> <button type="button" class="btn btn-primary wj-hide-ok"> OK </button> <button type="button" class="btn btn-warning wj-hide-cancel"> キャンセル </button> </div> </div> </div>
JS
var cvEditing = new wijmo.collections.CollectionView(getData(10), { // define newItemCreator with proper unique id newItemCreator: function () { var item = getData(1)[0]; item.id = wijmo.getAggregate(wijmo.Aggregate.Max, cvEditing.sourceCollection, 'id') + 1; return item; } }); var editingGrid = new wijmo.grid.FlexGrid('#editingGrid', { selectionMode: wijmo.grid.SelectionMode.Row, itemsSource: cvEditing }); // create dialog used to edit items var dlgDetail = new wijmo.input.Popup('#dlgDetail', { removeOnHide: false }); // start editing item document.getElementById('btnEdit').addEventListener('click', function () { var editItem = cvEditing.currentItem; cvEditing.editItem(editItem); showDialog(editItem, '項目の編集'); }); // start adding item document.getElementById('btnAdd').addEventListener('click', function () { var editedItem = cvEditing.addNew(); showDialog(editedItem, 'Add Item'); }); // delete current item document.getElementById('btnDelete').addEventListener('click', function () { cvEditing.remove(cvEditing.currentItem); }); // populate the dialog with the current item's values function showDialog(item, title) { // update dialog inputs setInputValue('edtID', item.id != null ? wijmo.Globalize.format(item.id) : ''); setInputValue('edtStart', item.start != null ? wijmo.Globalize.format(item.start) : ''); setInputValue('edtEnd', item.end != null ? wijmo.Globalize.format(item.end) : ''); setInputValue('edtCountry', item.country != null ? item.country : ''); setInputValue('edtProduct', item.product != null ? item.product : ''); setInputValue('edtColor', item.color != null ? item.color : ''); setInputValue('edtAmount', item.amount != null ? wijmo.Globalize.format(item.amount) : ''); setInputValue('edtActive', item.active); title.innerHTML = title; // show dialog dlgDetail.show(true, function (s) { if (s.dialogResult == 'wj-hide-ok') { // commit changes var item = cvEditing.currentEditItem || cvEditing.currentAddItem; if (item) { updateItem(item); } cvEditing.commitEdit(); cvEditing.commitNew(); } else { // cancel changes cvEditing.cancelEdit(); cvEditing.cancelNew(); } }); } // update item with values from the dialog function updateItem(item) { setItemValue(item, 'id', 'edtID', wijmo.DataType.Number); setItemValue(item, 'start', 'edtStart', wijmo.DataType.Date); setItemValue(item, 'end', 'edtEnd', wijmo.DataType.Date); setItemValue(item, 'country', 'edtCountry', wijmo.DataType.String); setItemValue(item, 'product', 'edtProduct', wijmo.DataType.String); setItemValue(item, 'color', 'edtColor', wijmo.DataType.String); setItemValue(item, 'amount', 'edtAmount', wijmo.DataType.Number); setItemValue(item, 'active', 'edtActive', wijmo.DataType.Boolean); } // set the value of an input element function setInputValue(id, value) { var input = document.getElementById(id); if (input.type == 'checkbox') { input.checked = value; } else { input.value = value; } } // set the value of an input element function setItemValue(item, prop, id, dataType) { var input = document.getElementById(id); item[prop] = input.type == 'checkbox' ? input.checked : wijmo.changeType(input.value, dataType) }

結果(ライブ):

ページング

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

この例では、1ページに10項目が表示されるようにCollectionViewオブジェクトが初期化されています。 テキストボックスでこの数をカスタマイズできます。 ナビゲーションボタンが用意されており、ボタンをクリックするとIPagedCollectionViewメソッドが呼び出されます。 また、pageIndexプロパティとpageCountプロパティを使用して現在のページと総ページ数を表示しています。

HTML
<div class="row-fluid well"> <label for="pagingInput"> ページサイズ </label> <input id="pagingInput"> <br/> <div id="currentPagePanel"> <label for="navigationPage"> 現在のページ </label> <div id="navigationPage" class="btn-group"> <button type="button" class="btn btn-default" id="btnFirstPage"> <span class="glyphicon glyphicon-fast-backward"></span> </button> <button type="button" class="btn btn-default" id="btnPreviousPage"> <span class="glyphicon glyphicon-step-backward"></span> </button> <button type="button" class="btn btn-default" disabled style="width:100px" id="btnCurrentPage"> </button> <button type="button" class="btn btn-default" id="btnNextPage"> <span class="glyphicon glyphicon-step-forward"></span> </button> <button type="button" class="btn btn-default" id="btnLastPage"> <span class="glyphicon glyphicon-fast-forward"></span> </button> </div> </div> </div> <div id="pagingGrid"></div>
JS
var cvPaging = new wijmo.collections.CollectionView(getData(55), { pageSize: 10, pageChanged: function () { updatePagingButtons(); } }); var pagingGrid = new wijmo.grid.FlexGrid('#pagingGrid', { isReadOnly: true, itemsSource: cvPaging }); // edit page size var pagingInput = new wijmo.input.InputNumber('#pagingInput', { min: 0, max: 20, step: 5, valueChanged: function (s, e) { cvPaging.pageSize = s.value; updatePagingButtons(); }, value: cvPaging.pageSize }); // page navigation document.getElementById('btnFirstPage').addEventListener('click', function () { cvPaging.moveToFirstPage(); });; document.getElementById('btnPreviousPage').addEventListener('click', function () { cvPaging.moveToPreviousPage(); }); document.getElementById('btnNextPage').addEventListener('click', function () { cvPaging.moveToNextPage(); });; document.getElementById('btnLastPage').addEventListener('click', function () { cvPaging.moveToLastPage(); });; // update the navigation buttons function updatePagingButtons() { // show/hide navigation bar var nav = document.getElementById('currentPagePanel'); if (cvPaging.pageSize <= 0) { nav.style.display = 'none'; return; } nav.style.display = ''; // show current page document.getElementById('btnCurrentPage').textContent = (cvPaging.pageIndex + 1) + ' / ' + cvPaging.pageCount; // first/prev var disabled = cvPaging.pageIndex == 0 ? 'disabled' : null; wijmo.setAttribute(document.getElementById('btnFirstPage'), 'disabled', disabled); wijmo.setAttribute(document.getElementById('btnPreviousPage'), 'disabled', disabled); // next/last disabled = cvPaging.pageIndex >= cvPaging.pageCount - 1 ? 'disabled' : null; wijmo.setAttribute(document.getElementById('btnNextPage'), 'disabled', disabled); wijmo.setAttribute(document.getElementById('btnLastPage'), 'disabled', disabled); }

結果(ライブ)


変更の追跡

CollectionViewクラスは、データに加えられた変更を追跡できます。 これは変更をサーバーに送信する必要がある状況で役立ちます。 変更の追跡を有効にするには、trackChangesプロパティをtrueに設定します。 そうすると、データに加えられた変更がCollectionViewによって追跡され、以下の3つの配列で公開されます。

FlexGridを使用してこの機能を実現した例を以下に示します。 グリッドがCollectionViewにバインドされており、そこでtrackChangesがtrueに設定されています。

HTML
<h5>データを変更</h5> <div id="tcMainGrid"></div> <h5>変更データを確認</h5> <h6>編集された項目:</h6> <div id="tcEditedGrid" class="tcGrid" style="height:100px"></div> <h6>追加された項目:</h6> <div id="tcAddedGrid" class="tcGrid" style="height:100px"></div> <h6>削除された項目:</h6> <div id="tcRemovedGrid" class="tcGrid" style="height:100px"></div>
JS
// create CollectionView with change tracking enabled var cvTrackingChanges = new wijmo.collections.CollectionView(getData(6), { trackChanges: true }); // create a grid to show and edit the data var tcMainGrid = new wijmo.grid.FlexGrid('#tcMainGrid', { allowAddNew: true, allowDelete: true, itemsSource: cvTrackingChanges }); // create gridS to show the changes (edits, additions, removals) var colDefs = [ { header: 'id', binding: 'id' }, { header: 'start', binding: 'start' }, { header: 'end', binding: 'end' }, { header: 'country', binding: 'country' }, { header: 'product', binding: 'product' }, { header: 'color', binding: 'color' }, { header: 'amount', binding: 'amount' }, { header: 'active', binding: 'active' } ]; var tcEditedGrid = new wijmo.grid.FlexGrid('#tcEditedGrid', { isReadOnly: true, autoGenerateColumns: false, columns: colDefs, itemsSource: cvTrackingChanges.itemsEdited }); var tcAddedGrid = new wijmo.grid.FlexGrid('#tcAddedGrid', { isReadOnly: true, autoGenerateColumns: false, columns: colDefs, itemsSource: cvTrackingChanges.itemsAdded }); var tcRemovedGrid = new wijmo.grid.FlexGrid('#tcRemovedGrid', { isReadOnly: true, autoGenerateColumns: false, columns: colDefs, itemsSource: cvTrackingChanges.itemsRemoved });

結果(ライブ):

データを変更
変更データを確認
編集された項目:
追加された項目:
削除された項目: