Polling with Knockout, JQuery, AJAX, and MVC

Tags: Knockout, pubsub, observer, MVC, jQuery, Ajax, EF, Validation, FluentValidation, Visual Studio 2010, ASP.NET, JSON, FullCalendar, Silverlight, Architecture, Vista, IIS, Generics, NHibernate, WCF, RIA Services, Visual Studio 2008, SQL, STORM!, Nullable, ChannelFactory, netTCPBinding, VSPAT, responsive, design, HTML5, CSS3, MVC WebAPI, MVC 4, WebAPI, JQuery Mobile, ScheduleWidget, recurring events, Ninject, Pluggable, CQRS DDD, Windows

Some colleagues and I have been taking a close look at Knockout, the javascript library that implements the MVVM pattern for rich client UI. Knockout takes advantage of the new custom data attributes (data-*) in HTML 5 to bind declaratively a javascript view model to UI elements. That's pretty cool. But even better, Knockout will update your UI automatically whenever the underlying view model changes. It does this when you declare your model as an observable:

var WidgetViewModel = {
    shape: ko.observable('Square'),
    color: ko.observable('Purple'),
    teeth: ko.observable(12)
};

How about a demonstration? Let's say we want to monitor several running widgets and update their operational status on a web page every 5 seconds. That means polling with a timer. I'm going to show you one way to do it.

Fire up Visual Studio and create a new MVC 3 project called "WidgetMonitor". Knockout (currently v2.0) is available through NuGet so search for knockoutjs and add it to the project. You can also add it with the console by running the command Install-Package knockoutjs. In your _Layout.cshtml reference the library:

<script 
    src="@Url.Content("~/Scripts/knockout-2.0.0.js")" 
    type="text/javascript">
</script>

Add a new Widget model to your solution:

public class Widget
{
    public bool IsSpinning { get; set; }
    public string Shape { get; set; }
    public string Color { get; set; }
}

Now we need to modify our HomeController to return a collection of widgets. To keep it simple it will have a single GetWidgets method. And to make it interesting each widget will have a random boolean value for its IsSpinning property:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Message = "Welcome to SquareWidget.com!";
        return View();
    }

    [OutputCache(Duration = 0)]
    public JsonResult GetWidgets()
    {
        IEnumerable<Widget> widgets = new[]
        {
            new Widget { IsSpinning = CheckWidget(), Shape = "Round", Color = "Orange" },
            new Widget { IsSpinning = CheckWidget(), Shape = "Square", Color = "Purple" },
            new Widget { IsSpinning = CheckWidget(), Shape = "Triangular", Color = "Blue" }
        };

        return Json(widgets, JsonRequestBehavior.AllowGet);
    }

    private static bool CheckWidget()
    {
        Thread.Sleep(100);
        return new Random().Next() % 2 == 0;
    }
}

So far this is standard MVC stuff. Go to the Index.cshtml view for the HomeController. First, we're going to create a table to hold our widgets. The table will be ordinary in all respects except Knockout requires us to use declarative binding with the data-bind attribute for our properties that will be bound to the javascript view model:

@{
    ViewBag.Title = "Widget Page";
}

<h2>Widgets</h2>

<table>
    <thead>
        <tr>
            <th>Spinning</th>
            <th>Shape</th>
            <th>Color</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: widgets">
        <tr>
            <td data-bind="text: spinning"></td>
            <td data-bind="text: shape"></td>
            <td data-bind="text: color"></td>
        </tr>
    </tbody>
</table>

The text binding is the workhorse of Knockout but there are others. Behind the scenes it will update the innerText (or textContent) for the element to which it is bound. Notice the foreach binding. Just like the foreach statement in C# this is the way you handle an array of data in Knockout.

Ok, we've got our UI above. Now let's dive right into the javascript. Below the table create a script region and declare a local variable called widgets:

<script type="text/javascript">

    var widgets = ko.observableArray([]);

</script>

According to the Knockout documentation on observables "if you want to detect and respond to changes on one object, you’d use observables. If you want to detect and respond to changes of a collection of things, use an observableArray." In our case we're monitoring a collection of widgets so we want to use an observableArray. Notice that it is initialized to an empty list. Below the array add these two view models:

function Widget(spinning, shape, color) {
    this.spinning = ko.observable(spinning);
    this.shape = ko.observable(shape);
    this.color = ko.observable(color);
}

function WidgetListViewModel() {
    var self = this;
    self.widgets = widgets;
}

Notice that Widget is sort of like a data transfer object, or at least identical to our server-side object. Each property is declared as an observable. The WidgetListViewModel is nothing more than a container for our widgets. When it is initialized or updated it will always set its local widgets property to the widgets observable array.

So far so good. We've got our client-side view models declared. Now for the polling part that will update them every 5 seconds. Below the WidgetListViewModel add these two functions:

function pollSystems() {

    widgets.removeAll();
        
    $.getJSON('/Home/GetWidgets', function (data) {
        $.each(data, function () {
            widgets.push(new Widget(this.IsSpinning, this.Shape, this.Color));
        });
    });

    setTimeout("pollSystems()", 5000);
}
    
$(function () {
    pollSystems();
});

Notice that most of this is just plain old jQuery. The document ready event calls the pollSystems function to kick things off. It will call setTimeout so it calls itself every 5 seconds. The Knockout magic comes in with the push function call. Knockout will add the new Widget to the widgets observableArray and then notify its listeners of the change. Those listeners will update your UI table automatically. What is the glue that binds the data-bind elements with the observables? The magic is with a function call applyBindings. Add it now:

ko.applyBindings(new WidgetListViewModel());

This is how you activate Knockout. Build and run the app and you should see a list of widgets that update every 5 seconds:

1 Comment

  • codeplexer said

    Try it and it's not working - because my knockoutjs is using different version than yours ;), src="@Url.Content("~/Scripts/knockout-2.0.1.js")" after I update it, works like as expected. Chrome or most browser will not show any error indicating the script is not loaded until I checked it using F12. check on the resource tab. Just in case someone is trying the code and can't get it working.

Add a Comment