Tag Archives: knockout

Knockout.js logo

Performance of JQXGrid combined with knockout

The other day I noticed poor performance of a JQXGrid when combined with knockout. I had an ko.ObservableArray() with objects. Each object contains only 3 ko.observable(). I was using JQXGrid’s selection check-box on each row. Event-handlers were established to react to changes in the check-box and set one of the ko.observable() in the the corresponding object in the array.

On my page I was displaying the following:


  1. The JQXgrid

  2. A HTML table using the knockout foreach binding. This table displayed a checkbox for 1 of the observables and static text for the other one

  3. A string representation of the ViewModel using data-bind="text: JSON.stringify(ko.toJS(MyViewModel), null, 4)"

When I increased the number of object in the array, just modifying one check-box caused the UI to slowdown to unacceptable levels.

Items in array Time to complete one click (ms) Time to select all (ms)
25 1.408,136 22.233,092
50 2.156,774 77.999,535
100 5.871,934 473.352,168
200 23.124,779
400 115.075,14
800 707.176,804

When we graph this, you can see a clear O(n^2) performance bottleneck:
A graph showing the exponential increase in runtime

I wanted to change the grid’s source property to use a dataAdapter, However, that did render the table, but each colunm had no value. This is detailed in link where they say:

 March 30, 2012 at 12:53 pm	

It is currently not possible to bind the grid datafields to observable properties. Could you send us a sample view model which demonstrates the required functionality, so we can create a new work item and consider implementing the functionality in the future versions? Looking forward to your reply.

Best Wishes,
Peter

How to see the real-time state of your view-model

When testing and debugging your web-application, its convenient to see the real-time state of the view-model. This is very easy when you’re using a data-binding framework such as knockout. You can simply bind the JSON representation of the view-model to some visible DOM element, like this:

<pre data-bind="text: JSON.stringify(ko.toJS(MyViewModel), null, 4)"></pre>

Sometimes, the objects in your view-model might have circular dependencies, like this:

//The ViewModel
var CurrencyViewModel = function () {
    var self         = this;
    this.DataContext = new CurrencyDataContext(this);
    this.Name        = ko.observable();
    ...
}
//The pseudo-model behind the ViewModel
var CurrencyDataContext = function CurrencyDataContext(ViewModel) {
    var self = this
    this.Viewmodel = ViewModel
    ...
}

In that case you’ll get the following error:
0x800a13aa - JavaScript runtime error: Circular reference in value argument not supported
You can fix this by overriding the toJSON() method, like this:

var CurrencyDataContext = function CurrencyDataContext(ViewModel) {
    var self = this
    this.Viewmodel = ViewModel
    ...
    //Needed to avoid circular reference when a viewModel is serialised into JSON
    CurrencyDataContext.prototype.toJSON = function ()
    {
        var copy = ko.toJS(self);
        delete copy.Viewmodel
        return copy;
    }

Using jQuery to give your user a “check all” option in the UI

Say you have a table where each row contains a check-box and you want to be able to check/uncheck every single check-box based on some action the user does. Using jQuery this is very easy. Assume we have the following HTML:

        <table>
            <thead>
                <tr>
                    <th><input type="checkbox" id="HeaderCheckbox"/></th>
                    <th>Name</th>
                </tr>
            </thead>
            <tbody>
                <tr><td><input type="checkbox"/></td><td>Tea</td></tr> 
                <tr><td><input type="checkbox"/></td><td>Coffee</td></tr> 
                <tr><td><input type="checkbox"/></td><td>Cola</td></tr> 
           </tbody>
        </table>

Then the following jQuery snippet will transform the HeaderCheckbox into a control that automatically checks or unchecks all the other check-boxes:

$(document).ready(function () {
    //Setup an eventhandler that fires 
    //when the user clicks on a control whose id = HeaderCheckbox
    $('#HeaderCheckbox').click(function (eventobject) {
       //the DOM element that triggered the event
       var $this = $(this);                       
       //Determine what the requested state is. 
       //I.e is the headercheck box checked or unchecked?
       var checked = $this.prop('checked');
       //Find each checkbox element below a <tr> and set 
       //it to the requested sate       
       $("tr :checkbox").prop('checked', checked) 
    })
})

If you’re using some framework with data-binding (e.g. Knockout) then its way better to simply bind each check box to a property of the the View Model and just set that property. The HTML would look like this

        <table >
            <thead>
                <tr>
                    <th><input type="checkbox" id="HeaderCheckbox" /></th>
                    <th>Name</th>
                </tr>
            </thead>
            <tbody data-bind="foreach: Drinks">
                <tr>
                    <td><input type="checkbox" data-bind="checked: $data.Selected"/></td>
                    <td data-bind="text: $data.Name"></td>
                </tr>
            </tbody>
        </table>

And the associated JavaScript would look like this:

var DrinkViewModel = function () {
    this.Name           = ko.observable('');
    this.Selected       = ko.observable(false);
}

var viewModel = function () {
        var self = this
        //Holds all the drinks we want to list in the table
        this.Drinks = ko.observableArray();
}

$('#HeaderCheckbox').click(function (eventobject) {
    var $this = $(this);
    var checked = $this.prop('checked');
    $.each(MyViewModel.Drinks(), function (index, theDrink) 
    { 
        theDrink.Selected(checked) 
    })
})
Knockout.js logo

knockout.js: Your observable isn’t seeing changes made to text controls until they lose focus

knockout is great library that’s easy to use. One thing I noticed is that changes made in text-controls are only propagated to the observable once that control loses focus.

If you want changes in a text-control to immediately be reflected in your observable, then avoid the value binding and use the textInput binding like below:

<input data-bind="textInput: Name" type="text" value="" />
<script type="text/javascript">
    $(document).ready(function () {
        viewModel = ViewModel()
        ko.applyBindings(viewModel);

    });
    function ViewModel() {
        var self = this;
        self.Name = ko.observable("");
    }
</script>