# CollectionView 101

This page shows how to get started with Wijmo's CollectionView class.

Wijmo has a solid infrastructure based on a powerful and familiar data layer. The main data binding interface is ICollectionView. Wijmo includes several classes that implement ICollectionView. The most basic is CollectionView, which uses regular JavaScript arrays as data sources.

The CollectionView class implements the following interfaces:

- ICollectionView: provides current record management, custom sorting, filtering, and grouping.
- IEditableCollectionView: provides methods for editing, adding, and removing items.
- IPagedCollectionView: provides paging.

The CollectionView class can keep track of changes made to the data. This feature is useful in situations where you must submit changes to the server.

## Getting Started

To use the CollectionView class, start by declaring it and passing a regular array as a data source. Then, access the view using the items property.

Here the collection will be shown in FlexGrid.

Steps for getting started with CollectionView class in applications:

1. Add references to Wijmo.
2. Add markup to serve as the FlexGrid's host.
3. Initialize the CollectionView instance and the FlexGrid instance(s) via Javascript.
4. (Optional) Add some CSS to customize the grid's appearance.

<!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; }

#### Result (live):

## Current Record Management

As implementing the interface ICollectionView, CollectionView can manage the current record.

This example shows how you can manage the current record through APIs provided by the CollectionView class.

In this case, we use the properties currentPosition to obtain the current record position in the collection. We also use the methods moveCurrentTo(item), moveCurrentToFirst(), moveCurrentToLast(), moveCurrentToNext(), moveCurrentToPosition(index) and moveCurrentToPrevious() to change the current position. When the current is changed, we use the events currentChanging and currentChanged to track it. We can cancel the current changing in the event currentChanging.

Notes: Click the "Move To Next" button to move the current to the next one. Click the "Move to Previous" to move the current to the previous on. Clicking the "Stop in 4th Row" button will cause the current is forbidden to be changed when it locates in the 4th row. Then clicking the "Clear Stopping" button will let the current be changed freely.

<div class="row-fluid well btn-group"> <button class="btn btn-default" id="btnCRMMoveNext">Move To Next</button> <button class="btn btn-default" id="btnCRMMovePre">Move To Previous</button> <button class="btn btn-default" id="btnCRMStop4">Stop in 4th Row</button> <button class="btn btn-default" id="btnCRMReset">Clear Stopping</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; } }

#### Result (live):

## Sorting

The CollectionView class supports sorting through the ICollectionView interface, which is identical to the one in .NET. To enable sorting, add one or more sortDescriptions objects to the CollectionView.sortDescriptions property. Then the sorted result can be obtained from the CollectionView.items property.

SortDescription objects are flexible, allowing you to sort data based on value in ascending or descending order. In the sample below, you can sort the collection based on the corresponding field value choosed in the first list. You can also specify the sorting order in the second list.

<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); }

#### Result (live):

## Filtering

The CollectionView class supports filtering through the ICollectionView interface, which is identical to the one in .NET. To enable filtering, set the CollectionView.filter property to a function that determines which objects to be included in the view.

In this example, we create a filter for the country, and get the filter value from the input control. When you input the filter, the grid will be refreshed and render the fitlered data.

<div class="row-fluid well"> <input id="filteringInput" type="text" class="form-control app-pad" placeholder="Please input the character you want filter by country(case-insensitive)" /> </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); }

#### Result (live):

## Grouping

The CollectionView class supports grouping through the ICollectionView interface, which is identical to the one in .NET. To enable grouping, add one or more GroupDescription objects to the CollectionView.groupDescriptions property, and ensure that the grid's showGroups property is set to true when creating the grid instance(the default value is false.).

GroupDescription objects are flexible, allowing you to group data based on value or on grouping functions.

The example below groups the collection by the field which you select from the list. The grid shows not only the items content but also the group information: the group name and the average value of amount in the group. You can find the rendering codes for these in the method initTBody. The corresponding code snippet locates in line 116.

Notes: Selecting one item in the list will add a new instance of GroupDescription. If the groupdescription already exists, nothing happens. In order to clear the group setting, select the first item in the list.

<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">Please choose the field you want to group by...</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; }

#### Result (live):

## Editing

As implementing the interface IEditableCollectionView, the CollectionView class supports editing.

This sample shows how you can update, add and remove the specified item in the collection.

In this sample, you can select the row in the grid and press the Edit Detail... button to start editing. After finishing editing in the popup dialog, press the OK button to commit your updating. If you want to add a new record to the collection, press the Add... button and customize the item content in the popup dialog. Then press the OK button to commit your adding. If you don't want to update/add the record, just press the Cancel button in the dialog. Select the row and press the Delete button will let you remove the record from the collection.

After updating, adding and removing, the grid will be refreshed according to the tracked item array.

<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"> Edit Detail... </button> <button class="btn btn-default" data-toggle="modal" data-target="#dlgDetail" id="btnAdd"> Add... </button> <button class="btn btn-default" id="btnDelete"> Delete </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">Edit Item</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"> Cancel </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 ? 'Edit Item' : '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; }

#### Result (live):

#### Edit Item

ID
Start Date
End Start
Country
Product
Color
Amount
Active

## Paging

The CollectionView class supports paging through the IPagedCollectionView interface, which is nearly identical to the one in .NET. To enable paging, set the IPagedCollectionView.pageSize property to the number of items you want on each page, and provide a UI for navigating the pages.

In this example, we use JavaScript to show 10 items per page. You can customize it in the text box. We add navigation buttons, and call IPagedCollectionView methods in the button click. Note that we use the pageIndex and pageCount properties to show the current page and total number of pages. You can customize the page size in the first text box. Let it empty or 0 to make CollectionView no paging. Then the navigation buttons will be invisible.

<div class="row-fluid well row"> <div class="col-md-5"> <input id="pagingInput" type="text" class="form-control" placeholder="0 or empty is for no paging." /> </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(); });

#### Result (live):

## Tracking changes

The CollectionView class can keep track of changes made to the data. It is useful in situations where you must submit changes to the server. To turn on change tracking, set the trackChanges property to true. Once you do that, the CollectionView keeps track of any changes made to the data and exposes them in three arrays:

- itemsEdited: This list contains items that are edited using the editItem and commitEdit methods.
- itemsAdded: This list contains items that are added using the addNew and commitNew methods.
- itemsRemoved: This list contains items that are removed using the remove method.

This feature is demonstrated below using a FlexGrid. The grid is bound to a CollectionView with trackChanges set to true.

<h5>Change the data here</h5> <div id="tcMainGrid"></div> <h5>See the changes here</h5> <h6>Items edited:</h6> <div id="tcEditedGrid" style="height:100px"></div> <h6>Items added:</h6> <div id="tcAddedGrid" style="height:100px"></div> <h6>Items removed:</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;

#### Result (live):

##### Change the data here

##### See the changes here

###### Items edited:

###### Items added:

###### Items removed: