CollectionView入門

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

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

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

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

はじめに

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

ここではコレクションはFlexGridで表示されます。

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

  1. Wijmoへの参照を追加します。
  2. FlexGridのホストとして機能するマークアップを追加します。
  3. Javascriptを使用して、CollectionViewインスタンスとFlexGridインスタンスを初期化します。
  4. (オプション)グリッドの外観をカスタマイズするCSSを追加します。
<!DOCTYPE html> <html> <head> <link rel="stylesheet" type="text/css" href="css/bootstrap.css"/> <script src="scripts/wijmo.js" type="text/javascript"></script> <script src="scripts/wijmo.grid.js" type="text/javascript"></script> </head> <body> <div id="gsGrid"></div> </body> </html>
// create collectionview, grid var cvGettingStarted = new wijmo.collections.CollectionView(getData(10)), gsGrid = new wijmo.grid.FlexGrid('#gsGrid', { itemsSource: cvGettingStarted });
/* 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; }

結果(ライブ):

現在位置の管理

CollectionViewICollectionViewインタフェースを実装しているため、現在のレコードの位置を管理できます。

この例は、CollectionViewクラスによって提供されるAPIを使用して現在位置を管理する方法を示します。

currentPositionプロパティを使用してコレクション内の現在位置を確認しています。 また、moveCurrentTo(item)moveCurrentToFirst()moveCurrentToLast()moveCurrentToNext()moveCurrentToPosition(index)およびmoveCurrentToPrevious()などのメソッドを使用して、現在位置を変更することができます。 現在位置が変更されると、currentChangingおよびcurrentChangedイベントを使用して位置を追跡することができます。 currentChangingイベントで現在位置の変更をキャンセルすることができます。

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

<div class="row-fluid well btn-group"> <button class="btn btn-default" id="btnCRMMoveNext">次へ移動</button> <button class="btn btn-default" id="btnCRMMovePre">前へ移動</button> <button class="btn btn-default" id="btnCRMStop4">4行目で停止</button> <button class="btn btn-default" id="btnCRMReset">停止の解除</button> </div> <div id="crmGrid"></div>
// create collectionview, grid var cvCRM = new wijmo.collections.CollectionView(getData(10)), crmGrid = new wijmo.grid.FlexGrid('#crmGrid'); // initialize grid crmGrid.initialize({ isReadOnly: true, selectionMode: wijmo.grid.SelectionMode.Row, itemsSource: cvCRM }); // Add the processes for buttons' click // move the current to the next one document.getElementById('btnCRMMoveNext').addEventListener('click', function () { cvCRM.moveCurrentToNext(); }); // move the current to the preivous one document.getElementById('btnCRMMovePre').addEventListener('click', function () { cvCRM.moveCurrentToPrevious(); }); // when the current item is the 4th one, forbid changing current. document.getElementById('btnCRMStop4').addEventListener('click', function () { cvCRM.currentChanging.addHandler(stopCurrentIn4th); }); // restore to be able to change current. document.getElementById('btnCRMReset').addEventListener('click', function () { cvCRM.currentChanging.removeHandler(stopCurrentIn4th); }); // define the funciton to forbid the current moving. function stopCurrentIn4th(sender, e) { // when the current is the 4rd item, stop moving. if (sender.currentPosition === 3) { e.cancel = true; } }

結果(ライブ):

ソート

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

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

<div class="row-fluid well row"> <div class="col-md-8"> <select id="sortingFieldNameList" class="form-control"> </select> </div> <div class="col-md-4"> <select id="sortingOrderList" class="form-control"> <option value="true" selected="selected">Ascending</option> <option value="false">Descending</option> </select> </div> </div> <div id="sortingGrid"></div>
// create collectionview, grid, the jQuery elements, the field name list. var cvSorting = new wijmo.collections.CollectionView(getData(10)), sortingGrid = new wijmo.grid.FlexGrid('#sortingGrid'), sortingFieldNameList = document.getElementById('sortingFieldNameList'), sortingOrderList = document.getElementById('sortingOrderList'), sortingNames = getNames(); // initialize grid sortingGrid.initialize({ isReadOnly: true, allowSorting: false, itemsSource: cvSorting }); // initialize the list items for field names and orders. sortingFieldNameList.innerHTML += '<option value="" selected="selected">Please choose the field you want to sort by...</option>'; for (var i = 0; i < sortingNames.length; i++) { sortingFieldNameList.innerHTML += '<option value="' + sortingNames[i] + '">' + sortingNames[i] + '</option>'; } // track the list change in order to udpate the sortDescriptions property. sortingFieldNameList.addEventListener('change', sortGrid); sortingOrderList.addEventListener('change', sortGrid); function sortGrid() { var fieldName = sortingFieldNameList.value, ascending = sortingOrderList.value, sd, sdNew; if (!fieldName) { return; } ascending = ascending === 'true'; sd = cvSorting.sortDescriptions; sdNew = new wijmo.collections.SortDescription(fieldName, ascending); // remove any old sort descriptors and add the new one sd.splice(0, sd.length, sdNew); }

結果(ライブ):

フィルタリング

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

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

<div class="row-fluid well"> <input id="filteringInput" type="text" class="form-control app-pad" placeholder="国名を入力してください" /> </div> <div id="fileringGrid"></div>
// create collectionview, grid, filter with timeout, textbox for inputting filter. var cvFiltering = new wijmo.collections.CollectionView(getData(20)), filteringGrid = new wijmo.grid.FlexGrid('#filteringGrid'), toFilter, filteringInput = document.getElementById('filteringInput'); // initialize grid filteringGrid.initialize({ isReadOnly: true, itemsSource: cvFiltering }); // apply filter when input filteringInput.addEventListener('input', filterGrid); // define the filter function for the collection view. function filterFunction(item) { var filter = filteringInput.value.toLowerCase(); if (!filter) { return true; } return item.country.toLowerCase().indexOf(filter) > -1; } // apply filter (applied on a 500 ms timeOut) function filterGrid() { if (toFilter) { clearTimeout(toFilter); } toFilter = setTimeout(function () { toFilter = null; if (cvFiltering.filter === filterFunction) { cvFiltering.refresh(); } else { cvFiltering.filter = filterFunction; } }, 500); }

結果(ライブ):

グループ化

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

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

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

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

<div class="row-fluid well"> <select id="groupingFieldNameList" class="form-control"></select> </div> <div id="groupingGrid"></div>
// create collectionview, grid, the select element and the names list. var cvGrouping = new wijmo.collections.CollectionView(getData(20)), groupingGrid = new wijmo.grid.FlexGrid('#groupingGrid'), groupingFieldNameList = document.getElementById('groupingFieldNameList'), groupingNames = getNames(); // initialize grid groupingGrid.initialize({ isReadOnly: true, itemsSource: cvGrouping }); // initialize the list and listen to the list's change. groupingFieldNameList.innerHTML += '<option value="" selected="selected">グループ化するフィールドを選択してください</option>'; for (var i = 0; i < groupingNames.length; i++) { groupingFieldNameList.innerHTML += '<option value="' + groupingNames[i] + '">' + groupingNames[i] + '</option>'; } groupingFieldNameList.addEventListener('change', groupGrid); // update the group settings. function groupGrid() { var gd, fieldName = groupingFieldNameList.value; gd = cvGrouping.groupDescriptions; if (!fieldName) { // clear all the group settings. gd.splice(0, gd.length); return; } if (findGroup(fieldName) >= 0) { return; } 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 findGroup(propName) { var gd = cvGrouping.groupDescriptions; for (var i = 0; i < gd.length; i++) { if (gd[i].propertyName === propName) { return i; } } return -1; }

結果(ライブ):

編集

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

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

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

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

<div id="editingGrid"></div> <!-- commands --> <div class="row-fluid well grid-sort-group"> <!-- edit details in a popup --> <button class="btn btn-default" data-toggle="modal" data-target="#dlgDetail" id="btnEdit"> 詳細の編集... </button> <button class="btn btn-default" data-toggle="modal" data-target="#dlgDetail" id="btnAdd"> 追加... </button> <button class="btn btn-default" id="btnDelete"> 削除 </button> </div> <!-- a dialog for editing item details --> <div class="modal fade" id="dlgDetail"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true"> &times; </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" type="text" /> </dd> <dt>Start Date</dt> <dd> <input class="form-control" id="edtStart" type="text" /> </dd> <dt>End Start</dt> <dd> <input class="form-control" id="edtEnd" type="text" /> </dd> <dt>Country</dt> <dd> <input class="form-control" id="edtCountry" type="text" /> </dd> <dt>Product</dt> <dd> <input class="form-control" id="edtProduct" type="text" /> </dd> <dt>Color</dt> <dd> <input class="form-control" id="edtColor" type="text" /> </dd> <dt>Amount</dt> <dd> <input class="form-control" id="edtAmount" type="text" /> </dd> <dt>Active</dt> <dd> <input class="form-control" id="edtActive" type="checkbox" /> </dd> </dl> </div> <div class="modal-footer"> <button type="button" class="btn btn-primary" data-dismiss="modal" id="btnCRUDOK"> OK </button> <button type="button" class="btn btn-warning" data-dismiss="modal" id="btnCRUDCancel"> キャンセル </button> </div> </div> </div> </div>
// create collectionview, grid // create collectionview, grid var cvEditing = new wijmo.collections.CollectionView(getData(10)), editingGrid = new wijmo.grid.FlexGrid('#editingGrid'); // initialize grid editingGrid.initialize({ selectionMode: wijmo.grid.SelectionMode.Row, itemsSource: cvEditing }); // track the collection changes so that updating the grid. cvEditing.trackChanges = true; // define the new item value. cvEditing.newItemCreator = function () { var item = getData(1)[0]; // aggregate the max value of id in the collection. item.id = wijmo.getAggregate(wijmo.Aggregate.Max, cvEditing.sourceCollection, 'id') + 1; return item; }; // Add the processes for buttons' click document.getElementById('btnEdit').addEventListener('click', function () { cvEditing.editItem(cvEditing.currentItem); // update the content in the dialog with the current edited item. updateDialog(cvEditing.currentEditItem, true); }); document.getElementById('btnAdd').addEventListener('click', function () { var newItem = cvEditing.addNew(); // update the content in the dialog with the current added item updateDialog(newItem, false); }); document.getElementById('btnDelete').addEventListener('click', function () { var position = cvEditing.currentPosition; cvEditing.remove(cvEditing.currentItem); }); // commit editing or adding document.getElementById('btnCRUDOK').addEventListener('click', function () { // update the editing/adding item with the returned data from dialog. var updatedItem = getUpdatedData(), cItem = cvEditing.currentEditItem, names = getNames(); if (!cItem) { cItem = cvEditing.currentAddItem; } if (!cItem) { return; } for (var i = 0; i < names.length; i++) { var fName = names[i]; cItem[fName] = updatedItem[fName]; } // commit editing/adding cvEditing.commitEdit(); cvEditing.commitNew(); }); // cancel editing or adding document.getElementById('btnCRUDCancel').addEventListener('click', function () { cvEditing.cancelEdit(); cvEditing.cancelNew(); }); // fill the dialog with the item. function updateDialog(item, isEdit) { document.getElementById('edtID').value = item.id !== null && typeof (item.id) != 'undefined' ? wijmo.Globalize.format(item.id) : ''; document.getElementById('edtStart').value = item.start ? wijmo.Globalize.format(item.start) : ''; document.getElementById('edtEnd').value = item.end ? wijmo.Globalize.format(item.end) : ''; document.getElementById('edtCountry').value = item.country ? item.country : ''; document.getElementById('edtProduct').value = item.product ? item.product : ''; document.getElementById('edtColor').value = item.color ? item.color : ''; document.getElementById('edtAmount').value = item.amount !== null && typeof item.amount != 'undefined' ? wijmo.Globalize.format(item.amount) : ''; document.getElementById('edtActive').checked = item.active; var title = document.getElementById('dlgDetail').querySelector('div.modal-header h4.modal-title'); title.innerHTML = isEdit ? '項目の編集' : 'Add Item'; } // get the content from the dialog function getUpdatedData() { var item = {}, content = document.getElementById('edtID').value; if (content) { item.id = wijmo.Globalize.parseInt(content); } content = document.getElementById('edtStart').value; if (content) { item.start = wijmo.Globalize.parseDate(content); } content = document.getElementById('edtEnd').value; if (content) { item.end = wijmo.Globalize.parseDate(content); } item.country = document.getElementById('edtCountry').value; item.product = document.getElementById('edtProduct').value; item.color = document.getElementById('edtColor').value; content = document.getElementById('edtAmount').value; if (content) { item.amount = wijmo.Globalize.parseFloat(content); } item.active = document.getElementById('edtActive').checked; return item; }

結果(ライブ):

ページング

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

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

<div class="row-fluid well row"> <div class="col-md-5"> <input id="pagingInput" type="text" class="form-control" placeholder="0または空白はページングなし" /> </div> <div class="btn-group col-md-7" id="naviagtionPage"> <button type="button" class="btn btn-default" id="btnMoveToFirstPage"> <span class="glyphicon glyphicon-fast-backward"></span> </button> <button type="button" class="btn btn-default" id="btnMoveToPreviousPage"> <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="btnMoveToNextPage"> <span class="glyphicon glyphicon-step-forward"></span> </button> <button type="button" class="btn btn-default" id="btnMoveToLastPage"> <span class="glyphicon glyphicon-fast-forward"></span> </button> </div> </div> <div id="pagingGrid"></div>
// create collectionview, grid, the navigation buttons' elements var cvPaging = new wijmo.collections.CollectionView(getData(55)), pagingGrid = new wijmo.grid.FlexGrid('#pagingGrid'), btnFirstPage = document.getElementById('btnMoveToFirstPage'), btnPreviousPage = document.getElementById('btnMoveToPreviousPage'), btnNextPage = document.getElementById('btnMoveToNextPage'), btnLastPage = document.getElementById('btnMoveToLastPage'), btnCurrentPage = document.getElementById('btnCurrentPage'); // initialize grid pagingGrid.initialize({ isReadOnly: true, itemsSource: cvPaging }); // initialize the page size with 10. cvPaging.pageSize = 10; // initialize the input value. document.getElementById('pagingInput').value = cvPaging.pageSize; // init the button status. updateNaviagteButtons(); // update the collectionview's pagesize according to the user's input. document.getElementById('pagingInput').addEventListener('blur', function () { var pagesize = this.value; if (!pagesize) { pagesize = 0; } else { pagesize = wijmo.Globalize.parseInt(pagesize); } cvPaging.pageSize = pagesize; updateNaviagteButtons(); }); // update the navigation buttons' status function updateNaviagteButtons() { if (cvPaging.pageSize <= 0) { document.getElementById('naviagtionPage').style.display = 'none'; return; } document.getElementById('naviagtionPage').style.display = 'block'; if (cvPaging.pageIndex === 0) { btnFirstPage.setAttribute('disabled', 'disabled'); btnPreviousPage.setAttribute('disabled', 'disabled'); btnNextPage.removeAttribute('disabled'); btnLastPage.removeAttribute('disabled'); } else if (cvPaging.pageIndex === (cvPaging.pageCount - 1)) { btnFirstPage.removeAttribute('disabled'); btnPreviousPage.removeAttribute('disabled'); btnLastPage.setAttribute('disabled', 'disabled'); btnNextPage.setAttribute('disabled', 'disabled'); } else { btnFirstPage.removeAttribute('disabled'); btnPreviousPage.removeAttribute('disabled'); btnNextPage.removeAttribute('disabled'); btnLastPage.removeAttribute('disabled'); } btnCurrentPage.innerHTML = (cvPaging.pageIndex + 1) + ' / ' + cvPaging.pageCount; } // commands: moving page. btnFirstPage.addEventListener('click', function () { // move to the first page. cvPaging.moveToFirstPage(); updateNaviagteButtons(); }); btnPreviousPage.addEventListener('click', function () { // move to the previous page. cvPaging.moveToPreviousPage(); updateNaviagteButtons(); }); btnNextPage.addEventListener('click', function () { // move to the next page. cvPaging.moveToNextPage(); updateNaviagteButtons(); }); btnLastPage.addEventListener('click', function () { // move to the last page. cvPaging.moveToLastPage(); updateNaviagteButtons(); });

結果(ライブ):

変更の追跡

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

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

<h5>データを変更</h5> <div id="tcMainGrid"></div> <h5>変更データを確認</h5> <h6>編集された項目:</h6> <div id="tcEditedGrid" style="height:100px"></div> <h6>追加された項目:</h6> <div id="tcAddedGrid" style="height:100px"></div> <h6>削除された項目:</h6> <div id="tcRemovedGrid" style="height:100px"></div>
// create collectionview, grids, the grid column layout var cvTrackingChanges = new wijmo.collections.CollectionView(getData(6)), tcMainGrid = new wijmo.grid.FlexGrid('#tcMainGrid'), // the flexGrid to edit the data tcEditedGrid = new wijmo.grid.FlexGrid('#tcEditedGrid'), // the flexGrid to record the edited items tcAddedGrid = new wijmo.grid.FlexGrid('#tcAddedGrid'), // the flexGrid to record the added items tcRemovedGrid = new wijmo.grid.FlexGrid('#tcRemovedGrid'), // the flexGrid to record the removed items columnsDefinition = [ { 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' } ]; // initialize the grids tcMainGrid.initialize({ allowAddNew: true, allowDelete: true, itemsSource: cvTrackingChanges }); tcEditedGrid.initialize({ isReadOnly: true, autoGenerateColumns: false, columns: columnsDefinition, itemsSource: cvTrackingChanges.itemsEdited }); tcAddedGrid.initialize({ isReadOnly: true, autoGenerateColumns: false, columns: columnsDefinition, itemsSource: cvTrackingChanges.itemsAdded }); tcRemovedGrid.initialize({ isReadOnly: true, autoGenerateColumns: false, columns: columnsDefinition, itemsSource: cvTrackingChanges.itemsRemoved }); // track changes of the collectionview cvTrackingChanges.trackChanges = true;

結果(ライブ):

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