How to write real-time web app using SignalR, TypeScript, KnockoutJS, HTML5?
Nowadays, end-users like responsive applications and there are times when certain type of applications need to support real-time updates if required. In this post, I’ll tell you how to write real-time web app using SignalR, TypeScript, KnockoutJS, HTML5.
As such this is a how-to post, I expect readers to have some basic knowledge about these technologies mentioned above. But in a nutshell:
What is SignalR?
ASP.NET SignalR is a new library for ASP.NET developers that makes developing real-time web functionality easy. SignalR allows bi-directional communication between server and client. Servers can now push content to connected clients instantly as it becomes available. SignalR supports Web Sockets, and falls back to other compatible techniques for older browsers. SignalR includes APIs for connection management (for instance, connect and disconnect events), grouping connections, and authorization.
– ASP.NET
What is TypeScript?
Typescript is a typed superset of JavaScript that compiles to plain JavaScript. It runs in any browser, any host and any OS. It is open source with good tooling support for various IDE and code editors.
– Learn more about it here.
What is KnockoutJS?
Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model. Any time you have sections of UI that update dynamically (e.g., changing depending on the user’s actions or when an external data source changes), KO can help you implement it more simply and maintainably.
– KnockoutJS
Requirement/Scope
A real-time web application can be really exciting and SignalR makes it more interesting by providing strong client support as it can leverage Web Sockets, Server-Sent Events (SSE), Forever Frame and Long Polling whenever needed.
Let us define the scope of the real-time application that will be ready by the end of this post. There is a requirement to show the CPU, paging and disk usage on the server to the users of our web application. This can be part of an internet or intranet web app. The code snippets in this post doesn’t account authentication or authorization to keep the scope simple. Ideally, this kind of information can be a part of app’s dashboard.
Tools/Packages
I’m using Visual Studio for this requirement to create an ASP.NET web application with MVC & Web API and name it as “PerfSurf”. I opted this template type so as to get Bootstrap, jQuery and some other default packages. Make sure you have proper TypeScript support in your IDE with latest Web Essentials update.
Use the Nuget Package Manager to install following packages: KnockoutJS, SignalR and their nuget packages names are knockoutjs, Microsoft.AspNet.SignalR respectively.
To show the real-time updates on the user-interface get Smoothie.js, a charting library. Unfortunately, this is not available via nuget at the time of writing this post. So, visit the link, copy the raw library code and paste it in a new file in your vendor specific scripts folder.
To leverage strong typing support of TypeScript, install following packages: `jquery.TypeScript.DefinitelyTyped`, `knockout.TypeScript.DefinitelyTyped`, `signalr.TypeScript.DefinitelyTyped`, `smoothie.TypeScript.DefinitelyTyped`. These are the definition files that helps an IDE to give proper intellisense support.
Server-side Setup
Based on your authentication type – Internet or Intranet, you may or may not have a `Startup.cs` file in your project at root level. If it is not there, add this file and configure the app to use SignalR. My project is internet type so I already had this file and it looks like this:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(PerfSurf.Startup))]
namespace PerfSurf
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
ConfigureAuth(app);
}
}
}
The method named `MapSignalR` maps SignalR hubs to the app builder pipeline at `/signalr`.
Hub
The next main step is to create a custom Hub that derives from `Microsoft.AspNet.SignalR.Hub`. It provides methods that communicate with SignalR connections that is connected to a hub. This hub will basically define all the methods (marked public) that clients (browser) can use. Below is the code to create a hub for our requirement:
namespace PerfSurf.Hubs
{
public class PerfHub : Hub<IPerfHubClient>
{
public PerfHub()
{
StartCounterCollection();
}
private void StartCounterCollection()
{
var task = Task.Factory.StartNew(async () =>
{
var perfService = new PerfCounterService();
while (true)
{
var results = perfService.GetResults();
Clients.All.NewCounters(results);
await Task.Delay(2000);
}
}, TaskCreationOptions.LongRunning);
}
}
}
The code statement `Clients.All.NewCounters(results);` instructs SignalR to invoke a JavaScript function named `newCounters()` on every connected client and pass `results` object as parameter to it. The `All` member is a dynamic object which allow us to add any name. In the later version of SignalR, a new feature was added which allows a custom Hub to inherit from the base hub of specific type where all the members defined in this type is supposed to be implemented in JavaScript/client code.
So, defining a `IPerfHubClient` interface also provides us intellisense support in IDE otherwise there will be no support as `All` is a dynamic type. It’s definition is shown below:
namespace PerfSurf.Hubs
{
public interface IPerfHubClient
{
[HubMethodName("newCounters")]
void NewCounters(object results);
}
}
`HubMethodName` attribute on member instructs SignalR to convert pascal-case to camel-case when it creates the client-side methods on the fly for consumption. This is handy as writing a method name in camel-case on server-side doesn’t look right!
Let us look at `StartCounterCollection` implementation. We want our hub to start sending the counter values as soon as possible. So, we just start a new task which runs a while loop but with some delay. It consumes a custom counter service named `PerfCounterService` to get the results of CPU, Paging and Disk usage of server which is then sent to all the connected client using `Clients.All.NewCounters(results)`. Note that we also instruct that this task is a long-running task with the help of `TaskCreationOptions`.
`PerfCounterService` is a simple class that just setups the counters that the client needs as per the requirement of our application. It is good to keep this separate so that this counters can be added or removed as and when required without affecting the rest of the application. The definition of this class is as follows:
namespace PerfSurf.Counters
{
public class PerfCounterService
{
public PerfCounterService()
{
_counters = new List<PerfCounterWrapper>()
{
// CPU usage
new PerfCounterWrapper("Processor", "Processor", "% Processor Time", "_Total"),
// Paging on the server
new PerfCounterWrapper("Paging", "Memory", "Pages/sec"),
// Disk usage
new PerfCounterWrapper("Disk", "PhysicalDisk", "% Disk Time", "_Total")
};
}
List<PerfCounterWrapper> _counters;
public dynamic GetResults()
{
return _counters.Select(c => new { name = c.Name, value = c.Value });
}
}
}
It stores a list of counters to be used in app and `GetResults` method just returns a friendly name and a value for counter. It uses a custom counter wrapper type – the definition is mentioned below:
namespace PerfSurf.Counters
{
public class PerfCounterWrapper
{
private PerformanceCounter _counter;
public PerfCounterWrapper(string name, string category, string counter, string instance = "")
{
Name = name;
_counter = new PerformanceCounter(category, counter, instance, readOnly: true);
}
public string Name { get; set; }
public float Value
{
get { return _counter.NextValue(); }
}
}
}
The above class is a wrapper for `System.Diagnostics.PerformanceCounter` to define a friendly name, category, counter, instance for a counter. Note that all counter have different values for category, counter or instance. Look at the `PerfCounterService` to check what is required for different required counters.
As such this project is using ASP.NET MVC, we will next modify the `Home/Index.cshtml` view to have following code:
@{
ViewBag.Title = "Home Page";
}
<div class="row" data-bind="foreach: counters">
<div class="col-md-12">
<h2 data-bind="text: name"></h2>
<canvas width="800" height="100" data-bind="attr: { 'id': name }"></canvas>
</div>
</div>
@section scripts{
<script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
<script src="~/SignalR/hubs"></script>
<script src="~/Scripts/knockout-3.4.0.js"></script>
<script src="~/Scripts/smoothie.js"></script>
<script src="~/koapp/ChartEntry.js"></script>
<script src="~/koapp/viewmodel.js"></script>
<script src="~/koapp/app.js"></script>
}
Code from line 5-10 uses Knockout’s `foreach` binding to render the structure for all the counters defined in the model. We have 3 counters as per requirement. It then creates a canvas element for each counter with a unique id using counter’s name. This makes sure that our app is ready to support any number of counters sent from the server. Also this canvas is required to use Smoothie chart library so that it can stream data on it.
Next is the scripts section in the view. First, all the third-party scripts are references. The important one is on-line 15 – which doesn’t exist physically but when a request goes to `~/SignalR/hubs` then server responds with all the JavaScript code that it creates on the fly by going through all the hubs and its public methods to the browser.
Client-side Setup
Next we reference our custom JavaScript files.
`ChartEntry.ts` file is created to define a `ChartEntry` class internally uses SmoothieChart which defines a chart for a counter. SmoothieChart has a `timeSeries` which it uses to track a value at particular time. Instead of matching server & client time, we just use client-side time to append a counter’s value. The implementation of this type looks like this below:
/// <reference path="../scripts/typings/smoothie/smoothie.d.ts" />
module app {
export class ChartEntry {
name: string;
chart: any;
timeSeries: any;
constructor(name: string) {
this.name = name;
this.chart = new SmoothieChart({ millisPerPixel: 50, labels: { fontSize: 15 } });
this.timeSeries = new TimeSeries();
this.chart.addTimeSeries(this.timeSeries, { lineWidth: 3, strokeStyle: "#00ff00" });
}
addValue(value) {
this.timeSeries.append(new Date().getTime(), value);;
}
start() {
var canvas: HTMLCanvasElement = document.getElementById(this.name) as HTMLCanvasElement;
this.chart.streamTo(canvas);
}
}
}
Next, we define a `ViewModel` class in `ViewModel.ts` file that acts as a state for current view. This basically uses KnockoutJS for data-binding and looks like:
module app {
export class ViewModel {
counters: KnockoutObservableArray<any>;
constructor() {
this.counters = ko.observableArray([]);
}
addCounters(updatedCounters: any) {
$.each(updatedCounters, (index, updatedCounter) => {
var entry = ko.utils.arrayFirst(this.counters(), (counter: any) => (counter.name === updatedCounter.name));
if (!entry) {
entry = new ChartEntry(updatedCounter.name);
this.counters.push(entry);
entry.start();
}
entry.addValue(updatedCounter.value);
});
}
}
}
It is quite a simple class with `counters` member as an array that will hold all the counter values. As discussed above, the friendly name of every counter is used to identify whether the counter already exists or not. If it doesn’t exist then a new chart-entry is created and added to the counters array. And eventually, the value is added to the entry irrespective of whether entry is new or old.
The important bit is still remaining to glue SignalR work done on server with client-side setup. Add `app.ts` file that looks like snippet below:
module app {
// new up the perfHub connection and view-model for view
var perfHub = $.connection.perfHub;
var viewModel = new ViewModel();
// enabled logging to see in browser dev tools what SignalR is doing behind the scenes
$.connection.hub.logging = true;
// establish a connection; use promise resolve if needed
$.connection.hub.start();
perfHub.client.newCounters = counters => {
viewModel.addCounters(counters);
};
$(function () {
// when document is ready, use the viewModel to bind the document via knockout.
ko.applyBindings(viewModel);
});
}
If you use the code above in your project, you will see compile time errors for `perfHub`. TypeScript compiler isn’t happy as it can’t find any matching definition for it. To solve this, use Hubs.tt – a T4 Template that goes through all the registered Hubs and creates a typescript definition to be used from other typescript files. Paste this template file inside Scripts folder so that the references for SignalR and jQuery in the template matches with your physical files. Also, update line number 4 to match your version of SignalR.
If everything goes well, the compile time errors wherever `perfHub` is used will be solved. Not the method `newCounters` is camel-case as instructed in the server code above. When server instructs to call this method, client code then internally calls `addCounters` function defined in `ViewModel` class. Read the comments in the code above to see what each statement is doing to fulfil the requirements of our real-time web app.
If you have followed all the instructions in this post, you will now have a working real-time web application which will look like this:

Real-time web app showing Process, Paging and Disk usage of a server.
This will work well on IIS Express on your local machine but make sure to enable Web Sockets for IIS if you are using IIS 8 and above as by default it is turned off.
This sums up this post to write real-time web app using SignalR, TypeScript, KnockoutJS, HTML5. If you face any issues, let me know and I’ll try to help you. This just scratches the surface and there are many other great real-time usages that can be achieved using SignalR.
