Reactive data with SignalR and Knockout

with 4 Comments

I’ve been dabbling with Meteor for awhile and I have to say it’s a pretty awesome platform for building small web applications (I haven’t tried making something big yet). One of Meteor’s main strengths lie in its ability to easily push collection data to and from connected clients. This is not only convenient, but also solves the problem with outdated client data in single page applications. Normally in traditional web applications, the page is refreshed in response to navigation or submitting of a form by the user, so the client has updated data most of the time. In single page applications, this does not neccesarily happen. The programmer must decide when its convenient to refresh the data from the server. Meteors approach of automatically publishing new data as soon as its available is what we call reactivity.

This got me thinking; can we make something similar in ASP.NET that makes data reactive, while maintaining the simplicity of the Meteor implementation? Now I needed to refresh my Knockout skills for a project at work and this seemed like a fun little weekend project so I fired up Visual Studio and got to work.

Design

First of, a scenario. I decided to build a small todo list application. It’s a pretty washed-out example but it’s easy enough to get going fast. Here’s what it looks like:

screen1

With just a few lines of code both on the client and the server we have a very primitive todo list. Now the traditional way of writing this usually boils down to a bunch of Ajax calls for inserting, updating and refreshing data. The problem with this approach is that if another client were to modify the list, we would never get notified unless I were to manually refresh the page or add a cleaver Ajax call somewhere to grab fresh data periodically.

So what we really want to do is automatically get notified when the list is updated. Preferrably the list should get automatically updated to reflect the change so we don’t have to worry about that. If we also can make the user interface update itself, then we won’t have to do anything!

I decided to build the system with very few conventions. An Ajax GET request to get the initial data to our collection and POST actions for updates or inserts to the collection. These actions wouldn’t need to return anything related to the collections they modified (if any) since they would be automatically updated anyway. We would only care about handling any potential errors.

Implementation

We know that Knockout is great for updating the user interface in response to data changes. We also know that SignalR is pretty great at pushing data to clients. So let’s combine these powers. First, let’s look at the controller on the server:

Here we just statically define a list of todo items for simplicity. We also define a method for fetching the entire collection and a method for updating or inserting a new todo item. Along the bottom of the AddOrUpdate-method, we invoke our SignalR-hub to notify all clients that an item has changed or a new item has been added. That’s all that is necessary on the server side.

On the client we need to fetch the initial set of data and subscribe to the update notifications from the server. Let’s take a look at how we initialize our collections client side.

The interesting thing to note here is that we have something called IReactiveCollection<T>. An IReactiveCollection<T> is very simple, it’s just a regular JavaScript array wrapped in a closure to automatically update items in it whenever it recieves an update message from its corresponding SignalR-hub. It’s also slightly augmented with a function to update its contents for the initial data fetch.

Apart from the SignalR hub name on which we’ll be listening for notifications, the reactive collection requires a mapping function to map data coming from the server into a more convenient ViewModel. This is not essential for the system to work; one might want to do the actual mapping in a computed observable instead. I’ll save that one for further discussion.

After this setup is done, todoCollection is available across the application for us to use. Here’s how we could use it in our todo ViewModel:

In our constructor, we initialize our collection with what data the server collection contains at the moment. There is really no need to ever run this method again. From now on the collection will automatically stay updated when any new or updated todo-item is published on the server (including changes we initiate with one of our action methods). Note that we don’t really care about the response from the server in our addTodo-function. All we do is notify the user if anything goes wrong. Since adding a todo will result in an update being sent to our collection, this will just work ™ without any interference from us.

The Knockout bindings in our HTML are equally simple:

For completeness sake, here is what the react function looks like. This may break your computer or launch a nuclear missile or something less horrible. Beware.

Closing thoughts and obligatory GIF

react

This was just an experiment, but I think it provides an interesting view on how we should think about data in our web applications. Reactivity is a really cool concept and I’ll probably continue exploring it in this blog in the future.

The code can be found on GitHub: https://github.com/r1sc/react-test

Follow Erik Sandberg:

Latest posts from

4 Responses

  1. Johan Classon
    | Reply

    Nice tutorial. And I appreciate your effort making a GIF-animation!

  2. Stephan
    | Reply

    Hi,

    I was wondering if it is possible to avoid broadcasting to all clients when a certain record is modified. For ex if I modified data related to ID=1, then only the webpage/machine that has this client1 ope must get the message that, hey the data related to you is updated. It should not broadcast to all clients. Is it possbile to achieve this in SignalR

    • Erik Sandberg
      | Reply

      Hi Stephan! Yes this is possible with SignalR. In this example I didn’t bother with that since I specifically wanted all clients to be aware of any updates. However you could design your system with client-specific messages in mind to avoid broadcasting to all clients.

Leave a Reply