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 getters 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 getters 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 getters 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 formatters 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 formatters 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
<div id="dtable"></div>