Thursday, November 15, 2012

Punchout Grid implementation

I was recently working on a client side grid implementation for a messaging application and stumbled upon EntitySpaces's Punchout Grid. It is a very lightweight Knockout grid implementation which I found quite easy to customize and put to use in a generic sort of way. It supports paging and sorting, however the sorting methods were left up to the developer to implement (you can see some of the sorting methods I have added in the punchout_grid.js file for basic types).

I have slightly modified the template to add some of the Twitter bootstrap table styles, and added some custom knockout bindings for cell bound and header bound events. The beauty of this approach is you significantly improve page load times (trust me, I've compared with several other jQuery grids), and it enables you to write much cleaner code leveraging Knockout's DOM/Javascript object binding. This will help you avoid writing excess jQuery methods to manipulate the DOM and make use of the powerful MVVM pattern (making your UI much more flexible and interactive).

To use the grid, simply construct a JSON data property in your view model (I used an extension to pass in my strongly typed MVC view's JSON data), and pass in the grid column definitions in the columns property:
//strongly typed model property names
var idPropertyName = '@Html.DisplayNameFor(model => model.FirstOrDefault().ID)';
var flaggedPropertyName = '@Html.DisplayNameFor(model=> model.FirstOrDefault().Flagged)';

var viewModel = {
   //data
   data: ko.observableArray(@(new MvcHtmlString(Model.ToJSON()))),
   
   //knockout observableArray 
   columns: ko.observableArray([
               {
                   "columnName": idPropertyName, "dataType": "Int32", "displayName": idPropertyName, "isNullable": false,
                   "isVisible": false, "isSortable": false, "propertyName": idPropertyName
               },
               {
                   "columnName": flaggedPropertyName, "dataType": "Boolean", "displayName": flaggedPropertyName, "isNullable": false,
                   "isVisible": true, "isSortable": true, "propertyName": flaggedPropertyName
               }
            ])
        }

Next implement any custom bindings/events needed for your grid (in this case I'm adding a custom css class to two of the grid columns):
ko.bindingHandlers.cellBound = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        switch (valueAccessor()) {
            case flaggedPropertyName:
                $(element).text() == 'true' ? $(element).attr("class", "flagged") : $(element).attr("class", "unflagged");
                break;
            case attachmentPropertyName:
                $(element).text() > 0 ? $(element).attr("class", "attachment") : $(element).attr("class", "noattachment");
                break;
            default:
                return;
        }
    }
}

And finally add your html div for the grid:

Handling grid cell selections become as simple as the following:

When a row becomes selected, Knockout updates the data bound text box, and automatically updates the grid after the user edits the textbox.

The full implementation is on github here. Feel free to make use in your own projects, and enjoy!