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 important 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.
In this example, we used a FlexGrid control to show the collection.
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.
You can use the CollectionView class to manage the current record.
This example shows how you can use the API provided in the CollectionView
class to change the current record by clicking a row in the grid or by
clicking buttons.
We use the currentPosition property to get the position of the current
record in the collection, and use the following methods to change the
current position:
moveCurrentTo(item)
moveCurrentToFirst()
moveCurrentToLast()
moveCurrentToNext()
moveCurrentToPosition(index)
moveCurrentToPrevious()
When the position changes, we use the currentChanging and currentChanged events
to track it. We can cancel the change of position in the currentChanging event.
Click the Next button to set the next record as the current one.
Click the Previous button to set the previous record as the current one.
Click the Stop at 4th Row button to prevent the current record from being
changed once it reaches the 4th row.
Click the Clear button to remove the stop and allow the current records
to be changed freely.
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;
}
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 chosen in the first list. You can also specify the sorting order
in the second list.
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: 'Choose Field to Sort On',
isRequired: false,
selectedIndex: -1,
textChanged: applySort
});
var sortingOrderList = new wijmo.input.ComboBox('#sortingOrderList', {
itemsSource: ['Ascending', 'Descending'],
textChanged: applySort
});
// apply the sort
function applySort() {
var sds = cvSorting.sortDescriptions,
fieldName = sortingFieldNameList.text,
ascending = sortingOrderList.text == 'Ascending';
// remove old sort
sds.splice(0, sds.length);
// add new sort
if (fieldName) {
sds.push(new wijmo.collections.SortDescription(fieldName, ascending));
}
}
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 should
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 change the filter text, the grid is updated to show
the filtered data.
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;
}
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.
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: 'Select field',
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;
}
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.
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, 'Edit Item');
});
// 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)
}
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.
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);
}
Result (live)
Change Tracking
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.