SignalR Tutorial with KnockoutJs



What we'll be doing

We're going to do a basic quickstart tutorial on building a realtime webappliction using SignalR. SignalR is hosted on GitHub and makes it easy to push messages from the server to the client browsers. We'll be simply flling out a form to add a story div to the current page, KnockoutJs and SignalR source code download is here.

SignalR Sceencast (see written tutorial below)

SignalR Tutorial

For this app you'll need Visual Studio 2010 with SP1 and Asp.Net MVC 3 package installed. We also assume you've got NuGet installed, if you haven't install NuGet.

Step 1 - Create an Empty Asp.Net MVC 3 App

Goto File --> New Project --> Web --> Asp.Net MVC 3 Application (Make sure it's .Net Framework 4). Call your project something interesting like SignalRTutorial .

Firstly let's just check everything compiles and we indeed have an empty web application.  We're going to break the tradition and make a Scrum board. This is partially to try and keep me more organised in the new year.

Step 2 - Add SignalR Package

Right click on references and click Manage NuGet Packages. Search for SignalR and click install. Just check everything compiles before we start.

Step 3 - Create a StoryCard Class

Minor part but this will be our simple DTO (Data Transfer Object) that we'll pass between the server and browser.

using SignalRTutorial.Hubs;

namespace SignalRTutorial.Models
{
    public class StoryCard
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public StoryState State { get; set; }

        public int Id { get; set; }

        public static StoryCard Create(string title, string description, StoryState storyState)
        {
            return new StoryCard
                       {
                           Id=1,
                           Title = title,
                           Description = description,
                           State = storyState
                       };
        }
    }
}

namespace SignalRTutorial.Models
{
    public enum StoryState{Todo,InProgress,Verify,Done}
}

Step 4 - Create a SignalR Hub

Create a new class called ScrumBoardHub and inherit from Hub this lets SignalR work it's magic.

using System.Collections.Generic;
using SignalR.Hubs;
using SignalRTutorial.Models;

namespace SignalRTutorial.Hubs
{
    public class ScrumBoardHub : Hub
    {
        private readonly IScrumRepository _repository;

        public ScrumBoardHub():this(new ScrumBoardRepository()){}

        public ScrumBoardHub(IScrumRepository repository)
        {
            _repository = repository;
        }
       
        public void CreateStory(StoryCard story)
        {
            Caller.AddedStory(story);
        }

        public void GetStories()
        {
            var storyCards = new List()
                                 {
                                     new StoryCard
                                         {
                                             Id = 1, Title = "Story 1", Description = "Story 1 desc", State = StoryState.Todo
                                         }, new StoryCard
                                                {
                                                    Id = 2, Title = "Story 2", Description = "Story 2 desc", State = StoryState.Todo
                                                },
                                 };
            var array = storyCards.ToArray();
            Caller.populateStories( array);
        }
    }
}

Step 5 - Create a Board Listing Page

I've just used the index page from the HomeController. Below are the various bits of Javascript you'll need for this to work. Please pay particular attentiaon to KnockoutJs and Jquery.signalr-0.5.3.js 

Just above is the line that will thanks to a bit of KnockoutJs magic our list of stories. We're using the data-bind attribute but this time we're specifying a template called story-template.

Then we simply tell it to bind to stories property on our ScrumBoard viewModel which you'll see defined below in the javascript.

Step 6 - wireup KnockoutJs model binding with SignalR

Below is the Javascript for mapping our StoryCard object on the server side to a Story client side object. We have a Scrumboard object that exposes a few methods including binding itself to the title and description fields for adding new stories. When the form is submitted, addStory is called which in turn calls createStory on the SignalR hub. On the serverside we then use SignalR to call out to the addedStory javascript method which simply creates a new instance of a Story in the javascript and adds it onto the stories collection. 

KnockoutJs then uses it's model binding, to add another div to the container, as indicated by the foreach binding above.

$(function () {

            function Story(id, title, description, state) {
                this.Id = ko.observable(id);
                this.Title = ko.observable(title);
                this.Description = ko.observable(description);
                this.State = ko.observable(state);
            }

            function ScrumBoard() {
                this.title = ko.observable("New Story");
                this.description = ko.observable("New Description");

                this.hub = $.connection.scrumBoardHub;
                this.viewModel = null;
                this.stories = ko.observableArray([]);
                var stories = this.stories;

                this.init = function () {
                    console.log("init");
                    this.hub.getStories();
                };

                this.addStory = function (newStory) {
                    console.log("addStory");
                    var t = { "title": this.title(), "description": this.description(), "state":"todo" };
                    this.hub.createStory(t).done(function () {
                        console.log('Success!')
                    }).fail(function (e) {
                        console.warn(e);
                    });
                    this.title("");
                    this.description("");

                };

                this.hub.addedStory = function (story) {
                    console.log("addedStory");
                    stories.push(new Story(story.Id, story.Title, story.Description,story.State));
                };

                this.hub.populateStories = function (storyArray) {
                    console.log("populate stories");
                    var mappedTasks = $.map(storyArray, function (item) {
                        return new Story(item.Id, item.Title, item.Description,item.State);
                    });

                    stories(mappedTasks);
                };

            }


            var scrumBoard = new ScrumBoard();
            $.connection.hub.start(function () { scrumBoard.init(); });
            ko.applyBindings(scrumBoard);


        });

Conclusion

That's it. Isn't that cool, we could improve it by also storing stories in a database using NHibernate, and we can further scale it by utilising Redis. I'll embelish later

References

Testing SignalR Tutorial

The Source code for this tutorial

https://github.com/SignalR/SignalR

Scott Hanselman's SignalR Tutorial

AmazedSaint - RealTime TaskPad with SignalR and KnockoutJS

SignalR and KnockoutJs Realtime task list


blog comments powered by Disqus

Search

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2014