index.mustache revision acf1a78b9c946fbd3560053c3390089b37ce6072
<style type="text/css">
.yui3-datatable table {
width: auto;
}
.yui3-datatable td, .yui3-datatable th {
border: 0 none;
}
.yui3-datatable-col-Module {
white-space: nowrap;
}
.notice {
background: #faf3d1;
border: 1px solid #eac9a9;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: px;
padding: 0 1em;
-moz-box-shadow: 0 0 5px #ccc8b3;
-webkit-box-shadow: 0 0 5px #ccc8b3;
box-shadow: 0 0 5px #ccc8b3;
margin-bottom: 1em;
}
.notice h2 {
margin-top: .6em;
}
</style>
<div class="intro component">
<p>
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.
</p>
</div>
{{>getting-started}}
<div class="notice">
<h2 id="migration-intro">Upgrading from version 3.4.1 or older?</h2>
<p>
DataTable was refactored for 3.5.0. Some APIs were changed in backward
incompatible ways.
</p>
<p>
Read the <a href="migration.html">3.5.0 Migration Guide</a> for tips to
avoid unpleasant surprises. If you still run into issues, please
<a href="../../../projects/yui3/newticket/">file a ticket</a>.
</p>
<p>
If you are unable to upgrade due to unresolvable issues, you can use the
<a href="../datatable-deprecated/index.html">`datatable-deprecated`</a>
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.
</p>
</div>
<h2 id="using">DataTable Basics</h2>
<p>
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.
</p>
<p>
Under the hood, the DataTable class uses a
<a href="../model-list/index.html">ModelList</a> instance to manage the row
data properties. Read the <a href="#data">Table Data Configuration</a>
section below for details about how to load, configure, and work with the
table data.
</p>
```
// 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");
```
<p>This code produces this table:</p>
<div id="basic-example" class="yui3-skin-sam"></div>
<script>
YUI({ filter: 'raw' }).use('datatable-base', function (Y) {
// 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,
caption: "My first DataTable!",
summary: "Example DataTable showing basic instantiation configuration"
});
table.render("#basic-example");
});
</script>
<h2 id="columns">Column Configuration</h2>
<p>
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.
</p>
<p>
Use objects instead of key strings to customize how the cells in a column
display.
</p>
```
// 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");
```
<p>This code produces this table:</p>
<div id="column-example1" class="yui3-skin-sam"></div>
<script>
YUI().use('datatable-base', function (Y) {
// 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", 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("#column-example1");
});
</script>
<p>
Some column configurations affect the table headers and others affect the
data cells.
</p>
<p>
Use the `key` property to reference the associated data field when
configuring columns with objects. Other supported configuration
properties are listed in <a href="#column-config">Appendix A</a> below.
</p>
<h3 id="nested">Stacked Column Headers</h3>
<p>
Use the `children` column configuration to create multiple rows of column
headers.
</p>
```
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");
```
<p>This code produces this table:</p>
<div id="nested-example" class="yui3-skin-sam"></div>
<script>
YUI().use('datatable-base', function (Y) {
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: [
'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'
]
}
],
data : data
}).render("#nested-example");
});
</script>
<p>
`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.
</p>
<p>
Columns that have `children` don't relate directly to the data cells in the
table rows, so they <strong>should not</strong> have a `key` configured.
They should, however, include a `label` to provide the header's content.
</p>
<h3 id="formatters">Formatting Cell Data</h3>
<p>
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.
</p>
<p>
`formatter`s are expected to return the string content to populate each
cell in that column, and `nodeFormatter`s are provided with the cell Nodes
and expected to populate them using the Node API.
</p>
<p>
For best performance, <strong><a href="#formatter-vs-nodeformatter">avoid
`nodeFormatter`s unless absolutely necessary</a></strong>.
</p>
```
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);
'<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));
}
}
];
```
<p>This code produces this table:</p>
<div id="formatter-example" class="yui3-skin-sam">
<style scoped>
.yui3-datatable .yui3-datatable-data .expensive {
background-color: #ffe;
}
.yui3-skin-sam .yui3-datatable-data .auth td {
border-bottom: 1px dashed #cbcbcb;
}
.negative {
color: #700;
font-weight: 700;
}
tr.auth td {
text-align: right;
background-color: #fff;
border-top: 1px dashed #cbcbcb;
border-left: 1px solid #cbcbcb;
padding-right: 5px;
}
</style>
</div>
<script>
YUI().use('datatable-base', function (Y) {
var columns = [
'item',
{
key: 'cost',
formatter: '${value}' // formatter template string
},
{
key: 'price',
formatter: function (o) {
if (o.value > 10) {
o.className += ' expensive';
}
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');
row.one('td').setAttribute('rowspan', 2);
'<tr class="auth"><td colspan="3">' +
'<button class="ok">authorize</button>' +
'<button class="stop">discontinue</button>' +
'</td></tr>',
'after');
}
o.cell.setContent(prefix + profit.toFixed(2));
}
}
];
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: columns,
data : data
}).render("#formatter-example");
});
</script>
<p>
The parameters passed to `formatter` functions and `nodeFormatter`
functions are described in <a href="#formatter-props">Appendix B</a> and <a
href="#nodeformatter-props">Appendix C</a>, respectively.
</p>
<p>
<strong>Note</strong>: 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.
</p>
<h4 id="formatter-function">Setting content with `formatter` functions</h4>
<p>
Set the cell content with column `formatter`s 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` <em>do not include a return statement</em>.
</p>
<p>
`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.
</p>
```
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 },
...
```
<p>
See <a href="#formatter-props">Appendix B</a> for a list of all properties
passed to `formatter` functions.
</p>
<h4 id="nodeformatters">Setting content with `nodeFormatter` functions</h4>
<p>
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.
</p>
<p>
<strong>`nodeFormatter`s should return `false`</strong>.
<a href="formatter-vs-nodeformatter">See below</a> for details.
</p>
<p>
While there are <a href="#formatter-vs-nodeformatter">few scenarios that
require `nodeFormatter`s</a>, 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.
</p>
<p>
Like `formatters`, `nodeFormatters` are provided with the data field value,
the record data, the record Model instance, and the column configuration
object.
</p>
<p>
See <a href="#nodeformatter-props">Appendix C</a> for a list of all
properties passed to `nodeFormatter` functions.
</p>
<h4 id="formatter-vs-nodeformatter">Why `formatter` and `nodeFormatter`?</h4>
<p>
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 `formatter`s are applied.
</p>
<p>
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
<a href="http://yuilibrary.com/yui/docs/event/delegation.html">delegated</a>
from the DataTable instance itself using the
<a href="http://yuilibrary.com/yui/docs/api/classes/DataTable.html#method_delegate">`delegate()` method</a>.
</p>
<p>
On the rare occasion that you <em>must</em> 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.
</p>
<p>
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. <strong>This will not remove the rendered DOM, but it will remove
event subscriptions made on those Nodes</strong>.
</p>
<p>
In general, `nodeFormatter`s should only be used if absolutely necessary,
and should <em>always return `false`</em>.
</p>
<h4 id="formatters-vs-empty">Formatters vs. `emptyCellValue`</h4>
<p>
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.
</p>
<p>
String formatters will only be applied if the field data for that cell is
not `undefined`. This allows the `emptyCellValue` to populate the cell.
</p>
<p>
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.
</p>
<p>
The `emptyCellValue` configuration is ignored by columns configured with
`nodeFormatter`s.
</p>
<h2 id="data">Table Data Configuration</h2>
<p>
In each of the examples above, the DataTable's `data` attribute was
assigned an array of objects. This is a convenient way of initializing the
table, but not the only way.
</p>
<p>
Each record in the table is stored as a
<a href="../model/index.html">Model</a> instance, where the
keys of the record objects become Model attributes. This allows you to
interact with the models as you would any other <a
href="../base/index.html">Base</a>-based class, with `get(attr)`,
`set(attr, value)`, and subscribing to attribute change events.
</p>
```
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);
```
<p>
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.
</p>
<h3 id="recordtype">Specifying the Record Model</h3>
<p>
To use a custom Model for your records, pass your Model subclass to the
`recordType` attribute.
</p>
```
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
```
<p>
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.
</p>
<p>
If the `columns` configuration is omitted, but the `recordType` is set, the
`columns` will default to the `recordType`'s attributes.
</p>
```
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
});
```
<p>
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.
</p>
<h3 id="modellist">The `data` ModelList</h3>
<p>
</p>
<!-- TODO: describe data as array, data as ModelList instance, assigned and dynamic recordTypes -->
<p>
Integrate with the <a href="../datasource/">DataSource</a> data abstraction
utility to easily load data from remote sources and implement features such
as caching and polling.
</p>
```
var cols = [
"Title",
"Phone",
"Rating"
];
var myDataSource = new Y.DataSource.Get({
});
schema: {
resultListLocator: "query.results.Result",
resultFields: [
"Title",
"Phone",
{
key: "Rating",
locator: "Rating.AverageRating"
}
]
}
}),
var table = new Y.DataTable({
columns: cols,
summary: "Pizza places near 98089"
});
datasource: myDataSource
})
table.render("#pizza");
// Load the data into the table
request: "&q=select%20*%20from%20local.search%20where%20zip%3D%2794089%27%20and%20query%3D%27pizza%27"
});
// Make another request later
request: "&q=select%20*%20from%20local.search%20where%20zip%3D%2794089%27%20and%20query%3D%27chinese%27"
});
```
<p>
Enable DataSource caching.
</p>
```
var cols = [
"Title",
"Phone",
{ key: "Rating.AverageRating", label: "Rating" }
];
var myDataSource = new Y.DataSource.Get({
});
myDataSource
.plug(Y.Plugin.DataSourceJSONSchema, {
schema: {
resultListLocator: "query.results.Result",
resultFields: ["Title", "Phone", "Rating.AverageRating"]
}
})
.plug(Y.Plugin.DataSourceCache, {
max: 3
});
var table = new Y.DataTable({
columns: cols,
summary: "Pizza places near 98089",
caption: "Table with JSON data from YQL"
});
table
.plug(Y.Plugin.DataTableDataSource, {
datasource: myDataSource
})
.render("#pizza");
request: "&q=select%20*%20from%20local.search%20where%20zip%3D%2794089%27%20and%20query%3D%27chinese%27"
});
```
<p>
Enable DataSource polling.
</p>
```
var cols = ["Title", "Phone", "Rating"];
var myDataSource = new Y.DataSource.IO({
source: "/path/to/service.php?"
});
schema: {
resultListLocator: "Result",
resultFields: [
{ key: "Title", locator: "*[local-name() ='Title']" },
{ key: "Phone", locator: "*[local-name() ='Phone']" },
{ key: "Rating", locator: "*[local-name()='Rating']/*[local-name()='AverageRating']" }
]
}
});
var table = new Y.DataTable({
columns: cols,
summary: "Chinese restaurants near 98089",
caption: "Table with XML data from same-domain script"
});
table
.plug(Y.Plugin.DataTableDataSource, {
datasource: myDataSource,
initialRequest: "zip=94089&query=chinese"
})
.render("#chinese");
myDataSource.setInterval(5000, {
request: "zip=94089&query=chinese",
callback: {
}
});
```
<h3 id="sorting">Column sorting</h3>
<p>
Column sorting functionality can be added with the `datatable-sort` module
(included in the `datatable` rollup module).
Enable sorting for all columns by setting `sortable` to true during
instantiation. Alternately, pass `sortable` an array of column keys to
enable sortability of those specific columns, `false` to disable sorting,
or the string "auto" (the default) to determine which columns to make
sortable by reading the `sortable` property from the column configurations.
</p>
```
var cols = [
{ key: "Company", sortable: true },
{ key: "Phone" },
{ key: "Contact", sortable: true }
];
var data = [
{ Company: "Company Bee", Phone: "415-555-1234", Contact: "Sally Spencer"},
{ Company: "Acme Company", Phone: "650-555-4444", Contact: "John Jones"},
{ Company: "Indutrial Industries", Phone: "408-555-5678", Contact: "Robin Smith"}
];
// Alternately, include sortable: true or sortable: ["Company", "Contact"]
// in the DataTable configuration
var table = new Y.DataTable({
columns: cols,
data: data,
summary: "Contacts list",
caption: "Table with simple column sorting"
}).render("#sort");
```
<h3 id="scrolling">Scrolling</h3>
<p>
<strong>Note:</strong> Scrolling is not currently supported on the Android
WebKit browser.
</p>
<p>
Scrolling functionality can be added with the `datatable-scroll` module
(included in the `datatable` rollup module). Scrolling is enabled by the
`scrollable` attribute, which accepts values "x", "y", "xy", `true` (same
as "xy"), or `false` (the default).
</p>
<p>
Note, vertical scrolling also requires the table's `height` attribute to be
set, and horizontal scrolling requires the `width` to be set.
</p>
```
var cols = [
{ key: "Company" },
{ key: "Phone" },
{ key: "Contact" }
];
var data = [
{ Company: "Company Bee", Phone: "415-555-1234", Contact: "Sally Spencer"},
{ Company: "Acme Company", Phone: "650-555-4444", Contact: "John Jones"},
{ Company: "Indutrial Industries", Phone: "408-555-5678", Contact: "Robin Smith"}
];
var table = new Y.DataTable({
columns: cols,
data: data,
scrollable: "y",
height: "300px"
}).render("#scroll");
```
<h2 id="knownissues">Known Issues</h2>
<ul>
<li>
Scrolling is
<a href="http://yuilibrary.com/projects/yui3/ticket/2529761">not
currently supported on Android</a> WebKit browser.
</li>
<li>
Scrolling DataTable
<a href="http://yuilibrary.com/projects/yui3/ticket/2531047">may not
appear scrollable</a> on iOS browsers or OS X 10.7 depending on the
system preference "Show scroll bars" (General).
</li>
</ul>
<h2 id="column-config">Appendix A: Column Configurations</h2>
<p>
The properties below are supported in the column configuration objects
passed in the `columns` attribute array.
</p>
<div id="column-config-table" class="yui3-skin-sam">
<table>
<thead>
<tr>
<th scope="col">Configuration</th>
<th scope="col">Description</th>
<th scope="col">Module</th>
</tr>
</thead>
<tbody>
<tr>
<td>key</td>
<td>
```
{ key: 'username' }
```
<p>
Binds the column values to the named property in the `data`.
<p>
<p>
Optional if `formatter`, `nodeFormatter`, or `cellTemplate`
is used to populate the content.
</p>
<p>It should not be set if `children` is set.</p>
<p>
The value is used for the `_id` property unless the `name`
property is also set.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>name</td>
<td>
```
{ name: 'fullname', formatter: ... }
```
<p>
Use this to assign a name to pass to `table.getColumn(NAME)`
or style columns with class "yui3-datatable-col-NAME" if a
column isn't assigned a `key`.
</p>
<p>
The value is used for the `_id` property.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>field</td>
<td>
```
{ field: 'fullname', formatter: ... }
```
<p>An alias for `name` for backward compatibility.</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>id</td>
<td>
```
{
name: 'checkAll',
id: 'check-all',
label: ...
formatter: ...
}
```
<p>
Overrides the default unique id assigned `<th id="HERE">`.
</p>
<p>
<em>Use this with caution</em>, since it can result in
duplicate ids in the DOM.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>label</td>
<td>
```
{ key: 'MfgPrtNum', label: 'Part Number' }
```
<p>HTML to populate the header `<th>` for the column.</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>children</td>
<td>
<p>
Used to create stacked headers.
<a href="#nested">See the example above</a>.
</p>
<p>
Child columns may also contain `children`. There is no limit
to the depth of nesting.
</p>
<p>
Columns configured with `children` are for display only and
<strong>should not</strong> be configured with a `key`.
Configurations relating to the display of data, such as
`formatter`, `nodeFormatter`, `emptyCellValue`, etc. are
ignored.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>abbr</td>
<td>
```
{
key : 'forecast',
label: '1yr Target Forecast',
abbr : 'Forecast'
}
```
<p>Assigns the value `<th abbr="HERE">`.</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>headerTemplate</td>
<td>
```
{
headerTemplate:
'<th id="{id}" ' +
'title="Unread" ' +
'class="{className}" ' +
'{_id}>●</th>'
}
```
<p>
Overrides the default
<a href="{{apiDocs}}/classes/DataTable.HeaderView.html#property_CELL_TEMPLATE">CELL_TEMPLATE</a>
used by `Y.DataTable.HeaderView` to render the header cell
for this column. This is necessary when more control is
needed over the markup for the header itself, rather than
its content.
</p>
<p>
Use the `label` configuration if you don't need to
customize the `<th>` iteself.
</p>
<p>
Implementers are strongly encouraged to preserve at least
the `{id}` and `{_id}` placeholders in the custom value.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>cellTemplate</td>
<td>
```
{
key: 'id',
cellTemplate:
'<td class="{className}">' +
'<input type="checkbox" ' +
'id="{content}">' +
'</td>'
}
```
<p>
Overrides the default
<a href="{{apiDocs}}/classes/DataTable.BodyView.html#property_CELL_TEMPLATE">CELL_TEMPLATE</a>
used by `Y.DataTable.BodyView` to render the data cells
for this column. This is necessary when more control is
needed over the markup for the `<td>` itself, rather than
its content.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>formatter</td>
<td>
<p>
Used to customize the content of the data cells for this
column.
</p>
<p>
<a href="#formatters">See the example above</a>
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>nodeFormatter</td>
<td>
<p>
Used to customize the content of the data cells for this
column.
</p>
<p>
<a href="#formatters">See the example above</a>
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>emptyCellValue</td>
<td>
```
{
key: 'price',
emptyCellValue: '???'
}
```
<p>
Provides the default value to populate the cell if the data
for that cell is `undefined`, `null`, or an empty string.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>allowHTML</td>
<td>
```
{
key: 'preview',
allowHTML: true
}
```
<p>
Skips the security step of HTML escaping the value for cells
in this column. This is also necessary if `emptyCellValue`
is set with an HTML string.
</p>
<p>
`nodeFormatter`s ignore this configuration. If using a
`nodeFormatter`, it is recommended to use
<a href="{{apiDocs}}/classes/Escape.html#method_html">Y.Escape.html()</a>
on any user supplied content that is to be displayed.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>className</td>
<td>
```
{
key: 'symbol',
className: 'no-hide'
}
```
<p>
A string of CSS classes that will be added to the `<td>`'s
`class` attribute.
</p>
<p>
Note, all cells will automatically have a class in the
form of "yui3-datatable-col-KEY" added to the `<td>`, where
KEY is the column's configured `name`, `key`, or `id` (in
that order of preference).
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>width</td>
<td>
```
{ key: 'a', width: '400px' },
{ key: 'b', width: '10em' }
```
<p>
Adds a style `width` setting to an associated `<col>`
element for the column.
</p>
<p>
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 `width` configuration or
the natural width of the table's containing DOM elements.
</p>
<p>
If absolute widths are required, it can be accomplished with
some custom CSS and the use of a `cellTemplate`, or
`formatter`. See
<a href="{{apiDocs}}/modules/datatable-column-widths.html">the
description of `datatable-column-widths`</a> for an example
of how to do this.
</p>
</td>
<td>`datatable-column-widths`</td>
</tr>
<tr>
<td>sortable</td>
<td>
```
{ key: 'lastLogin', sortable: true }
```
<p>
Used when the instance's `sortable` attribute is set to
"auto" (the default) to determine which columns will support
user sorting by clicking on the header.
</p>
<p>
If the instance's `sortable` attribute is set, this
configuration is ignored.
</p>
</td>
<td>`datatable-sort`</td>
</tr>
<tr>
<td>sortFn</td>
<td>
```
{
label: 'Name',
sortFn: function (a, b, desc) {
order = (an > bn) ? 1 : -(an < bn);
return desc ? -order : order;
},
formatter: function (o) {
return o.data.lname + ', ' + o.data.fname;
}
}
```
<p>
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
`true` if the sort order should be descending.
</p>
<p>
The function should return `-1` to sort `a` above `b`, `-1`
to sort `a` below `b`, and `0` if they are equal. Keep in
mind that the order should be reversed when `desc` is
`true`.
</p>
<p>
The `desc` parameter is provided to allow `sortFn`s to
always sort certain values above or below others, such as
always sorting `null`s on top.
</p>
</td>
<td>`datatable-sort`</td>
</tr>
<tr>
<td>sortDir</td>
<td>
<p>
(<strong>read-only</strong>) If a column is sorted, this
will be set to `1` for ascending order or `-1` for
descending. This configuration is public for inspection,
but can't be used during DataTable instantiation to set the
sort direction of the column. Use the table's
<a href="{{apiDocs}}/classes/DataTable.html#attr_sortBy">sortBy</a>
attribute for that.
</p>
</td>
<td>`datatable-sort`</td>
</tr>
<tr>
<td>_yuid</td>
<td>
<p>
(<strong>read-only</strong>) The unique identifier assigned
to each column. This is used for the `id` if not set, and
the `_id` if none of `name`, 'field`, `key`, or `id` are
set.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>_id</td>
<td>
<p>
(<strong>read-only</strong>) 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
`table.getColumn(HERE)`, and in the column header's
`<th data-yui3-col-id="HERE">`.
</p>
<p>
The value is populated by the first of `name`, `field`,
`key`, `id`, or `_yuid` to have a value. If that value
has already been used (such as when multiple columns have
the same `key`), an incrementer is added to the end. For
example, two columns with `key: "id"` will have `_id`s of
"id" and "id2". `table.getColumn("id")` will return the
first column, and `table.getColumn("id2")` will return the
second.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>_colspan</td>
<td>
<p>
(<strong>read-only</strong>) Used by
`Y.DataTable.HeaderView` when building stacked column
headers.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>_rowspan</td>
<td>
<p>
(<strong>read-only</strong>) Used by
`Y.DataTable.HeaderView` when building stacked column
headers.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>_parent</td>
<td>
<p>
(<strong>read-only</strong>) Assigned to all columns in a
column's `children` collection. References the parent
column object.
</p>
</td>
<td>`datatable-base`</td>
</tr>
<tr>
<td>_headers</td>
<td>
<p>
(<strong>read-only</strong>) Array of the `id`s of the
column and all parent columns. Used by
`Y.DataTable.BodyView` to populate `<td headers="THIS">`
when a cell references more than one header.
</p>
</td>
<td>`datatable-base`</td>
</tr>
</tbody>
</table>
</div>
<h2 id="formatter-props">Appendix B: Formatter Argument Properties</h2>
<p>
The properties below are found on the object passed to `formatter`
functions defined in a column configuration. See
<a href="#nodeformatter-props">Appendix C</a> for the object properties
passed to `nodeFormatter`s.
</p>
<div id="formatter-props-table" class="yui3-skin-sam">
<table>
<thead>
<tr>
<th scope="col">Property</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>`value`</td>
<td>
```
formatter: function (o) {
// assumes a numeric value for this column
return '$' + o.value.toFixed(2);
}
```
<p>
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]`.
</p>
</td>
</tr>
<tr>
<td>`data`</td>
<td>
```
formatter: function (o) {
return o.data.lname + ', ' + o.data.fname;
}
```
<p>
The Model data for this row in simple object format.
</p>
</td>
</tr>
<tr>
<td>`record`</td>
<td>
```
formatter: function (o) {
return '<a href="/service/' + o.record.get('id') + '"> +
o.value + '</a>';
}
```
<p>
The Model for this row.
</p>
</td>
</tr>
<tr>
<td>`column`</td>
<td>
```
formatter: function (o) {
// Use a custom column property
var format = o.column.dateFormat || '%D';
return Y.DataType.Data.format(o.value, format);
}
```
<p>
The column configuration object.
</p>
</td>
</tr>
<tr>
<td>`className`</td>
<td>
```
formatter: function (o) {
if (o.value < 0) {
o.className += 'loss';
}
}
```
<p>
A string of class names to add `<td class="HERE">` in
addition to the column class and any classes in the
column's `className` configuration.
</p>
</td>
</tr>
<tr>
<td>`rowIndex`</td>
<td>
```
formatter: function (o) {
return (o.rowIndex + 1) + ' - ' + o.value;
}
```
<p>
The index of the current Model in the ModelList.
<em>Typically</em> correlates to the row index as well.
</p>
</td>
</tr>
<tr>
<td>`rowClass`</td>
<td>
```
formatter: function (o) {
if (o.value < 0) {
o.rowClass += 'loss';
}
}
```
<p>
A string of css classes to add `<tr class="HERE"><td...`.
</p>
<p>
This is useful to avoid the need for `nodeFormatter`s to add
classes to the containing row.
</p>
</td>
</tr>
</tbody>
</table>
</div>
<h2 id="nodeformatter-props">Appendix C: nodeFormatter Argument Properties</h2>
<p>
The properties below are found on the object passed to `nodeFormatter`
functions defined in a column configuration. See
<a href="#formatter-props">Appendix B</a> for the object properties
passed to `formatter`s.
</p>
```
// 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');
}
}
```
<div id="nodeformatter-props-table" class="yui3-skin-sam">
<table>
<thead>
<tr>
<th scope="col">Property</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>`td`</td>
<td>The `<td>` Node for this cell.</td>
</tr>
<tr>
<td>`cell`</td>
<td>
<p>
If the cell `<td>` contains an element with class
"yui3-datatable-liner", this will refer to that Node.
Otherwise, it is equivalent to `o.td` (default behavior).
</p>
<p>
By default, liner elements aren't rendered into cells, but
to implement absolute column widths, some cell liner
element with `width` and `overflow` style is required
(barring a table style of `table-layout: fixed`). This may
be applied to the columns `cellTemplate` configuration or
to the `bodyView` instance's `CELL_TEMPLATE` for all
columns.
</p>
<p>
Generally, the liner, if present, corresponds to where the
content should go, so use `o.cell` to add content and
`o.td` to specifically work with the `<td>` Node.
</p>
</td>
</tr>
<tr>
<td>`value`</td>
<td>
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]`.
</td>
</tr>
<tr>
<td>`data`</td>
<td>The Model data for this row in simple object format.</td>
</tr>
<tr>
<td>`record`</td>
<td>The Model for this row.</td>
</tr>
<tr>
<td>`column`</td>
<td>The column configuration object.</td>
</tr>
<tr>
<td>`rowIndex`</td>
<td>
The index of the current Model in the ModelList.
<em>Typically</em> correlates to the row index as well.
</td>
</tr>
</tbody>
</table>
</div>
<script>
YUI.add('datatable-node', function (Y) {
initializer: function (config) {
var columns = config.host.all('thead th').get('text'),
count = columns.length,
data = [],
i;
config.host.all('tbody td').each(function (node, i) {
var record = (i % count) ?
data[data.length - 1] :
(data[data.length] = {});
record[columns[i % count]] = node.getContent();
});
for (i = columns.length - 1; i >= 0; --i) {
columns[i] = { key: columns[i], allowHTML: true }
}
columns : columns,
data : data
});
this.render();
config.host.replace(this.get('boundingBox'));
}
}, {
NS: 'table'
});
setter: function (val) {
if (val === true) {
val = {};
}
this.plug(Y.Plugin.DataTable, val);
}
};
}, '0.1', { requires: ['datatable-base', 'node-pluginhost'] });
YUI({ filter: 'raw' }).use('datatable-node', 'datatable-sort', function (Y) {
Y.one('#column-config-table table').set('datatable', {
sortable: ['Configuration', 'Module']
});
Y.one('#formatter-props-table table').set('datatable', true);
Y.one('#nodeformatter-props-table table').set('datatable', true);
});
</script>