Working with the Grid
In this tutorial, you'll learn about the Grid's event system, how to select rows, and how to set up your grid for editing.
dgrid
This tutorial covers the
dojox/grid/DataGrid
in depth. Beginning with Dojo 1.7, you should use the dgrid, a next-generation grid component that takes full advantage of modern browsers and object stores. Visit the dgrid site for a collection of dgrid tutorials.
Getting Started
We've learned about the Data Grid and how
to populate a Grid with data. Now
we'll introduce three major concepts to be used when working with the dojox/grid/DataGrid
:
how the Grid's event system works, how the Grid's selections work, and how to set up your Grid
so that you can edit data.
The Grid's Event System
The Grid supports quite a few events—see the API reference
for a full list—but for this tutorial, we are going to focus on the most common event handler
used by developers: onRowClick
. onRowClick
is used to
detect clicks on rows (duh!), but the way in which it passes information about the event
is special: instead of passing specific arguments like many of the events in the
Dijit ecosphere, it augments standard DOM event objects with some custom information.
The decorated event object
When events are fired, the Grid decorates the DOM event object passed to event handlers by the browser. It does this by attaching the following properties to the event object:
grid
: A reference to the grid in which the event was firedcell
: A reference to the specific cell in the grid from which the event was firedrowIndex
: A reference to the index of the row in which the event was fired
All of these custom properties are accessible from the event object that is passed to your connected
handler. Say, for example, we'd like to get a reference to the underlying dojo/data
item
that a row represents when it is clicked. Remember that the name of event is the event handler without the
"on" prefix, and so when using the new on() event handler, we use the event name of "RowClick" (and widgets
allow for all lower case, so we could use "rowclick"), therefore we would do something like this:
// assuming our grid is stored in a variable called "myGrid":
myGrid.on("RowClick", function(evt){
var idx = evt.rowIndex,
rowData = grid.getItem(idx);
// The rowData is returned in an object, last is the last name, first is the first name
document.getElementById("results").innerHTML =
"You have clicked on " + rowData.last + ", " + rowData.first + ".";
}, true);
We focus on rows instead of cells for events because a Grid is a representation (or view) of a collection of data items. Since each row represents a single item, it is more logical and efficient to work with full rows instead of attempting to handle individual cells.
Don't forget: when working with a Grid, you should be doing any kind of data operations in the underlying data store and not with the DOM structure of the Grid directly.
Other events
The Grid supports a large set of basic events on both rows and cells, which you can use to create customizations (such as visual alterations) if needed. They are as follows:
Row | HeaderCell | Cell |
---|---|---|
onRowClick | onHeaderCellClick | onCellClick |
onRowContextMenu | onHeaderCellContextMenu | onCellContextMenu |
onRowDblClick | onHeaderCellDblClick | onCellDblClick |
onRowFocus | onHeaderCellFocus | onCellFocus |
onRowMouseDown | onHeaderCellMouseDown | onCellMouseDown |
onRowMouseOut | onHeaderCellMouseOut | onCellMouseOut |
onRowMouseOver | onHeaderCellMouseOver | onCellMouseOver |
Each of these events are passed the same decorated DOM event object as we saw in
our onRowClick
example.
The Grid also supports a number of other events, including notifications when a cell is being edited, and when rows/items are selected. See the API reference for more details.
Row selections with the Grid
The Grid supports the notion of row-based selections, providing several
options for selection behavior. The desired behavior can be specified when
creating the Grid using the selectionMode
property:
require(["dojox/grid/DataGrid"], function(DataGrid){
var myGrid = new DataGrid({
store: myStore,
structure: myStructure,
selectionMode: "single"
}, "someNode");
myGrid.startup();
The possible values for selectionMode
are:
none
: No selections are allowedsingle
: Only one row may be selected at a time; clicking a second row will remove the selection from the firstmultiple
: Each click toggles the selection of the row in questionextended
: The default mode; normal clicks operate likesingle
, but multiple rows / ranges of rows may be selected by holding modifier keys (e.g. Ctrl and Shift on Windows) while clicking
Row selectors
In addition to selecting rows by clicking directly on data cells, the Grid also provides a few options allowing selection via a dedicated area of each row.
The most basic of these is exposed via the Grid's rowSelector
property.
This can be set to a CSS-compatible width measurement (e.g. "20px"
),
or simply to true
which will use a default width.
The Grid also provides options for selection via checkboxes, or—in the
case of the single
row selection mode—radio buttons.
These capabilities are exposed via the semi-private custom view types
dojox/grid/_CheckBoxSelector
and dojox/grid/_RadioSelector
.
You would include one of these views in your grid structure as follows:
require(["dojox/grid/cells", "dojox/grid/_CheckBoxSelector"], function(gridCells){
var myStructure = [
// First, a view using the _CheckBoxSelector custom type.
// Don't forget to require dojox/grid/_CheckBoxSelector
{
type: "dojox.grid._CheckBoxSelector"
},
// Now include the data cells in a view occupying the rest of the grid.
[
[
new gridCells.RowIndex({ width: "10%" }),
{ name: "Column 1", field: "col1", width: "30%" },
{ name: "Column 2", field: "col2", width: "30%" },
{ name: "Column 3", field: "col3", width: "30%" }
],[
{ name: "Column 4", field: "col4", colSpan: 4 }
]
]
];
You may have noticed we snuck an interesting cell type into this structure:
gridCells.RowIndex
. This cell simply displays the index of each row in the grid. It is not at all required in order to take advantage of the Grid's selection capabilities, but it may be useful particularly during the process of prototyping a grid structure.
To see these selector features in action, check out the selector demo below.
Getting selections from the Grid
The ability to select data is pointless unless you have some way of retrieving the
current selection. The Grid handles this through the selection
property,
and three event handlers—onSelected
, onDeselected
,
and onSelectionChanged
.
When listening to the onSelected
or onDeselected
handlers, you
will receive the index of the row that has been selected or deselected, respectively.
Additionally, you can use the Grid's selection
property to
retrieve the items represented by the selected rows and operate on them, like so:
require(["dojo/_base/array", "dojo/_base/lang"], function(baseArray, lang){
function reportSelection(node){
var items = this.selection.getSelected();
var tmp = baseArray.map(items, function(item){
return item.id;
}, this);
var msg = "You have selected row" + ((tmp.length > 1) ? "s ": " ");
node.innerHTML = msg + tmp.join(", ");
}
// assuming our grid is stored in a variable called "myGrid":
myGrid.on("SelectionChanged",
lang.hitch(grid, reportSelection, document.getElementById("results")), true);
The return from
selection.getSelected()
is always an array, regardless of the selection mode used; i.e. if you set up your Grid to only allow one selection at a time,getSelected
will still return an array, with a single item in it.
For more information about the Grid's selection
object, take a look at
the Selection object in the API reference, where you'll
find that you have full programmatic access to grid selection operations.
Now that we have learned how selections work with a Grid, let's take a look at a major piece of functionality: editing data with a Grid.
Editing data with the Grid
Like a typical relational database GUI (Graphical User Interface) or a spreadsheet program, the
Grid can also allow you to edit data at the field level. To do this, you have to designate
whether a field is editable in your structure definition, and specify what type of
editing you want to enable through the use of the type
property in each
column definition, like so:
require(["dojox/grid/DataGrid", "dojox/grid/cells", "dojox/grid/cells/dijit",
"dojo/date/locale", "dojo/currency", "dijit/form/DateTextBox", "dijit/form/CurrencyTextBox",
"dijit/form/HorizontalSlider", "dojo/domReady!"
], function(DataGrid, cells, cellsDijit, locale, currency, DateTextBox, CurrencyTextBox, HorizontalSlider){
function formatCurrency(inDatum){
return isNaN(inDatum) ? '...' : currency.format(inDatum, this.constraint);
}
function formatDate(inDatum){
return locale.format(new Date(inDatum), this.constraint);
}
gridLayout = [{
defaultCell: { width: 8, editable: true, type: cells._Widget, styles: 'text-align: right;' },
cells: [
{ name: 'Id', field: 'id', editable: false, width: 2 /* Can't edit ID's of dojo/store items */ },
{ name: 'Date', field: 'col8', width: 10, editable: true,
widgetClass: DateTextBox,
formatter: formatDate,
constraint: {formatLength: 'long', selector: "date"}},
{ name: 'Priority', styles: 'text-align: center;', field: 'col1', width: 10,
type: cells.ComboBox,
options: ["normal","note","important"]},
{ name: 'Mark', field: 'col2', width: 5, styles: 'text-align: center;',
type: cells.CheckBox},
{ name: 'Status', field: 'col3',
styles: 'text-align: center;',
type: cells.Select,
options: ["new", "read", "replied"] },
{ name: 'Message', field: 'col4', styles: '', width: 10 },
{ name: 'Amount', field: 'col5', formatter: formatCurrency, constraint: {currency: 'EUR'},
widgetClass: CurrencyTextBox, width: "auto" },
{ name: 'Amount', field: 'col5', formatter: formatCurrency, constraint: {currency: 'EUR'},
widgetClass: HorizontalSlider, width: 10}
]
}];
Note that when defining a Grid's structure declaratively, cell editor type is specified in the
th
via thecellType
attribute, nottype
.
By default, if you specify a column to be editable but don't specify a widget constructor, you will get a plain text box. This is often adequate; however, you might find that you'll need to limit the options of entry, deal with dates, or have some other special needs—like in the example above.
We set up our example structure with a defaultCell
definition,
which sets a baseline of properties to be applied to all cells in our structure,
unless explicitly overridden on a per-cell basis. In this case, we specify that
we want cells to be editable unless otherwise noted, and set the type to a basic
custom editor widget called dojox/grid/cells._Widget
, from which
all the custom grid-based editing widgets derive. We then customize each
column definition to specify which kind of editing widget we'd really like to
use in each case.
As you can see, the Grid itself provides a number of "special" widgets for you.
In our example, we can see dojox/grid/cells/DateTextBox
,
dojox/grid/cells/ComboBox
, dojox/grid/cells/Select
,
dojox/grid/cells/Editor
, and dojox/grid/cells/CheckBox
;
we can also see the use of actual Dijits, such as
dijit/form/CurrencyTextBox
and dijit/form/HorizontalSlider
.
Using each of these widgets may also require additional properties to be defined;
for example, the ComboBox and Select widgets require an additional property
called options
.
These widgets for editing on a cell-level (all defined within the
dojox/grid/cells
namespace) have been defined because of the special HTML needs of the Grid. When in doubt about what kind of widget to use for editing, look first to see if the widget in question has been implemented underdojox/grid/cells
before trying to use the Dijit equivalent. This is particularly true of any kind of Dijit that defines/uses a popup of some sort.
Because of the data binding that occurs between Dijit-based form widgets and
dojo/store
via the Grid, this should be all you need to enable
editing for a data set.
Another friendly reminder: you will need to use a write-enabled data store in order to do any kind of editing within a Grid. Depending on the store, you may also have to do a periodic save() in order to capture any edits within a Grid; this is entirely dependent on the store of choice.
Conclusion
In this tutorial, we've built upon previous topics by introducing additional features of the Grid. The Grid exposes many events to which custom logic can be applied. Several row selection modes are available; the Grid provides APIs for determining selected rows and being notified when a selection occurs. Using the Grid's powerful editing capabilities, it is possible to modify the data set from within the Grid itself.
Armed with this knowledge, you should be well on your way towards creating an application capable of displaying and manipulating information in complex data sets.