Getting Started
Steps for getting started with FlexGrid in JavaScript applications:
- Add references to Wijmo.
- Add markup to serve as the Wijmo control's host.
- Initialize the Wijmo control(s) via JavaScript.
- (Optional) Add some CSS to customize the FlexGrid control's appearance.
This will create a FlexGrid with default behavior, which includes
automatic column generation, column sorting and reordering, editing,
and clipboard support.
HTML
<div id="gsFlexGrid"></div>
JS
new wijmo.grid.FlexGrid('#gsFlexGrid', {
itemsSource: data.getData(100)
});
CSS
/* set default grid height and some shadow */
.wj-flexgrid {
background-color: #fff;
box-shadow: 4px 4px 10px 0 rgba(50, 50, 50, 0.75);
height: 300px;
margin-bottom: 12px;
}
Column Definitions
The Getting Started example did not define any columns, so FlexGrid generated them
automatically.
This example shows how you can define the columns using the options
parameter in the FlexGrid's constructor, or by adding items to the grid's
columns collection at any time.
Specifying the columns allows you to choose which columns to show, and in
what order.
This also gives you control over each column's width, heading,
formatting, alignment, and other properties.
In this case, we use star sizing to set the width of the "Country" column.
This tells the column to stretch to fill the available width of the grid
so there is no empty space.
On the "Revenue" column, we set the format property to "n0", which results
in numbers with thousand separators and no decimal digits.
HTML
<b>Constructor Options</b>
<div id="cdInitMethod"></div>
<b>Columns Collection</b>
<div id="cdColsCollection"></div>
JS
// initialize a grid using an 'options' object
new wijmo.grid.FlexGrid('#cdInitMethod', {
autoGenerateColumns: false,
columns: [
{ header: 'Country', binding: 'country', width: '*' },
{ header: 'Date', binding: 'date' },
{ header: 'Revenue', binding: 'amount', format: 'n0' },
{ header: 'Active', binding: 'active' },
],
itemsSource: data.getData(100)
});
// initialize a second grid by setting properties
var fgColsCollection = new wijmo.grid.FlexGrid('#cdColsCollection');
fgColsCollection.autoGenerateColumns = false;
fgColsCollection.itemsSource = data.getData(100);
// add columns one by one
var c = new wijmo.grid.Column();
c.binding = 'country';
c.header = 'Country';
c.width = '*';
fgColsCollection.columns.push(c);
c = new wijmo.grid.Column();
c.binding = 'date';
c.header = 'Date';
fgColsCollection.columns.push(c);
c = new wijmo.grid.Column();
c.binding = 'amount';
c.header = 'Revenue';
c.format = 'n0';
fgColsCollection.columns.push(c);
c = new wijmo.grid.Column();
c.binding = 'active';
c.header = 'Active';
fgColsCollection.columns.push(c);
Result (live):
Constructor Options
Columns Collection
Selection Modes
By default, FlexGrid allows you to select a range of cells with the mouse or keyboard,
just like Excel. The selectionMode property allows you to change that so that you
can select a row, a range of rows, non-contiguous rows (like in a list-box), a single cell,
or disable selection altogether.
This example allows you to pick the selectionMode from a Wijmo Menu control.
HTML
<div id="smFlexGrid"></div>
<select id="smMenu">
<option value="None">None</option>
<option value="Cell">Cell</option>
<option value="CellRange" selected>CellRange</option>
<option value="Row">Row</option>
<option value="RowRange">RowRange</option>
<option value="ListBox">ListBox</option>
</select>
JS
var smFlexGrid= new wijmo.grid.FlexGrid('#smFlexGrid', {
itemsSource: data.getData(100)
});
var smMenu = new wijmo.input.Menu('#smMenu', {
itemClicked: function (s) {
smFlexGrid.selectionMode = s.selectedValue;
udpateMenuHeader(s, 'Selection Mode');
}
});
udpateMenuHeader(smMenu, 'Selection Mode');
Cell Freezing
The FlexGrid allows you to freeze rows and columns so they remain in view as the
user scrolls the grid. Frozen cells can be edited and selected as regular cells,
exactly as in Excel.
This example allows you to toggle whether the first two rows and columns should
be frozen.
HTML
<div id="fzFlexGrid"></div>
<button id="btnFreeze" class="btn btn-default">
Unfreeze
</button>
JS
var fzFlexGrid = new wijmo.grid.FlexGrid('#fzFlexGrid', {
itemsSource: data.getData(100),
frozenRows: 2,
frozenColumns: 2
});
document.getElementById('btnFreeze').addEventListener('click', function (e) {
if (fzFlexGrid.frozenRows) {
fzFlexGrid.frozenRows = 0;
fzFlexGrid.frozenColumns = 0;
e.target.textContent = 'Freeze';
} else {
fzFlexGrid.frozenRows = 2;
fzFlexGrid.frozenColumns = 2;
e.target.textContent = 'Unfreeze';
}
});
CSS
/* frozen cells and frozen boundaries */
.wj-flexgrid .wj-cell.wj-frozen:not(.wj-header):not(.wj-group):not(.wj-state-selected):not(.wj-state-multi-selected) {
background-color: #f8ffd6;
}
.wj-flexgrid .wj-cell.wj-frozen-row {
border-bottom: 1px solid blue;
}
.wj-flexgrid .wj-cell.wj-frozen-col {
border-right: 2px solid red;
}
Editing
FlexGrid has built-in support for fast, in-cell editing like you find in Excel. There is no
need to add extra columns with Edit buttons that switch between display and edit modes.
Users can start editing by typing into any cell. This puts the cell in quick-edit mode.
In this mode, pressing a cursor key finishes the editing and moves the selection to a different cell.
Another way to start editing is by pressing F2 or by clicking a cell twice. This puts the cell in
full-edit mode. In this mode, pressing a cursor key moves the caret within the cell text.
To finish editing and move to another cell, the user must press the Enter, Tab, or Escape key.
Data is automatically coerced to the proper type when editing finishes. If the user enters invalid
data, the edit is cancelled and the original data remains in place.
You can disable editing at the grid, column, or row levels using the isReadOnly property of the
grid, column, or row objects. In this example, we make the ID column read-only.
HTML
<div id="eFlexGrid"></div>
JS
new wijmo.grid.FlexGrid('#eFlexGrid', {
autoGenerateColumns: false,
columns: [
{ header: 'ID', binding: 'id', width: '*', isReadOnly: true }, // cannot edit
{ header: 'Country', binding: 'country' },
{ header: 'Date', binding: 'date' },
{ header: 'Revenue', binding: 'amount', format: 'n0' },
{ header: 'Active', binding: 'active' },
],
itemsSource: data.getData(100)
});
Grouping
FlexGrid 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 (the default value).
GroupDescription objects are flexible, allowing you to group data
based on value or on grouping functions.
The example below groups dates by year; amounts by range returning three ranges:
over 5,000, 500 to 5,000, and under 500; other properties are grouped by value.
Use the menu to see the effect of each grouping.
Notice that the "Revenue" column displays the totals in the group rows.
We do this by setting the column's aggregate property to "Sum."
The aggregate is automatically updated when you edit the values in the column.
HTML
<div id="gFlexGrid"></div>
<select id="gMenu">
<option value="" selected>(no grouping)</option>
<option value="country">Country</option>
<option value="amount">Revenue</option>
<option value="date">Date</option>
<option value="country,date">Country and Date</option>
<option value="country,amount">Country and Revenue</option>
<option value="country,date,amount">Country, Date, and Revenue</option>
</select>
JS
var gFlexGrid = new wijmo.grid.FlexGrid('#gFlexGrid', {
autoGenerateColumns: false,
columns: [
{ header: 'Country', binding: 'country', width: '*' },
{ header: 'Date', binding: 'date' },
{ header: 'Revenue', binding: 'amount', format: 'n0' }
],
itemsSource: data.getData(100)
});
var gMenu = new wijmo.input.Menu('#gMenu', {
itemClicked: function (sender) {
// clear any current groups
var gds = gFlexGrid.collectionView.groupDescriptions;
gds.clear();
// add new group
var groupBy = sender.selectedValue;
if (groupBy) {
var groupNames = groupBy.split(',');
for (var i = 0; i < groupNames.length; i++) {
var groupName = groupNames[i];
if (groupName == 'date') { // group dates by year
var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName, function (item, prop) {
return item.date.getFullYear();
});
gds.push(groupDesc);
} else if (groupName == 'amount') { // group amounts in ranges
var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName, function (item, prop) {
return item.amount >= 5000 ? '> 5,000' : item.amount >= 500 ? '500 to 5,000' : '< 500';
});
gds.push(groupDesc);
} else { // group everything else by value
var groupDesc = new wijmo.collections.PropertyGroupDescription(groupName);
gds.push(groupDesc);
}
}
}
udpateMenuHeader(s, 'Group By');
}
});
udpateMenuHeader(gMenu, 'Group By');
Filtering
The FlexGrid 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 include in the view.
In this example, we create a filter for the country, and get the filter
value from the input control.
HTML
<div id="fFlexGrid"></div>
<div class="input-group">
<span class="input-group-addon">
<i class="glyphicon glyphicon-filter"></i>
</span>
<input id="fFilter" type="text" class="form-control" placeholder="Filter by Country..." />
</div>
JS
var fFlexGrid = new wijmo.grid.FlexGrid('#fFlexGrid', {
itemsSource: data.getData(100)
});
// apply a filter to the grid's CollectionView
var filterText = '';
fFlexGrid.collectionView.filter = function (item) {
return filterText
? item.country.toLowerCase().indexOf(filterText) > -1
: true;
}
// refresh filter when text changes
document.getElementById('fFilter').addEventListener('input', function (e) {
filterText = e.target.value.toLowerCase();
fFlexGrid.collectionView.refresh();
});
Paging
The FlexGrid 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.
We add navigation buttons, and call IPagedCollectionView methods when a button
is clicked.
We use the pageIndex and pageCount properties
to show the current page and total number of pages.
HTML
<div id="pFlexGrid" style="height:auto"></div>
<div class="btn-group" id="pPager">
<button id="btnPageFirst" class="btn btn-default">
<span class="glyphicon glyphicon-fast-backward"></span>
</button>
<button id="btnPagePrev" class="btn btn-default">
<span class="glyphicon glyphicon-step-backward"></span>
</button>
<button id="btnPageCurrent" class="btn btn-default" disabled style="width:100px"></button>
<button id="btnPageNext" class="btn btn-default">
<span class="glyphicon glyphicon-step-forward"></span>
</button>
<button id="btnPageLast" class="btn btn-default">
<span class="glyphicon glyphicon-fast-forward"></span>
</button>
</div>
JS
var pFlexGrid = new wijmo.grid.FlexGrid('#pFlexGrid', {
itemsSource: data.getData(100)
});
// set page size to 10
var cvPaged = pFlexGrid.collectionView;
cvPaged.pageSize = 10;
cvPaged.pageChanged.addHandler(function () {
updatePager(cvPaged);
});
updatePager(cvPaged);
// handle pager buttons
onClick('btnPageFirst', function () {
cvPaged.moveToFirstPage();
});
onClick('btnPagePrev', function () {
cvPaged.moveToPreviousPage();
});
onClick('btnPageNext', function () {
cvPaged.moveToNextPage();
});
onClick('btnPageLast', function () {
cvPaged.moveToLastPage();
});
// disable/enable buttons and update display text for pager
function updatePager(cv) {
var firstPage = cv.pageIndex <= 0;
disable('btnPageFirst', firstPage);
disable('btnPagePrev', firstPage);
var lastPage = cv.pageIndex >= cv.pageCount - 1;
disable('btnPageNext', lastPage);
disable('btnPageLast', lastPage);
setText('btnPageCurrent', (cv.pageIndex + 1) + ' / ' + (cv.pageCount));
}
Master-Detail
The ICollectionView interface has built-in support for
currency, which enables you to implement master-detail scenarios
with FlexGrid.
You can refer to the currentItem and use it as a binding
source for any elements on the page.
Note that you have to update the details view when the current
item changes.
To do that, attach a handler to the ICollectionView.currentChanged
event and update the details view as needed.
HTML
<div id="mdFlexGrid"></div>
<dl class="dl-horizontal">
<dt>ID</dt>
<dd id="mdCurId"></dd>
<dt>Country</dt>
<dd id="mdCurCountry"></dd>
<dt>Date</dt>
<dd id="mdCurDate"></dd>
<dt>Revenue</dt>
<dd id="mdCurRevenue"></dd>
<dt>Active</dt>
<dd id="mdCurActive"></dd>
</dl>
JS
var mdFlexGrid = new wijmo.grid.FlexGrid('#mdFlexGrid', {
selectionMode: 'Row',
itemsSource: data.getData(100)
});
// update detail pane now and when the current item changes
updateDetails();
mdFlexGrid.collectionView.currentChanged.addHandler(updateDetails);
// update the details when the CollectionView's currentItem changes
function updateDetails() {
var cv = mdFlexGrid.collectionView,
item = cv.currentItem;
setText('mdCurId', item.id);
setText('mdCurCountry', item.country);
setText('mdCurDate', wijmo.Globalize.format(item.date, 'd'));
setText('mdCurRevenue', wijmo.Globalize.format(item.amount, 'c'));
setText('mdCurActive', item.active);
}
Result (live):
- ID
- Country
- Date
- Revenue
- Active
Conditional Styling
FlexGrid has a formatItem event that gives you complete control over
the contents of the cells.
This example adds a handler to the formatItem event that changes
the color of cells in the 'amount' column to reflect the cell value.
Small numbers are shown in red, large numbers in green.
HTML
<div id="csFlexGrid"></div>
JS
new wijmo.grid.FlexGrid('#csFlexGrid', {
autoGenerateColumns: false,
columns: [
{ header: 'Country', binding: 'country', width: '*', isContentHtml: true, isReadOnly: true },
{ header: 'Date', binding: 'date' },
{ header: 'Revenue', binding: 'amount', format: 'n0' },
{ header: 'Active', binding: 'active' },
],
formatItem: function (s, e) {
// we are only interested in regular (scrollable) cells
if (e.panel == s.cells) {
// compute the cell color (for all columns, since cells may be recycled)
var color = '';
if (s.columns[e.col].binding == 'amount') {
var amount = e.panel.getCellData(e.row, e.col);
color = amount < 500 ? 'red' : amount < 2500 ? 'black' : 'green';
}
// always set the color
e.cell.style.color = color;
}
},
itemsSource: data.getData(100)
});
Themes
The appearance of the FlexGrid is defined in CSS.
In addition to the default theme, we include about a dozen professionally
designed themes that customize the appearance of all Wijmo controls to
achieve a consistent, attractive look.
You can customize the appearance of the grid using CSS. To do this,
copy CSS rules from the default theme to a new CSS file and modify
the style attributes you want to change.
In this example, we add a "custom-flex-grid" class to the grid element and
define some CSS rules to create a simple "black and white, no borders"
theme for any grids that have the "custom-flex-grid" class.
HTML
<div id="tFlexGrid" class="custom-flex-grid"></div>
JS
new wijmo.grid.FlexGrid('#tFlexGrid', {
itemsSource: data.getData(100)
});
CSS
/* 'custom-flex-grid' theme for the FlexGrid */
.custom-flex-grid .wj-header.wj-cell {
color: #fff;
background-color: #000;
border-bottom: solid 1px #404040;
border-right: solid 1px #404040;
font-weight: bold;
}
.custom-flex-grid .wj-cell {
background-color: #fff;
border: none;
}
.custom-flex-grid .wj-alt:not(.wj-state-selected):not(.wj-state-multi-selected) {
background-color: #fff;
}
.custom-flex-grid .wj-state-selected {
background: #000;
color: #fff;
}
.custom-flex-grid .wj-state-multi-selected {
background: #222;
color: #fff;
}
Trees and Hierarchical Data
In addition to grouping, FlexGrid supports hierarchical data,
that is, data with items that have lists of subitems.
This type of hierarchical structure is very common, and is
usually displayed in a tree-view control.
To use FlexGrid with hierarchical data sources, set the
childItemsPath property to the name of the data element that
contains the child elements. The grid automatically scans the data
and builds the tree for you.
HTML
<div id="tvFlexGrid" class="custom-flex-grid"></div>
JS
new wijmo.grid.FlexGrid('#tvFlexGrid', {
autoGenerateColumns: false,
columns: [
{ binding: 'name', width: '*' },
{ binding: 'length', width: 80, align: 'center' }
],
itemsSource: data.treeData, // hierarchical data
childItemsPath: 'items', // set hierarchy path
allowResizing: 'None', // disable resizing
headersVisibility: 'None', // hide headers
selectionMode: 'ListBox' // use ListBox selection
});
CSS
/* 'custom-flex-grid' theme for the FlexGrid */
.custom-flex-grid .wj-header.wj-cell {
color: #fff;
background-color: #000;
border-bottom: solid 1px #404040;
border-right: solid 1px #404040;
font-weight: bold;
}
.custom-flex-grid .wj-cell {
background-color: #fff;
border: none;
}
.custom-flex-grid .wj-alt:not(.wj-state-selected):not(.wj-state-multi-selected) {
background-color: #fff;
}
.custom-flex-grid .wj-state-selected {
background: #000;
color: #fff;
}
.custom-flex-grid .wj-state-multi-selected {
background: #222;
color: #fff;
}
Handling nulls
By default, FlexGrid allows you to enter empty values in columns of
type string, and will not allow empty/null values in columns of any
other type.
You can change this behavior using the isRequired property on
grid columns.
If you set the isRequired property to false, the grid will allow
you to enter empty values in that column, regardless of type. Conversely,
if you set the isRequired property to true, the grid will not
allow empty values even in string columns.
Setting isRequired to null reverts to the default behavior
(nulls are allowed only in string columns).
The grid below reverts the default behavior. It sets isRequired
to true for the first column and to false for all others.
You can delete content that is not required by entering an empty string or
simply by pressing the delete key.
HTML
<div id="nvGrid"></div>
JS
new wijmo.grid.FlexGrid('#nvGrid', {
autoGenerateColumns: false,
itemsSource: data.getData(100),
columns: [
{ header: 'Country', binding: 'country', width: '*', isRequired: true },
{ header: 'Date', binding: 'date', isRequired: false },
{ header: 'Revenue', binding: 'amount', format: 'n0', isRequired: false },
{ header: 'Active', binding: 'active', isRequired: false }
]
});