This example shows how to use the DataTable's recordType
attribute to
create columns whose values are calculated from other columns. It was
contributed by community member Todd Smith.
In the table below, the "Market Value" and "Gain or Loss" columns are
created from data in the other columns by configuring them as
attributes with getter
s in the table's recordType
configuration.
recordType
sets the Model
DataTable's recordType
attribute is used to specify the class used to
store the record instances in your table. If you don't configure your
DataTable's recordType
, a Y.Model
subclass will be created for you
based on the keys in the data that you fill the table with.
As well as accepting Model subclasses, recordType
can be passed an object
that corresponds to a class's ATTRS
collection. From this, DataTable will
create the Model subclass accordingly. This is useful if you want to
provide default values, or validate or transform incoming values (such as
turning numeric strings into numbers).
var table = new Y.DataTable({ columns: ['cost', 'price', 'profit'], recordType: { cost: { value: 0.0001, // default value validator: function (val) { return Y.Lang.isNumber(val) && val > 0; } }, price: { setter: function (val) { val = +val; // coerce numeric strings to numbers return isFinite(val) ? val : Y.Attribute.INVALID_VALUE; } } }, ... });
Use attribute getter
s to populate custom columns
Another thing you can configure with attributes is a getter
, which is
responsible for returning a value to instance.get('yourAttribute')
calls.
Since DataTable uses this method to get cell contents for columns, you can
use getter
s to create columns whose content is calculated on the fly.
var table = new Y.DataTable({ columns: ['cost', 'price', 'profit'], recordType: { cost: {}, price: {}, profit: { getter: function () { return this.get('price') - this.get('cost'); }, readOnly: true } }, ... });
The data
populating the table need only include cost
and price
. The
profit
column will be populated automatically based on the other two.
Sorting for free
It is possible to create calculated columns using column formatter
s as
well, but if you need that column to be sortable, you need to associate
that column with a single key
, which may be insufficient, or configure
the column with a sortFn
.
Using an attribute with a getter
, you are creating a new field with a
key
that corresponds to the full values you want to sort by using the
default sort mechanism. And you can even add formatter
s on top of the
raw, sortable, calculated value.
Full Code Listing
JavaScript
YUI().use('datatable', 'datatype-number-format', function (Y) { var portfolio = [ { stock_id:3, ticker:'XYZEE', company:'XYZ Corporation', qty:300, cost:4500, price:15.83 }, { stock_id:11, ticker:'FUBAR', company:'FooBar Computers, Inc.', qty:100, cost:2187, price:28.90 }, { stock_id:17, ticker:'GIFT', company:"Spinoff Technology", qty:400, cost:0, price:4.11 }, { stock_id:19, ticker:'DOLLARS', company:"Consultants 'R Us", qty:1750, cost:6099.13, price:3.97 }, { stock_id:5, ticker:'SAFET', company:'Stability Partners LLP', qty:25, cost:7283.41, price:58.74 } ]; function formatCurrency(o) { return Y.DataType.Number.format(o.value, { prefix:"$ ", thousandsSeparator: ",", decimalSeparator: ".", decimalPlaces: 2 }); } function formatGainLoss(o) { o.className += (o.value < 0) ? 'loss' : 'gain'; return o.value ? Y.DataType.Number.format(o.value, { suffix: ' %', decimalSeparator: ".", decimalPlaces: 2 }) : 'n/a'; } var dt = new Y.DataTable({ data: portfolio, columns: [ { key: 'ticker', label: 'Ticker' }, { key: 'company', label: 'Company Name' }, { key: 'qty', label: 'Share Qty', className: 'numeric' }, { key : 'cost', label : 'Purchase Cost', className: 'numeric', formatter: formatCurrency }, { key : 'marketvalue', label : 'Market Value', className: 'numeric', formatter: formatCurrency }, { key : 'gainloss', label : 'Gain or Loss', className: 'percentage', formatter: formatGainLoss } ], recordType: { ticker: {}, company: {}, qty: {}, cost: {}, marketvalue: { getter: function () { return +((this.get('price') * this.get('qty')).toFixed(2)); } }, gainloss: { getter: function () { var cost = this.get('cost'), amt = (this.get('qty') * this.get('price')) - this.get('cost'); return cost ? ((100 * amt) / cost) : 0; } } }, sortable: ['ticket', 'company', 'cost', 'marketvalue', 'gainloss'], sortBy: { gainloss: 'desc' } }); dt.render("#dtable"); });
CSS
.numeric { text-align: right; } .percentage { text-align: center; } .loss { color: red; } .gain { color: green; }
HTML
Note: be sure to add the yui3-skin-sam
classname to the
page's <body>
element or to a parent element of the widget in order to apply
the default CSS skin. See Understanding Skinning.
<body class="yui3-skin-sam"> <!-- You need this skin class --> <div id="dtable"></div>