ChatR: just another chat application using SignalR

Standard

Heads up!

The blog has moved!
If you are interested in reading new posts, the new URL to bookmark is http://blog.valeriogheri.com/

 

Update (30 November, 2012): David Fowler (SignalR co-creator) has made ChatR into an example application downloadable via Nuget to show SignalR beginners how to quickly build a chat web app.
He forked the project on GitHub and updated the code base to the latest version of the library, 1.0Alpha2, and then sent me a pull request that I accepted and merged into the master branch.
Therefore the code that you see now in the latest commit on the master branch is slightly different than the one shown in this post, but this is not a problem, you just need to go back in time in the history of the commits to find exactly what is shown here if you really want to, otherwise just dive into the new code!

Update (17 November, 2012): this blog article is related to version 0.5.2. In these days the SignalR team is working on version 1.0 and has released an Alpha. There are quite a lot of significant changes in the API, but the concepts at the basis still apply.
I hope to be able to post an update article when 1.0 is released.

There’s a lot of buzz these days around SignalR (http://signalr.net/) and more in general around real time communication techniques for the web (http://www.webrtc.org/).
There are many scenarios where real time can be embedded in web applications in a way that is actually useful for the user, but the most easy to understand scenario is the good old chat :)
That’s why today I’m going to write about…

Goal

A chat web application! Yes, I know it’s not the most original example, but it’s still a good starting point to excercise the fundamentals of the framework.
The user will be able to choose a username and then join the (single room) chat, where he/she will be able to chat with the others and see a list of currently connected users.
This list will obviously update itself everytime someone joins or disconnect the chat.

Source code and demo

As always the code for this tutorial is published on GitHub at https://github.com/vgheri/ChatR
A working demo is deployed on the fantastic AppHarbor at http://chatr-1.apphb.com  (if you want to run a test, simply load up new tabs… SignalR is not relying on Sessions so it will be safe). 

Frameworks

This project relies on Asp.Net MVC 3 for the general architecture and the basic client-server communication.
As anticipated, I’ll be using SignalR for real time client-server communication, Twitter’s Bootstrap for basic UI layout and CSS and Knockout Js (knockoutjs.com) to automatize the view.

Before starting, I’d like to spend a few words about SignalR: it’s growing up as a solid library to use in order to build production code and it’s dead easy to use (https://github.com/SignalR/SignalR/wiki) once you grasp the fundamentals (which I’ll cover later on). It not only gets the job done, but also sports an awesome client-server async communication pattern, that makes really easy to invoke methods from Js to C# and viceversa (there is even someone who proposed to use it for general purpose communication http://www.novanet.no/blog/yngve-bakken-nilsen/dates/2012/7/clean-up-your-mvc-app-with-signalr/)
If you are interested in understanding more about the internals, I suggest you clone the repository on GitHub (https://github.com/SignalR/) and read something like http://ayende.com/blog/93185/reviewing-signalrndash-part-ii.

Implementation

This application will feature a 1-click process to join the chat:

Step 1: landing page with a form to enter username
Step 2: chat page

The landing page is really simple, just a few text elements and an HTML form with an input text and a submit button that will trigger a HTML POST handled by the server via a MVC action.

Models

I’m going to use 2 objects as Domain models and 1 repository object to store users currently connected to the system.
Let’s see them briefly:
- ChatUser has two properties, Id and Username. The former uniquely identifies a client during a session and is assigned by SignalR.
- ChatMessage is super easy and has a minimal set of properties: username (I could have used the ChatUser object, but because I’m displaying just a username and not the user id, I chose to transfer the minimum set of information), content and timestamp.
- InMemoryRepository implements the Singleton pattern and helps clients retrieve the list of users.

Basic UI structure

Super simple and clean: a title bar on the top of the page that holds the name of the app, a container on the left that holds the usernames of currently connected users, a container, side by side with the previous one, that holds the chat messages and below an input box to enter a message with a submit button.
All the client-server communication round-trips here are handled by SignalR.
To dynamically update the UI, I’ve used Knockout Js and its data-binding declarations.

Javascript namespace and Knockout view models

I chose to create a separate Js file to declare a namespace and the Knockout viewmodels.
Therefore I created a separate folder called “Js” and a javascript file called chatR.js.
The code is pretty straightforward, as you can see:

The rest of the javascript code is inside chat.cshtml as I needed to access the Model binding engine to retrieve the username chosen by the user.

SignalR

Server side code

Let’s start with the server side part of SignalR.
Do  you remember what I mentioned earlier in the post, that this library sports a great communication pattern that makes really easy to call C# methods from Javascript and viceversa? Well, the C# methods that we can call from the client side are those declared (as public) in the so called Hub.
What is a Hub? Well, a Hub is an abstraction that, to cite the Wiki,  “provides a higher level RPC framework over a PersistentConnection. If you have different types of messages that you want to send between server and client then hubs is recommended so you don’t have to do your own dispatching”.
SignalR offers two options to build a RTC app, leaving to the developer the choice between two built-in objects: Persistent Connections and Hubs.
It took me a while to fully understand what are the differences between these two, and why choose one over another.
Basically Hubs allow you to dispatch whatever object you want to the server, taking care of JSON serialization and deserialization, integrate model binding and are easier to setup, as you won’t have to write additional code to manually register additional routes (the SignalR specific routes). (You may want to read this question on SO, with words from David Fowler, creator of SignalR:  http://stackoverflow.com/questions/9280484/signalr-why-choose-hub-vs-persistent-connection).
I think it’s pretty safe to say that if you are writing a common web application or .Net application, you’ll be fine with Hubs.

N.B It’s imporant to note that Hubs are created on a “per request” basis, so no static stuff in it should be declared.

To start with, create a Hubs folder and create a new class that derives from Hub.
Here I called mine “ChatHub” and I defined the methods that will be used by the clients:
- GetConnectedUsers: retrieves the list of currently connected users
- Joined: fired when a new client joins the chat. We add the user to the repository and notify all the clients (even the newly joined) that this new client connected.
- Send: broadcasts the message to all the clients (plus some extra work, like formatting links and youtube urls embedded in the messages, showing them as active links and embedded video player respectively. This part is mostly taken by the Jabbr codebase, thanks again David Fowler :) )

Here’s the code:

As you can see, there’s something more: this class implements an interface called IDisconnect, part of SignalR, which allows us to handle the Disconnect() event fired when a user leaves the application. Again what we do here is remove the user from the repository and notify the other clients of the event.
There is another interface, called IConnected, that allows us to catch connection events like Connect and Reconnect, but I chose not to use them because unfortunately (at least in Connect) the round-trip state (see https://github.com/SignalR/SignalR/wiki/SignalR-JS-Client-Hubs) is not available, therefore I cannot access the state set on the Hub with information like the username set on the client that caused the event to fire.
Later on I’ll explain in more detail with code.
This is also the reason why I have a Joined event handler, because I need an event handler, fired after the Connect event, where I can access the round-trip state. Just for the record, Jabbr uses the same philosohpy, not implementing IConnected.
Finally, as you can see almost all of the methods have a void return type, but one: GetConnectedUsers has a ICollection<ChatUsers> return type and this means that only the client invoking the method will get the data back and furthermore the library will handle the JSON serialization and deserialization of the collection.
Alternatively I could have used Caller.callback and set a void return type to the method.

Client side code

On the client side, the first important thing to note is the order of the script references:
1) jQuery 2) jQuery.signalR 3) <script src=”signalr/hubs” type=”text/javascript”></script>

The last one is important because navigating to /signalr/hubs will dynamically generate the script based on the hubs declared on the server. Each hub on the server will become a property on the client side $.connection, e.g. $.connection.myHub.

Let’s see now how to hook up the client and the server:

It’s important to note that all Hub names (methods and class name) are in  camelCase, so for example
var chatHub = $.connection.chatHub;   is creating the proxy to invoke server code.

The next statement,
chatHub.username = currentUser.username;
is setting the round-trip state on the Hub, in this case I’m setting the username so that it will be accessible from the server and I’ll be able to add this user to the list of currently connected clients.

Following we have client-side event handlers, as invoked by the server (remember? Clients.onMessageReceived(message); )

We also apply knockout bindings so that the UI will start updating itself as the ViewModels change.
Finally we have the code block to start the SignalR connection and invoke two Hubs method:

// Step 1: Start the connection
// Step 2: Get all currenlty connected users
// Step 3: Join to the chat and nmotify all the clients (me included) that there is a new user connected
$.connection.hub.start()
            .done(function () {
                  chatHub.getConnectedUsers()
                              .done(function (connectedUsers) {
                                   ko.utils.arrayForEach(connectedUsers, function (item) {
                                      users.contacts.push(new chatR.user(item.Username, item.Id));
                                   });
                               }).done(function () {
                                    chatHub.joined();
                               });
             });

method $.connection.hub.start provides two callbacks, done and fail.
So in the code above I’m saying that if the connections is successfully estabilished, we can now try to retrieve the list of connected users and if this worked too, we can notify all the clients that this new client connected via chatHub.joined().
At this point the round-trip state will be available and therefore we will be able to register a new User, along with its connection ID and username.

One final note: by default DateTime is serialized by SignalR using ISO8601 format (http://en.wikipedia.org/wiki/ISO_8601), which is ok to parse for Chrome and latest browsers, but, guess what?, not for IE 9 and older versions. That’s why I had to search the web for a nice and quick solution, that you can find in the source code on GitHub.

Useful links

ChatR on GitHub: https://github.com/vgheri/ChatR
ChatR on AppHarbor: http://chatr-1.apphb.com
SignalR Wiki: https://github.com/SignalR/SignalR/wiki/Hubs
Hubs vs Persistent Connections: http://stackoverflow.com/questions/9280484/signalr-why-choose-hub-vs-persistent-connection
Useful tutorial: http://www.amazedsaint.com/2011/11/introduction-ksigdo-knockout-signalr-to.html
Jabbr on GitHub: https://github.com/davidfowl/JabbR

About these ads

17 thoughts on “ChatR: just another chat application using SignalR

  1. I am new to asp.net, Can you please explain clearly… the “Javascript namespace and Knockout view models” Section. In which I am confused when you declared directly “chatR.chatMessage = //function ” after “var chatR = {};”. Without defining what is “chatMessage”..

    I am stucked there itself in your tutorial :)
    Thanks in advance

  2. Hello, I’m sorry if I haven’t been clear on that point. Those lines of code are basically just creating a new empty object called chatR (var chatR = {}) and then declaring a new method inside chatR, called chatMessage.
    chatMessage is a function that can then be invoked along with the “new” keyword to act as a C# constructor: var myMessage = new chatR.chatMessage(…).
    In javascript you can define object like this, because javascript is a dynamic object. Have you ever used the dynamic keyword in C#? It’s somewhat similar for this kind of usage.
    KnockoutJs is a javascript library to help you automatize the user interface by providing bindings between your DOM elements and your javascript view models (a javascript viewModel for example is declared in the chatR javascript file we saw before), so that when you change something in the UI, the update is auomatically reflected in the javascript view model. It’s really cool and if you are interested, you can take a look at the Knockout website, it’s full of useful tutorials!
    Tell me if you need more help

  3. Great article! Very well explained!
    I only take a doubt … why when you need to assign an event to a jQuery object whatsoever, I can only do this using # id, for example: $ (“# object”.) click ({…}), and not so $ (“. cssClass” .) click ({…}) (via anchoring class)? Are there any restrictions on the basis of knockoutjs? (I’m still lay over knockoutjs lol) :)

    Again congratulations tutorial.

    • Hello Igor,

      thanks for your kind words!
      I’m not sure to fully understand your question but knockout poses no restrictions to jQuery whatsoever. The only thing is that if you want to subscribe to a element click directly using knockout databinding, then yes you need to bind to an element and not a class.
      Example: Click me

      If this is not what you were asking, then try explaining your question with an example please.
      Valerio

      • Ok I understand! Well here we go: I need that when the user clicks on the name of a User List () open a particular window to chat! Only I need to anchor this event in items, ie … via class (class = “user-box-name”). I tried to do the following:

        Sample Code:

        Users

        $ (“. user-box-name”). click (function () {
             alert (anything);
        });

        And no success!

        Now tried to anchor the same event in div (id = “users-list”) thus

        $ (“# users-list”). click (function () {
             alert (anything);
        });

        And it worked, even though it takes Naoe. I need this event to be triggered separately, according to the name that the user clicked.

        Given to understand anything I wrote up there? lol

        Sorry my english is bad, I used the google translator I’m Brazilian!

        Hugs!

        Igor.

      • Sorry forgot the code I’m talking about (Please do not approve this comment, or if possible complement my previous). Thank you!

                
                     Users
                    
                        
                    
                

  4. Igor

    Hello again guy (sorry for the pollution of comments, then delete them if deemed necessary). I found with a quick search that knockout attaches the click event on an object as follows (in our case):

    But I need to create a function in the viewModel, but do not know how to do … this function viewModel happens is that javascript that you created separately? (chatR)

    Looking forward to good news, if posssível! :)

    Cheers!

  5. KnockMan

    Hey friend, excellent tutorial!

    How can I call a function that is within ‘document.ready’ through the file chatR.js detailed form:

    chatR.user = function (username, userId) {
         var self = this;
         self.username = username;
         self.id = userId;
         self.ObjectClicked = function (id) {
             CallFunctionOnDocumentReady ();
         };
    }

    hope you can help me.

    Thank you!

    • Well, if withing document.ready you are doing
      var user = new chatR.user(…), you can imagine to add a parameter to the user constructor that is this function you want to call.
      But before doing that, consider a code refactoring because you are mixing stuff here.

  6. KnockMan

    This is a solution:

    Create a function inside (document.ready), using viewmodel ‘users’

    $(function() {
    users.funcFromDocReady = function(e) {
    alert(e.username);
    };
    })

    And call this:

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s