Working with real time applications is a ton of fun. I recently built a web application that utilized microsofts SignalR.Net library to achieve near real-time functionality.

The client application was to be built with Angular-js, it made a lot of sense to use a library that gave us simple two-way data binding out of the box.

SignalR provides a jQuery 'plugin' that can be used to communicate with the server side 'Hubs'. The plugin does all the leg work of choosing the best Transport layer (WebSockets, Server Sent Events, Forever Frame and Ajax long polling) available, depending on the browsers abilities. It also gives you a nicely defined API of sending and receiving events to and from the Hub (server).

The one challenge was determining how to tie Angular and SignalR together.

Angular SignalR Service

The plugin alone wasn't going to be suitable to build out the application with Angular. I needed a service that could be shared across services and controllers within my application modules.

Multiple Hubs

SignalR allows you to break out your server side application code into multiple hubs. I needed a way to create an Angular service instance per hub, essentially a class that can be instantiated.

This led me to creating an Angular SignaR Service.

The service returns an object that can be instantiated and returned in an Angular factory recipe.

Code Example

A simple example would be creating a Message hub. The goal of the Message Hub is to receive a message from a client, then broadcast that message to all other connected clients (everyone except for the sender).

Server MessageHub Class

using Microsoft.AspNet.SignalR;

namespace SignalRwithAngular.Hubs  
{
    public class MessageHub : Hub
    {

        public void SendMessage(string message) {
            Clients.Others.addMessage(message);
        }

    }
}

Client Side Module

We're going to make use of the Angular SignaR Service module to create a MessageHub instance that can be used throughout the application.

HTML scripts to include

Here is a sample of what our script includes might look like:

    <body>
        <!-- left out for brevity -->
        <script src="angular.js"></script>
        <script src="service.hub.js"></script>
        <script src="app.js"></script>
        <script src="app.controller.js"></script>
    </body>
</html>  

In the app.js file we register our application module called, app with a dependency on the services.hub module.

(function (angular) {
    'use strict';

    // MessageHub depends on the HubProxy
    MessageHub.$inject = ['HubProxy'];
    function MessageHub(Hub) {
        var messageHub = new Hub('messageHub', { connectionPath: '/signalr', loggingEnabled: true });
        messageHub.start();
        return messageHub; // return MessageHub instance
    }

    angular.module('app', ['services.hub']).factory('MessageHub', MessageHub);

})(window.angular);

Great, we now have a MessageHub service that can be utilized throughout the application. Let's see what that may look like in our app.controller.js controller.

Message Controller

The MessageController is going to be responsible for a few things:

  • Manage and expose a list of messages to the view
  • Expose an sendMessage action method that can be invoked by the view
  • Invoke the sendMessage on the Hub
  • Listen for the addMessage event sent by the hub
(function (angular) {
    'use strict';

    MessageController.$inject = ['MessageHub'];
    function MessageController(MessageHub) {

        var self = this;

        // Message list that we'll expose to the View
        this.messages = [];

        // Message model exposed to the view, we'll bind to this in an input element
        this.message = '';

        // sendMessage action, that will handle the submit event in the view
        this.sendMessage = function () {
            // Add the message to the chat message list
            addMessage({ from: 'me', txt: this.message });

            // Send the message to the message hub
            MessageHub.invoke('sendMessage', this.message);

            // Clear the message model
            self.message = '';
        };

        // Internal Method that adds the message object to the message list
        function addMessage(message) {
            self.messages.push(message);
        }

        // Listen to the Hub event for new messages
        MessageHub.on('addMessage', function (message) {
            addMessage({ from: 'others', txt: message });
        });
    }

})(window.angular);

NOTE: I have to mention that a lot of this responsibility shouldn't really be in the controller, you would generally put the message queing, and Hub methods in a service. The Controller's main responsibility is to expose objects to the view and handle actions from the view.

Our View Layer

Let's take a look at the message view would look like.

<main ng-controller="MessageController as msgCtrl">  
  <form ng-submit="msgCtrl.sendMessage()">
    <input type="text" ng-model="msgCtrl.message" placeholder="Enter message" />
    <ul>
      <li ng-repeat="message in msgCtrl.messages">
      <strong>{{message.from}}</strong>: {{ message.txt }}
      </li>
    </ul>
  </form>
</main>  

What's going on?

Here we essentially have Angular instantiate our MessageController as msgCtrl

<main ng-controller="MessageController as msgCtrl">  

We then set the sendMessage action of the message controller to the ng-submit directive. This way, when the form is submitted, the action gets triggered.

<form ng-submit="msgCtrl.sendMessage()">  

We then, bind the controllers message property to the input element via the ng-model directive.

<input type="text" ng-model="msgCtrl.message" placeholder="Enter message" />  

Finanly, we loop through all the message items in the controllers' messages array property.

<li ng-repeat="message in msgCtrl.messages">  

Final words

This is a primitive example of how the Angular Service class can be used. There are additional benefits to the object, for example it provides methods that can be utilized to monitor the connection state of the Hub instance.

function MessageController(MessageHub) {  
   ...
   this.conn = MessageHub.connectionStatus;

   ...
}
<div class='alert alert-warning' ng-show="msgCtrl.conn.isReconnecting()">  
    We're experiencing connection issues and will reconnect shortly.
</div>  
<div class='alert alert-danger' ng-show="msgCtrl.conn.isDisconnected()">  
    We lost the connection. Try refreshing your browser?
</div>  

Example App

I creted a sample ASP.NET chat client application that essentially coveres how the service can be used.

It covers:

  • Using Multiple Hubs
  • Binding to the Hub connection status
  • Sending Hub events with promises
  • Receieving Hub events
Angular SignalR Service

https://github.com/alettieri/angular-signalr-service

Sample Applicationn

https://github.com/alettieri/angular-signalr-chat