Knockout Grid – Cell Templates

Recently, I posted a tutorial for creating a Grid bound to a Knockout ViewModel that loaded remote data from an OData feed. Today we are going to enhance this sample by adding custom cell templates using Knockout.

It is easy to render data as text in a grid, but we can improve on that greatly. Let’s add sparklines to each grid row that displays sales trends for each product. Creating rich cell templates are an easy way to improve your application. And the best part of templates is that you can put anything in them!

Modify our ViewModel

First, We need sales history data for each product, so lets add a collection of sales data to our person constructor.

var product = function (data) {
    return {
        ProductID: ko.observable(data.ProductID),
        ProductName: ko.observable(data.ProductName),
        UnitPrice: ko.observable(data.UnitPrice),
        UnitsInStock: ko.observable(data.UnitsInStock),
        Sales: ko.observableArray([{ OrderID: 1, Units: rando(), Year: "2009" }, { OrderID: 2, Units: rando(), Year: "2010" }, { OrderID: 3, Units: rando(), Year: "2011" }, { OrderID: 4, Units: rando(), Year: "2012" }])
    };
};

I also added a function called rando() for generating random sales numbers.

function rando() {
    return Math.floor(Math.random() * 51);
}

Next, we will need to add a cellFormatter function to our ViewModel. This will be called before the rendering of each cell in the last column of our grid. In this function, I am making sure that the row is a data row (and not a header or footer). I am also checking to make sure that a template exists before rendering it. If there is a template, we can apply the binding context of the current row to it and render it.

self.cellFormatter = function cellFormatter(args) {
    var $rt = $.wijmo.wijgrid.rowType;

    if ((args.row.type & $rt.data)) {
        var tmpl = document.getElementById("chartTemplate");

        if (tmpl) {
            args.$container.append(tmpl.text);
            var bindingContext = args.$container.get(0); //get Grid cell element
            ko.applyBindings(self.dataRows()[args.row.dataItemIndex], bindingContext); //apply bindings to cell
            return true;
        }
    }

    return false; // use default formatting
}

Create Our Template

Now, we need to create the Knockout template that will be rendered in our grid cell. inside our template is a wijmo line chart. We have set some options to make it look like a simple sparkline chart. Notice that it is bound to the Sales collection. It’s context the the current row of the grid which is a product object.

<script type="text/html" id="chartTemplate">
    <div data-bind="
        wijlinechart: {
            dataSource: Sales,
            legend: { visible: false },
            marginLeft: 0,
            marginRight:0,
            marginTop:0,
            marginBottom:0,
            height: 20,
            width:100,
            showChartLabels: false,
            axis: {
                x: {
                    visible: false,
                    textVisible: false,
                    gridMajor: { visible: false },
                    gridMinor: { visible: false }
                },
                y: {
                    visible: false,
                    textVisible: false,
                    gridMajor: { visible: false },
                    gridMinor: { visible: false }
                }
            },
            seriesList: [{
                data: {
                    x: { bind: 'OrderID' },
                    y: { bind: 'Units'} }
        }] }">
    </div>
</script>

Modify our View

Lastly, we just need to specify the cellFormatter handler for the last column in our View.

<table id="dataGrid" data-bind="
    wijgrid: {
        selectionMode: 'singleRow',
        data: dataRows,
        pageSize: pageSize,
        pageIndex: pageIndex,
        totalRows: totalRows,
        allowPaging: true,
        allowSorting: true,
        sorted: sorted,
        pageIndexChanged: paged,
        columns: [
            { dataKey: 'ProductID', sortDirection: 'ascending', dataType: 'number', dataFormatString: 'n0', headerText: 'ID', width: 60 },
            { dataKey: 'ProductName', headerText: 'Product' },
            { dataKey: 'UnitPrice', dataType: 'currency', headerText: 'Price', width: 100},
            { dataKey: 'UnitsInStock', dataType: 'number', dataFormatString: 'n0', headerText: 'Units', width: 100},
            { visible: true, cellFormatter: cellFormatter, width: 100 }]
    }">
</table>

The key piece of code above is cellFormatter: cellFormatter. The rest is standard. Also notice that the last column does not have a dataKey. It is just an empty cell column.

Now we have a beautiful column of sparklines that displays sales trends for each row!

Run it!

That’s it, just run the app and check out the column to the far right. It now has nice spark lines that display sales data.

Summary

Grids are powerful UI tools for end users. We often just bind them to data and display it in text format. Try using a more visual template to give your data more meaning. By combining the cellFormatter function in the wijmo grid with Knockout templates you can easily accomplish this.