The DataTable widget is responsible for rendering columnar data into a highly customizable and fully accessible HTML table. The core functionality of DataTable is to visualize structured data as a table. A variety of class extensions can then be used to add features to the table such as sorting and scrolling.
Getting Started
To include the source files for DataTable and its dependencies, first load the YUI seed file if you haven't already loaded it.
<script src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js"></script>
Next, create a new YUI instance for your application and populate it with the
modules you need by specifying them as arguments to the YUI().use() method.
YUI will automatically load any dependencies required by the modules you
specify.
<script>
// Create a new YUI instance and populate it with the required modules.
YUI().use('datatable', function (Y) {
// DataTable is available and ready for use. Add implementation
// code here.
});
</script>
For more information on creating YUI instances and on the
use() method, see the
documentation for the YUI Global Object.
Upgrading from version 3.4.1 or older?
DataTable was refactored for 3.5.0. Some APIs were changed in backward incompatible ways.
Read the 3.5.0 Migration Guide for tips to avoid unpleasant surprises. If you still run into issues, please file a ticket.
If you are unable to upgrade due to unresolvable issues, you can use the
datatable-deprecated
module suite, which is equivalent to the 3.4.1 implementation. But be
aware that these modules will be removed in a future version of YUI.
DataTable Basics
A basic DataTable is made of columns and rows. Define the columns you
want to display in your DataTable with the columns attribute. Rows are
created for you based on the data you provide to the data attribute.
Under the hood, the DataTable class uses a ModelList instance to manage the row data properties. Read the Table Data Configuration section below for details about how to load, configure, and work with the table data.
// Columns must match data object property names
var data = [
{ id: "ga-3475", name: "gadget", price: "$6.99", cost: "$5.99" },
{ id: "sp-9980", name: "sprocket", price: "$3.75", cost: "$3.25" },
{ id: "wi-0650", name: "widget", price: "$4.25", cost: "$3.75" }
];
var table = new Y.DataTable({
columns: ["id", "name", "price"],
data: data,
// Optionally configure your table with a caption
caption: "My first DataTable!",
// and/or a summary (table attribute)
summary: "Example DataTable showing basic instantiation configuration"
});
table.render("#example");
This code produces this table:
Column Configuration
The columns attribute takes an array of field names that correspond to
property names in the data objects. These field names are called "keys".
As long as these keys exist in your data, DataTable will display the
values in the table. By default, the key is also used as the label of the
column header.
Use objects instead of key strings to customize how the cells in a column display.
// Columns must match data object property names
var data = [
{ id: "ga-3475", name: "gadget", price: "$6.99", cost: "$5.99" },
{ id: "sp-9980", name: "sprocket", price: "$3.75", cost: "$3.25" },
{ id: "wi-0650", name: "widget", /* missing */ cost: "$3.75" }
];
var table = new Y.DataTable({
columns: [
"id",
{ key: "name", label: "part name" },
{ key: "price", allowHTML: true, emptyCellValue: "<em>(not set)</em>" },
"cost"
],
data: data
});
table.render("#example");
This code produces this table:
Some column configurations affect the table headers and others affect the data cells.
Use the key property to reference the associated data field when
configuring columns with objects. Other supported configuration
properties are listed in Appendix A below.
Stacked Column Headers
Use the children column configuration to create multiple rows of column
headers.
var columns = [
'username',
{
// Important: Parent columns do NOT get a key...
// but DO get a label
label: "Access",
// Pass an array of column configurations (strings or objects) as children
children: [
'read',
'write',
]
}
];
var data = [
{ username: "root", read: true, write: true },
{ username: "spilgrim", read: true, write: false },
{ username: "fizzgig", read: false, write: false }
];
var table = new Y.DataTable({
columns: columns,
data : data
}).render("#example");
This code produces this table:
children takes an array of column configurations, just like the columns
attribute itself. The columns defined in the children property will have
header cells rendered below the parent column's header.
Columns that have children don't relate directly to the data cells in the
table rows, so they should not have a key configured.
They should, however, include a label to provide the header's content.
Formatting Cell Data
To customize the display of cell data in a column, DataTable provides the
formatter and nodeFormatter column configurations. Both configurations
accept functions, but formatter will also accept a template string.
formatters are expected to return the string content to populate each
cell in that column, and nodeFormatters are provided with the cell Nodes
and expected to populate them using the Node API.
For best performance, avoid
nodeFormatters unless absolutely necessary.
var columns = [
'item',
{
key: 'cost',
formatter: '${value}' // formatter template string
},
{
key: 'price',
formatter: function (o) {
if (o.value > 3) {
o.className += ' yellow-background';
}
return '$' + o.value.toFixed(2);
}
},
{
label: 'profit',
nodeFormatter: function (o) {
var profit = o.data.price - o.data.cost,
prefix = '$',
row;
if (profit < 0) {
prefix = '-' + prefix;
profit = Math.abs(profit);
row = o.cell.ancestor();
o.cell.addClass('negative');
// Assign a rowspan to the first cell and add a new row
// below this one to span the last three columns
row.one('td').setAttribute('rowspan', 2);
row.insert(
'<tr class="auth"><td colspan="3">' +
'<button class="ok">authorize</button>' +
'<button class="stop">discontinue</button>' +
'</td></tr>',
'after');
}
o.cell.set('text', prefix + profit.toFixed(2));
}
}
];
This code produces this table:
The parameters passed to formatter functions and nodeFormatter
functions are described in Appendix B and Appendix C, respectively.
Note: It's highly recommended to keep the data in the
underlying data ModelList as pure data, free from presentational
concerns. For example, use real numbers, not numeric strings, and store
link urls and labels either in separate data fields or in a single data
field, but as separate properties of a value object. This allows the data
to be used for calculations such as sorting or averaging.
Setting content with formatter functions
Set the cell content with column formatters by returning the desired
content string from the function. Alternately, just update o.value with
the new value in the object passed as an argument to the formatter. When
updating o.value do not include a return statement.
formatters are very powerful because not only do they have access to the
record's value for that column's field, but they also receive the rest of
the record's data, the record Model instance itself, and the column
configuration object. This allows you to include any extra configurations
in your column configuration that might be useful to customizing how cells
in the column are rendered.
function currency(o) {
return Y.DataType.number.format(o.value, {
prefix : o.column.currencySymbol || '$',
decimalPlaces : o.column.decimalPlaces || 2,
decimalSeparator : o.column.decimalSeparator || '.',
thousandsSeparator: o.column.thousandsSeparator || ','
});
}
var cols = [
{ key: "price", formatter: currency, decimalPlaces: 3 },
...
See Appendix B for a list of all properties
passed to formatter functions.
Setting content with nodeFormatter functions
Unlike formatters which can effectively default to the normal rendering
logic by leaving o.value unchanged, nodeFormatters must assign content
to the cells themselves. The cell's initial classes will be set up, but
that's it. Everything else is your responsibility.
nodeFormatters should return false.
See below for details.
While there are few scenarios that
require nodeFormatters, they do have the benefits of having the Node
API for constructing more complex DOM subtrees and the ability to access
all nodes in the <tbody>. This means they can reference, and even modify,
cells in other rows.
Like formatters, nodeFormatters are provided with the data field value,
the record data, the record Model instance, and the column configuration
object.
See Appendix C for a list of all
properties passed to nodeFormatter functions.
Why formatter and nodeFormatter?
For good rendering performance and memory management, DataTable creates
table content by assembling innerHTML strings from templates, with
{placeholder} tokens replaced with your data. However, this means that
the Nodes don't exist yet when a column's formatters are applied.
To minimize the need to create Nodes for each cell, the default rendering
logic supports the addition of cell classes as well as row classes via
formatter functions. Event subscriptions should be
delegated
from the DataTable instance itself using the
delegate() method.
On the rare occasion that you must use Nodes to supply the cell data, DataTable allows a second pass over the generated DOM elements once the initial string concatenation has been completed and the full HTML content created.
It is important to note that nodeFormatters will necessarily create a
Node instance for each cell in that column, which will increase the memory
footprint of your application. If the Node instance wrappers around the
DOM elements don't need to be maintained beyond the life of the
nodeFormatter, return false to remove them from the internal object
cache. This will not remove the rendered DOM, but it will remove
event subscriptions made on those Nodes.
In general, nodeFormatters should only be used if absolutely necessary,
and should always return false.
Formatters vs. emptyCellValue
The emptyCellValue configuration is useful to provide fallback content in
the case of missing or empty column data, but it interacts with each type of
formatter differently.
String formatters will only be applied if the field data for that cell is
not undefined. This allows the emptyCellValue to populate the cell.
Function formatters are applied before the return value or (potentially
altered) o.value property is tested for undefined, null, or the empty
string. In any of these cases, the emptyCellValue populates the cell.
The emptyCellValue configuration is ignored by columns configured with
nodeFormatters.
Table Data Configuration
Each record in the table is stored as a
Model instance, where the
keys of the record objects become Model attributes. This allows you to
interact with the models as you would any other Base-based class, with get(attr),
set(attr, value), and subscribing to attribute change events.
var data = [
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
{ item: "sprocket", cost: 4.08, price: 3.75 },
{ item: "nut", cost: 0.01, price: 0.25 }
];
var table = new Y.DataTable({
columns: ["item", "cost", "price"],
data: data
});
var sprocket = table.getRecord(2);
// Fires a costChange event, and the table is updated if rendered
sprocket.set('cost', 2.65);
The Model class used to store the record data is created for you, based on
the objects in the data array. If data is not set, the column keys
identified in the columns configuration is used.
Specifying the Record Model
To use a custom Model for your records, pass your Model subclass to the
recordType attribute.
var pieTable = new Y.DataTable({
recordType: Y.PieModel,
columns: ['slices', 'type'],
data: [
// Y.PieModel has attributes 'slices', which defaults to 6, and 'type',
// which defaults to 'apple'. Records can use these defaults.
{ type: 'lemon meringue' },
{ type: 'chocolate creme', slices: 8 },
{} // equivalent to { type: 'apple', slices: 6 }
]
});
// Y.PieModel has its idAttribute assigned to 'type', overriding the default
// of 'id'. Fetch a PieModel by its id.
var applePie = pieTable.getRecord('apple');
// eatSlice is a method on the Y.PieModel prototype
applePie.eatSlice();
Alternately, recordType will accept an array of attribute strings or an
ATTRS configuration object to make it easier to create custom attribute
behaviors without needing to explicitly build the Model subclass.
If the columns configuration is omitted, but the recordType is set, the
columns will default to the recordType's attributes.
var data = [
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
{ item: "sprocket", cost: 4.08, price: 3.75 },
{ item: "nut", cost: 0.01, price: 0.25 }
];
// Effectively synonymous with setting the columns attribute if no special
// column configuration is needed.
var table = new Y.DataTable({
recordType: [ 'item', 'cost', 'price' ],
data: data
});
// Or for more control, pass an ATTRS configuration object
var table = new Y.DataTable({
recordType: {
item: {},
cost: {
value: 0,
setter: function (val) { return +val || 0; }
},
price: {
valueFn: function () { return (this.get('cost') + 0.1) * 10; },
setter: function (val) { return +val || 0; }
}
},
data: data
});
When the table data is loaded asychronously, it is often a good idea to
configure the recordType. This can prevent the generation of a record
Model that is missing fields that are omitted from the columns
configuration because they aren't intended for viewing.
The data ModelList
The record Models are stored in a
ModelList, which is assigned to the
data property on the instance (for easier access than going through table.get('data')).
var records = [
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
{ item: "sprocket", cost: 4.08, price: 3.75 }
];
var table = new Y.DataTable({
columns: ["item", "cost", "price"],
data : records
});
// Add a new Model using the ModelList API. This will fire
// events and change the table if rendered.
table.data.add({ item: "nut", cost: 0.01, price: 0.25 });
When assigning the DataTable's data attribute with an array, a ModelList
is created for you. But you can also pass a ModelList instance if you are
sharing a ModelList between widgets on the page, or you have created custom
Model and ModelList classes with additional logic, such as adding a
data sync layer.
var table = new Y.DataTable({
columns: ['type', 'slices'],
data: new Y.PieList()
});
// The Y.PieList class implements a sync layer, enabling its load() method
table.data.load(function () {
table.render('#pies');
});
Getting Remote Table Data
To fetch remote data, you have three options:
-
For quick one-offs, you can load and parse the data manually, using
Y.io(...),Y.jsonp(...), etc., then assign that data to the DataTable'sdataattribute. This isn't very elegant or maintainable, so is best avoided for anything other than proofs of concept. -
For the most control, better maintainability, and better encapsulation of business logic, create Model and ModelList subclasses that implement a sync layer as suggested above.
-
For common read-only scenarios, use the
Y.Plugin.DataTableDataSourceplugin to bind your table to aDataSourceinstance. Use plugins to add DataSource features.
// Create a JSONP DataSource to query YQL
var myDataSource = new Y.DataSource.Get({
source: 'http://query.yahooapis.com/v1/public/yql?format=json&' +
'env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&q='
});
myDataSource.plug(Y.Plugin.DataSourceJSONSchema, {
schema: {
resultListLocator: 'query.results.Result',
resultFields: [
'Title',
'Phone',
{
// Important that record fields NOT include ".", so
// extract nested data with locators
key: 'Rating',
locator: "Rating.AverageRating"
}
]
}
})
.plug(Y.Plugin.DataSourceCache, {
max: 3
});
// No data is provided at construction because it will load via the
// DataTableDataSource plugin
var table = new Y.DataTable({
columns: ['Title', 'Phone', 'Rating'],
summary: 'Pizza places near 98089'
});
table.plug(Y.Plugin.DataTableDataSource, {
datasource: myDataSource
})
// Initially render an empty table and show a loading message
table.render('#pizza')
.showMessage('loadingMessage');
// Load the data into the table
table.datasource.load({
request: encodeURIComponent(
'select *' +
' from local.search' +
' where zip="94089"' +
' and query="pizza"');
});
DataTable Modules and Features
For a basic, stripped down Y.DataTable class, include the datatable-base
module in your use().
Feature modules, such as datatable-sort, will bring in datatable-base
automatically. By including only feature modules in your use(), you will
get a Y.DataTable that supports specifically those features, without
extra code for other features you won't be using.
The datatable module is a bundle of datatable-base plus a set of common
feature modules. Other feature modules need to be included explicitly in
use().
| Module | Description | In datatable? |
|---|---|---|
datatable-core |
The core API for DataTable, implemented as a class extension, used
by datatable-base to create Y.DataTable and Y.DataTable.Base.
|
yes |
datatable-base |
Creates the Y.DataTable and Y.DataTable.Base classes, and
defaults the headerView and bodyView to Y.DataTable.HeaderView
and Y.DataTable.BodyView respectively.
|
yes |
datatable-head |
Creates the Y.DataTable.HeaderView class as a subclass of
Y.View. DataTable defers rendering of the <thead> content to
this View when it is passed as the DataTable's headerView
attribute (the default, as set by datatable-base).
|
yes |
datatable-body |
Creates the Y.DataTable.BodyView class as a subclass of
Y.View. DataTable defers rendering of the <tbody> content to
this View when it is passed as the DataTable's bodyView
attribute (the default, as set by datatable-base).
|
yes |
datatable-message |
Creates the Y.DataTable.Message class extension and adds
showMessage and hideMessage methods to Y.DataTable.
|
yes |
datatable-column-widths |
Creates the Y.DataTable.ColumnWidths class extension, and adds
support for the width property in column configuration objects
to Y.DataTable.
|
yes |
datatable-mutable |
Creates the Y.DataTable.Mutable class extension and adds methods
such as addRow, removeRow, and moveColumn to Y.DataTable.
|
yes |
datatable-sort |
Creates the Y.DataTable.Sort class extension and adds methods
sort and toggleSort as well as attributes sortable and
sortBy to Y.DataTable. Enables sorting the table rows by
clicking on column headers.
|
yes |
datatable-datasource |
Creates the Y.Plugin.DataTableDataSource plugin for binding a
DataSource instance to the table as its source of record data.
|
yes |
datatable-scroll |
Creates the Y.DataTable.Scroll class extension and adds attribute
scrollable to Y.DataTable. Adds support for vertically and/or
horizontally scrolling table rows within fixed table dimensions.
|
no |
Features in DataTable.Base
By including only datatable-base in your use() line, you get both
Y.DataTable and Y.DataTable.Base classes. With no other module
inclusion, these classes are effectively the same. When additional
DataTable related modules are included, those modules' features will
usually be added to Y.DataTable, but never to
Y.DataTable.Base.
Though it can be instantiated, the purpose of Y.DataTable.Base is
primarily as a superclass to a custom DataTable implementation that has a
locked set of features that will not be modified, as Y.DataTable can be,
by the inclusion of other modules.
// Create a custom DataTable that includes only the core set of APIs, plus
// sorting and message support.
Y.MyDataTable = Y.Base.create('myTable', Y.DataTable.Base,
[ Y.DataTable.Sort, Y.DataTable.Message ]);
Y.use('datatable-scroll', function (Y) {
// Y.DataTable now has support for scrolling
var table = new Y.DataTable({ scrollable: 'y', ... });
// Y.MyDataTable does not (the config does nothing)
var myTable = new Y.MyDataTable({ scrollable: 'y', ... });
});
Y.DataTable.Base includes the columns, data, caption, and other
basic table attributes, the underlying ModelList and View rendering
architecture, as well as methods to fetch rows and cells or columns and
records.
Rendering features include most column configurations, such as children
and allowHTML, as well as column formatting options formatter,
nodeFormatter, cellTemplate, etc.
Table Messages
The datatable-message module adds the ability to display a message in the
table body. By default, the "emptyMessage" will display when the table's
ModelList has no data records. The message will hide when data is added.
var table = new Y.DataTable({
columns: ["id", "name", "price"],
data: []
}).render('#example');
This code produces this table:
Use table.showMessage("message") and table.hideMessage() to toggle the
message display.
showMessage supports internationalized strings by using a few named
strings, which are registered in the language packs for the
datatable-message module . These strings are currently:
-
table.showMessage("emptyMessage")defaults to "No data to display". -
table.showMessage("loadingMessage")defaults to "Loading...".
Other values passed to showMessage will pass that content directly
through to the message Node.
Column Width Configuration
The datatable-column-widths module adds basic support for specifying
column widths.
var table = new Y.DataTable({
columns: [
{ key: 'item', width: '125px' },
{ key: 'cost', formatter: '${value}' },
...
],
data : data
}).render("#example");
This code produces this table:
CAVEAT: Column widths will expand beyond the configured
value if column cells contain data that is long and can't line-wrap. Also,
column widths may be reduced below the configured value if the table width
(by configuring the DataTable's width attribute, or constrained by a
narrow containing element) is too narrow to fit all data at the configured
widths.
To force column widths, including cell data truncation and allowing the
table to spill beyond its configured or inherited width, wrap the cell
content in a <div> either by configuring the column's formatter or
cellTemplate, then assign the <div>'s CSS style with the desired width
(or "inherit"), plus overflow: hidden;. Then set the DataTable column's
width configuration accordingly.
Column sorting
The datatable-sort module adds support for sorting the table rows either
through the added APIs or by clicking on the table headers.
By default, when datatable-sort is included, DataTables will inspects
the columns objects, looking for sortable: true to enable table sorting
by those columns, triggered by clicking on their respective headers.
var cols = [
{ key: "Company", sortable: true },
{ key: "Phone" },
{ key: "Contact", sortable: true }
];
For convenience, you can enable header-click sorting for all columns by
setting the sortable attribute to true, or pass an array of column keys
to enable just those column's headers.
// Set all columns to be sortable
var table = new Y.DataTable({
columns: ["Company", "Phone", "Contact"],
data: ...
sortable: true
}).render("#example");
This code produces this table:
Hold down the shift key while clicking on column headers to subsort by
that column. Doing so repeatedly will toggle the subsort direction.
As long as the datatable-sort module has been included, you will always
be able to sort the table data through the API, even by columns that aren't
configured to accept header-click sorting.
When a table is sorted, any new records added to the DataTable's ModelList will be inserted at the proper sorted index, as will the created table rows.
Disable header-click sorting by setting sortable to false.
Custom Sorting
Assign a function to a column's sortFn to support customized sorting. The
function will receive the two records being compared and a boolean flag
indicating a descending sort was requested.
var columns = [
{
key: 'id',
label: '●', // a big dot
formatter: function (o) {
return o.value ? '' : '●'; // only new records have a dot
},
sortable: true,
sortFn: function (a, b, desc) {
var aid = a.get('id'),
bid = b.get('id'),
acid = a.get('clientId'),
bcid = b.get('clientId'),
order = // existing records are equivalent
(aid && bid) ? 0 :
// new records are grouped apart from existing records
(aid && -1) || (bid && 1) ||
// new records are sorted by insertion order
(acid > bcid) ? 1 : -(acid < bcid);
return desc ? -order : order;
}
},
...
The function must return 1, 0, or -1. 1 specifies that the Model passed as the first parameter should sort below the Model passed as the second parameter. -1 for above, and 0 if they are equivalent for the purpose of this sort.
Sorting Methods
To sort the table in the code, call table.sort(NAME OR KEY). To
toggle the sort direction, call table.toggleSort(NAME OR KEY).
// Sorts the table by values in the price field in ascending order
table.sort('price');
// Flips to descending
table.toggleSort('price');
To sort by multiple columns, pass an array of column keys to sort or
toggleSort.
Calling toggleSort with no arguments will reverse all current sort
directions. Calling with specific column names or keys will toggle only
those columns.
// Sort first by author, subsort by title in ascending order
table.sort(['author', 'title']);
// Now descending by author then title
// same as table.toggleSort(['author', 'title']);
table.toggleSort();
// Now ascending by author, descending by title
table.toggleSort('author');
To specify a sort direction, pass an object instead of a string to sort.
The object should have the column name as the key, and sort direction as its
value.
// Explicitly sort by price in descending order
table.sort({ price: 'desc' });
// Each column gets its own object
table.sort([{ author: 'desc' }, { title: 'desc' }]);
Acceptable values for the sort direction are "asc", "desc", 1, and -1. 1 is equivalent to "asc", and -1 to "desc".
The sortBy Attribute
Every sort operation updates the sortBy attribute. You can also trigger
a sort by setting this attribute directly. It accepts the same values as
the sort method.
// Sort by author in descending order, then by title in ascending order
table.set('sortBy', [{ author: -1 }, 'title']);
To specify an initial sort order for your table, assign this attribute during instantiation. This will sort the data as soon as it is added to the table's ModelList.
// Pre-sort the data
var table = new Y.DataTable({
columns: ['item', 'cost', 'price'],
data: [...],
sortBy: { price: -1 }
});
The sort Event
Clicking on a column header, or calling the sort or toggleSort methods
will fire a sort method containing an e.sortBy property that
corresponds to the requested sort column and direction. The value will be
in either string or object format, depending on how each method was used.
Preventing the sort event will prevent the sortBy attribute from being
updated. Updating the sortBy attribute directly will not fire the sort
event, but will still sort the data and update the table.
Table Mutation APIs (addRow, etc)
The datatable-mutable module adds APIs for adding, removing, and
modifying records and columns.
Column Mutation Methods
Use the methods addColumn, removeColumn, modifyColumn, and
moveColumn to update the table's configured columns.
// Insert a column for the profit field in the data records as the third column
table.addColumn('profit', 2);
// Actually, make that the fourth column
table.moveColumn('profit', 3);
// Actually, strike that. Don't show it after all
table.removeColumn('profit');
// Instead, add a formatter to the price column that includes the profit data
table.modifyColumn('price', {
formatter: function (o) {
return o.value + ' (' + (o.data.profit / o.data.cost).toFixed(2) + '%)';
}
});
Each column mutation method fires an identically named event. See the API docs for details.
Row Mutation Methods
Use the methods addRow, addRows, removeRow, and modifyRow to update
the table's ModelList.
table.addRow({ item: 'collet', cost: 0.42, price: 2.65 });
table.addRows([
{ item: 'nut', cost: 0.42, price: 2.65 },
{ item: 'washer', cost: 0.01, price: 0.08 },
{ item: 'bit', cost: 0.19, price: 0.97 }
]);
// Remove table records by their Model, id, clientId, or index
table.removeRow(0);
// Modify a record by passing its id, clientId, or index, followed by an
// object with new field values
table.modifyRow('record_4', { cost: 0.74 });
Everything that's done by these methods can be accomplished through the table's ModelList instance methods, but having methods on the table itself can make the code more readable.
// Same as table.addRow(...);
table.data.add({ item: 'collet', cost: 0.42, price: 2.65 });
By default, changes made to the table are only local, they don't update the
server or other data origin if the data was served remotely. However, if
your table's ModelList is built with a sync layer, the mutation methods
can also trigger the appropriate sync behavior by passing an additional
argument to the methods, an object with the property sync set to true.
// Tell the server we're down to one slice of apple pie!
table.modifyRow('apple', { slices: 1 }, { sync: true });
// Uh oh, make that 0. No more apple pie :(
table.removeRow('apple', { sync: true });
If all modifications are destined for the server/origin, you can set the
autoSync attribute to true, and the row mutation methods will
automatically call into the sync layer.
var pies = new Y.DataTable({
columns: ['type', 'slices'],
data: new Y.PieList()
autoSync: true
});
pies.data.load(function () {
pies.render('#pie-cart');
// The new PieModel's save() method is called, notifying the server
pies.addRow({ type: 'pecan', slices: 8 });
// Let us eat some pie!
pies.modifyRow('lemon meringue', { slices: 5 });
});
Scrolling
Note: Scrolling is not currently supported on the Android WebKit browser.
Scrolling functionality can be added to Y.DataTable by including
datatable-scroll module in your use(). datatable-scroll is
NOT included in the datatable rollup module, so must be
included separately.
Enable scrolling by setting the scrollable attribute, which accepts values
"x", "y", "xy", true (same as "xy"), or false (the default).
Note, vertical scrolling also requires the table's height attribute to be
set, and horizontal scrolling requires the width to be set.
// Data from the seafoodwatch YQL table as of 3/16/2012
var data = [
{ "fish": "Barramundi (Imported Farmed in Open Systems)", "recommendation": "avoid" },
{ "fish": "Caviar, Paddlefish (Wild caught from U.S.)", "recommendation": "avoid" },
{ "fish": "Caviar, Sturgeon (Imported Wild-caught)", "recommendation": "avoid" },
...
];
// Enable vertical scrolling with scrollable "y". The width is also set, but
// because scrollable is not "x" or "xy", this just sets the table width.
var table = new Y.DataTable({
caption: 'Seafood tips for the US West Coast',
columns: ['fish', 'recommendation'],
data: data,
scrollable: "y",
height: "200px",
width: "400px"
}).render("#scroll");
This code produces this table:
DataTable Events
DataTable is a composition of supporting class instances and extensions, so
to centralize event reporting, it is a bubble target for its data
ModelList as well as the View instances used for rendering.
In other words, some events you may need to subscribe to using an event prefix to be notified. Often, using a wildcard prefix is the simplest method to ensure your subscribers will be notified, even if classes change.
// The sort event is from an extension, so it originates from DataTable
table.after('sort', function (e) { ... });
// Model changes originate from the record's Model instance, propagate to the
// table's ModelList, then finally to the DataTable, so they must be
// subscribed with an event prefix. In this case, we'll use a wildcard
// prefix.
table.after('*:priceChange', function (e) { ... });
DataTable generates a custom Model class with the "record" event prefix, if
you want to be more specific. Otherwise, if your table uses a custom Model
class for its recordType, you can prefix Model events with the appropriate
prefix.
// Allow DataTable to generate the Model class automatically
var table = new Y.DataTable({
columns: ['items', 'cost', 'price'],
data: [
{ item: "widget", cost: 23.57, price: 47.5 },
{ item: "gadget", cost: 0.11, price: 6.99 },
...
]
});
// generated Model classes have prefix "record"
table.after('record:change', function (e) { ... });
// PieList uses PieModels, which have a prefix of, you guessed it, "pie"
var pies = new Y.DataTable({
columns: ['type', 'slices'],
data: new Y.PieList()
});
pies.on('pie:slicesChange', function (e) {
if (e.target.get('type') === 'chocolate creme') {
// Oh no you don't!
e.preventDefault();
}
});
The full list of events is included in the DataTable API docs.
Known Issues
- Scrolling is not currently supported on Android WebKit browser.
- Scrolling DataTable may not appear scrollable on iOS browsers or OS X 10.7 depending on the system preference "Show scroll bars" (General).
Appendix A: Column Configurations
The properties below are supported in the column configuration objects
passed in the columns attribute array.
| Configuration | Description | Module |
|---|---|---|
| key |
{ key: 'username' }
Binds the column values to the named property in the
Optional if It should not be set if
The value is used for the |
datatable-base |
| name |
{ name: 'fullname', formatter: ... }
Use this to assign a name to pass to
The value is used for the |
datatable-base |
| field |
{ field: 'fullname', formatter: ... }
An alias for |
datatable-base |
| id |
{
name: 'checkAll',
id: 'check-all',
label: ...
formatter: ...
}
Overrides the default unique id assigned Use this with caution, since it can result in duplicate ids in the DOM. |
datatable-base |
| label |
{ key: 'MfgPrtNum', label: 'Part Number' }
HTML to populate the header |
datatable-base |
| children |
Used to create stacked headers. See the example above.
Child columns may also contain
Columns configured with |
datatable-base |
| abbr |
{
key : 'forecast',
label: '1yr Target Forecast',
abbr : 'Forecast'
}
Assigns the value |
datatable-base |
| headerTemplate |
{
headerTemplate:
'<th id="{id}" ' +
'title="Unread" ' +
'class="{className}" ' +
'{_id}>●</th>'
}
Overrides the default
CELL_TEMPLATE
used by
Use the
Implementers are strongly encouraged to preserve at least
the |
datatable-base |
| cellTemplate |
{
key: 'id',
cellTemplate:
'<td class="{className}">' +
'<input type="checkbox" ' +
'id="{content}">' +
'</td>'
}
Overrides the default
CELL_TEMPLATE
used by |
datatable-base |
| formatter |
Used to customize the content of the data cells for this column. |
datatable-base |
| nodeFormatter |
Used to customize the content of the data cells for this column. |
datatable-base |
| emptyCellValue |
{
key: 'price',
emptyCellValue: '???'
}
Provides the default value to populate the cell if the data
for that cell is |
datatable-base |
| allowHTML |
{
key: 'preview',
allowHTML: true
}
Skips the security step of HTML escaping the value for cells
in this column. This is also necessary if
|
datatable-base |
| className |
{
key: 'symbol',
className: 'no-hide'
}
A string of CSS classes that will be added to the
Note, all cells will automatically have a class in the
form of "yui3-datatable-col-KEY" added to the |
datatable-base |
| width |
{ key: 'a', width: '400px' },
{ key: 'b', width: '10em' }
Adds a style
Note, the assigned width will not truncate cell content, and
it will not preserve the configured width if doing so would
compromise either the instance's
If absolute widths are required, it can be accomplished with
some custom CSS and the use of a |
datatable-column-widths |
| sortable |
{ key: 'lastLogin', sortable: true }
Used when the instance's
If the instance's |
datatable-sort |
| sortFn |
{
label: 'Name',
sortFn: function (a, b, desc) {
var an = a.get('lname') + b.get('fname'),
bn = a.get('lname') + b.get('fname'),
order = (an > bn) ? 1 : -(an < bn);
return desc ? -order : order;
},
formatter: function (o) {
return o.data.lname + ', ' + o.data.fname;
}
}
Allows a column to be sorted using a custom algorithm. The
function receives three parameters, the first two being the
two record Models to compare, and the third being a boolean
The function should return
The |
datatable-sort |
| sortDir |
(read-only) If a column is sorted, this
will be set to |
datatable-sort |
| _yuid |
(read-only) The unique identifier assigned
to each column. This is used for the |
datatable-base |
| _id |
(read-only) A unique-to-this-instance name
used extensively in the rendering process. It is also used
to create the column's classname, as the input name
The value is populated by the first of |
datatable-base |
| _colspan |
(read-only) Used by
|
datatable-base |
| _rowspan |
(read-only) Used by
|
datatable-base |
| _parent |
(read-only) Assigned to all columns in a
column's |
datatable-base |
| _headers |
(read-only) Array of the |
datatable-base |
Appendix B: Formatter Argument Properties
The properties below are found on the object passed to formatter
functions defined in a column configuration. See
Appendix C for the object properties
passed to nodeFormatters.
| Property | Description |
|---|---|
value |
formatter: function (o) {
// assumes a numeric value for this column
return '$' + o.value.toFixed(2);
}
The raw value from the record Model to populate this cell.
Equivalent to |
data |
formatter: function (o) {
return o.data.lname + ', ' + o.data.fname;
}
The Model data for this row in simple object format. |
record |
formatter: function (o) {
return '<a href="/service/' + o.record.get('id') + '"> +
o.value + '</a>';
}
The Model for this row. |
column |
formatter: function (o) {
// Use a custom column property
var format = o.column.dateFormat || '%D';
return Y.DataType.Data.format(o.value, format);
}
The column configuration object. |
className |
formatter: function (o) {
if (o.value < 0) {
o.className += 'loss';
}
}
A string of class names to add |
rowIndex |
formatter: function (o) {
return (o.rowIndex + 1) + ' - ' + o.value;
}
The index of the current Model in the ModelList. Typically correlates to the row index as well. |
rowClass |
formatter: function (o) {
if (o.value < 0) {
o.rowClass += 'loss';
}
}
A string of css classes to add
This is useful to avoid the need for |
Appendix C: nodeFormatter Argument Properties
The properties below are found on the object passed to nodeFormatter
functions defined in a column configuration. See
Appendix B for the object properties
passed to formatters.
// Reference nodeFormatter
nodeFormatter: function (o) {
if (o.value < o.data.quota) {
o.td.setAttribute('rowspan', 2);
o.td.setAttribute('data-term-id', this.record.get('id'));
o.td.ancestor().insert(
'<tr><td colspan"3">' +
'<button class="term">terminate</button>' +
'</td></tr>',
'after');
}
o.cell.setContent(o.value);
}
| Property | Description |
|---|---|
td |
The <td> Node for this cell. |
cell |
If the cell
By default, liner elements aren't rendered into cells, but
to implement absolute column widths, some cell liner
element with
Generally, the liner, if present, corresponds to where the
content should go, so use |
value |
The raw value from the record Model to populate this cell.
Equivalent to o.record.get(o.column.key) or
o.data[o.column.key].
|
data |
The Model data for this row in simple object format. |
record |
The Model for this row. |
column |
The column configuration object. |
rowIndex |
The index of the current Model in the ModelList. Typically correlates to the row index as well. |