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:
Add references to Wijmo.
Add markup to serve as the FlexGrid's host.
Initialize the CollectionView instance and the FlexGrid instance(s) via Javascript.
(Optional) Add some CSS to customize the grid's appearance.
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.
// 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.
// 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.
// 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.
// 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 beginEdit 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.