Table
Summary
The grid widget provides a scrollable table with sortable columns connected to a dojo.data Store object. It downloads data as needed from the data store, which typically pulls the data from the server in a JSON format. It leverages the store's functionality for sorting, filtering and paging. The table's outer size can be set, so that it fits into a swing-like application with a predetermined size for each section.
Some of the decisions below are based on various email conversations including this thread.
Model
In as much as possible, the Grid is modeled after a native html <table>.- variable height rows
- vertical but not horizontal scrolling (by default)
- data not inlined; it comes from a dojo.data.Store object
- height of table can be constrained, so that a scrollbar shows up
Basic Features
- lazy load via dojo.data interface
- vertical scrolling (the header of the table stays stationary but the inner section moves)
- column sorting by clicking on the column
- column formatting / expressions
- designer specifies width and height of outer dom node, and the relative (initial) width of each column
- resizeTo() method : dynamically change size of table (this may trigger pulling more data from the dojo.data store)
- cells can contain simple text strings, or complex HTML (like images)
- fast, even for large amounts of data (especially startup time)
- different classnames for alternate rows, to allow CSS for zebra striping; classname changes when hovering over a row
Advanced Features
These features are not required for the 0.9 release but will be added eventually (either in 0.9 or afterwards).- no height specified; table sizes to natural height needed to fit all the data
- column resizing (see below)
- editable data (edits reflected back over dojo.data)
- delete rows - TODO: how?
- add rows - TODO: how?
- row selection - each row has a checkbox; clicking it selects the row. TODO: how is the info about selected rows communicated?
Non-features
These features are explicitly not supported.- data inlined into the HTML markup as <tr>...</tr>. (Note though that you could inline JSON data into the web page using dojo.data.JsonItemStore. It would also be easy to write a dojo.data.HtmlTableStore that could read data from an HTML table on the page, but that's not part of this spec)
- filtering (if the user wants to filter they can do a new query on the data store and pass the new result set to the Grid)
- tree-table: nested rows
- specifying custom css class names. standard names are used (see dijit doc for details on theming)
- selecting a certain row and then sorting by a given column results in sorted table, put you are positioned at the same row as before
- embedded widgets in data
- horizontal scrolling
- drag and drop
Column resizing
Each column label has a bar after the column label. (It might be perceived as the separator between columns but the last column has it too.) You can adjust the size of a column by dragging the bar to the left of the right. Doing so does not change the width of the other columns. The columns on the right may get pushed out of the table's viewport. Adjusting column size may have an effect on the height of each row.Scroll Bar
Since the rows have varying sizes, scrolling is complicated. But it should work this way:
The scrollbar's position is relative to the current row #, vs. the total # of rows. If you are on row 75 out of 100 rows, the scrollbar button will be 3/4 of the way down.
The scrollbar button's size can be anything.
The scrollbar must have up/down arrows.
Implementation-wise, this means that we can't have a browser-native scrollbar isn't attached directly to the div holding the data. Probably should have a browser-native scrollbar connected to a dummy div that is hidden behind the div showing the data. Could also use a slider or other custom control for the scrollbar. If we do make a fake scrollbar, then you should use Typematic.js hooked up to the buttons (it's already hooked up for keyboard handling, see the A11Y section below).
API
Programmatic creation
var store1 = new dojox.data.RemoteJsonData({documentUrl: "customers.php"});
new dijit.Grid(
{
data: store1,
filter: "date > 2007-1-1",
columns:
[
{ attribute: "name", label: "Name", size: 30},
{ attribute: "dateAdded", label: "Date Added", size: 20 }, ...
]
}, srcNode);
Markup creation
<table dojoType="dijit.Grid" data="store1" style="height: 500px; width: 300px;"> <thead> <tr> <th attribute="Name" dataType="String">Name</th> <th field="DateAdded" dataType="Date" align="center">Date Added</th> <th field="NumAccounts" dataType="Number" sort="desc" align="center"># of accounts</th> <th field="Description" dataType="html">Description</th> </tr> </thead> </table>
Parameters to widget
- store: dojo.data.Store The underlying Store for all data represented by the widget.
- columns: Array. see above
- query: object
user specified query as per dojo.data standard query format, like {abbr: "ec"} - sort: array
user specified sort as per dojo.data standard sort format, like [ {attribute: "uniqueId", descending: true} ] - id: String The name of the attribute used as a unique key for each row
- editable: Boolean
If the widget rows can be edited. - selection: String
If rows can be selected. "none", "single", "multiple"
Store parameter
The user will call:
var store1 = new dojo.data.JsonItemStore({url: "customers.php"});
or something similar to create the store, and then pass that argument when creating the grid widget.
The Grid is designed to be able to access remote data (hence the paging interface to dojo.data), but the data doesn't necessarily need to be remote. In other words, the Grid accesses it's data via the dojo.data interface and doesn't care where it is. For small datasets, sometimes people will want to embed the data locally in a web page so that there's no server dependency, and for performance reasons.
This can be done with JsonItemStore. The interface looks like this:
var itemData = {items: [{name: "Bill", hiredate: new Date("2006-8-1")}, ...]};
var store = new dojo.data.JsonItemStore({ data: itemData});
Column specification:
For each column we specify the following:
- data source is either one of these:
- attribute: name of the attribute in the data store
- expression: see "computed columns" below
- type: datatype of the column (string, number, date, currenty, html); used to pick a suitable editor for when editing the column value or inserting a new row
- constraints: for a numeric column, there can be formatting options (like # of decimal places) and/or restrictions on input. This constraints objects is a black box to Grid, but it's passed through to formatting functions based on the type above.
- formatUsing - function to format from raw value into displayed value (overrides type)
- editUsing - widget to use to edit value (widget will have setValue() and getTextValue() methods (overrides type)
- label: html text to print for the label
- sortKey: in the case of formatted data like "<img src= 'bag.jpg'><i>Louis Vitton</i> bag", the sort key must be different than the data itself.
Computed columns:
Given a item with the following values:
productCode: 1234
image: shoes.jpg
description: Louis Vitton bag
We might want to write
<icon> 1234 Louis Vitton bagYou can specify the column contents as a function on the item:
expression: function(store, item){
return "<img src=" + store.getValue(item, "image") + "> "
+ store.getValue(item, "productCode") ...
}
Supported Methods
- setQuery() - resets the filter on the data, then reissues the query and displays the resulting data
Look And Feel
Accessibility
- Full keyboard access - arrow key navigation of the cells in the
grid. This means adding tabindex=-1 on each cell and appropriately
responding to onkey events (Bill: this means up/down/left/right arrow, right? This should use Typematic.js.)
- Each cell receives focus as the user navigates to it (either via click or keyboard).
- Adding the WAI grid role on the outer element (either table or div element)
- Adding the WAI cell role on each cell.
- Adding the WAI readonly attribute on cells which are not editable (generally header cells)
- Adding the WAI columnheader and rowheader roles on the header cells.
- Sortable headers need the WAI sort attribute.
- If multiselection will be implemented this must be made
keyboard accessible as well. The WAI multiselectable role will be added
to the outer element.
Dojo.data <--> Grid interaction
This isn't really part of the grid spec, but it's listed here for convenience.
Grid will define functions like:
function onBegin(size){
gets the # of rows in the table.
setup the scrollbar etc. here.
}
function onItem(item){
callback when each row is returned.
for(var i=0; i < this.columns.length; i++){
var value = store.getValue(item, this.columns[i].attribute);
...
}
And then it will do something like this to get the first set of rows:
var params = { query: myQuery, sort: mySort, onBegin: onBegin, onItem: onItem, start: 0, count: 1000};
store.fetch(params);
Then to get next set of rows:
params.start += 1000; store.fetch(params);
And on destruction, or when before redoing the query with a new filter or new sort:
store.close(params);
Note that it's the store's job to do caching, sorting, and querying. The Grid widget doesn't have code for any of that.
For editing an existing row:
store.setValue(item, attr, newValue); ... store.save();
For adding a new row:
var item = store.newItem({attr1: value1, attr2: value2, ...});
store.save();
For deleting a row:
store.deleteItem(item); store.save();
Note: how do I get back to the 300th row, which is not #300 when you sort by date, but maybe row #100 or row #400??? It won't be supported in dojo 1.0 but maybe later
| Attachment | Size |
|---|---|
| table.jpg | 103.14 KB |
- Printer-friendly version
- Login or register to post comments
- Subscribe post
