#

# Example: Sortable generated columns

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>