Cacomania: Node.js Chat client with jQuery Mobile

Cacomania

Node.js Chat client with jQuery Mobile

Guido Krömer - 25. November 2012 - Tags: , ,

Welcome to the last part of my node.js chat tutorial, the topic of this part is the jQuery Mobile based client. The decision choosing jQuery Mobile has the two reasons, the UI I created for "WebSockets chat with Node.js " was really ugly because I'm not a designer, so I decided taking something which looks well out of the box. The second reasons was that jQuery Mobile seems to be well tested on different browsers even if they are not WebKit based. If you have not read the server side tutorial you maybe should read it before.

The jQuery Mobile chat client
This screen shots show how the chat client will look.

node.js Code reuse

In theory it would be easy to reuse code written for node.js in the front end, too. One problem I had reusing some code is the usage if the require() function in node.js which works synchronous. Loading stuff in the browser is asynchronous which made it impossible to rewrite the require() function which works 100% equally to the node.js one. One possibility, as a kind of workaround for the asynchronous behavior, was to use a callback if a require had finished. The code below shows my FakeRequire.js script I used in the client.

The usage in the client below shows how the callback is used to ensure all files have been loaded before initializing the chat.

$(document).ready(function() {
    require('ChatClient.js', function() {
        require('Message.js', function() {
            initChat();
        });
    });
});

Form validation

For validating the different forms I used jquery.validate, Elijah Mano wrote a excellent Article about get it working with jQuery Mobile: "jQuery Mobile Form Validation". The login form below makes use of the validation, validation the Server input field only needs to add the two classes: "required" and "url".

<form id="login_form" class="validate">
  <div data-role="fieldcontain">
    <fieldset data-role="controlgroup">
      <label for="chatHost">Server</label>
      <input name="server" id="chatHost" placeholder="http://127.0.0.1" value="" type="text"  class="required url"/>
    </fieldset>
  </div>
  <div data-role="fieldcontain">
    <fieldset data-role="controlgroup">
      <label for="chatPort">Port</label>
      <input name="port" id="chatPort" placeholder="8888" value="" type="text"  class="required digits"/>
    </fieldset>
  </div>
  <div data-role="fieldcontain">
    <fieldset data-role="controlgroup">
      <label for="nickname">Nickname</label>
      <input name="nickname" id="nickname" placeholder="" value="" type="text"  class="required"/>
    </fieldset>
  </div>
  <input data-theme="b" data-icon="check" data-iconpos="left" value="Login" type="submit" />
</form>

The screen shot was taken using the Firefox OS Simulator and shows the form validation in action: jQuery Mobile Form Validation in Firefox OS

JSONP

I wanted a client which would communicate with different servers even if the client source url differs with the node.js server url, therefore the client does not make use of Ajax. The calls are in JSONP. The principle is really simple the server does not answer with a XML document or JSON the response is a function call with a JSON object as param. If you never heard about JSONP take a look at the example below, at page load the following script gets loaded (http://cacodaemon.de/tutorials/jsonp_example.js) which contains the following code: jsonpCallback({msg: 'Hello from cacodaemon.de'}), the jsonpCallback() function is already defined and replaces the "Loading Text" with the response message.

Instead of reinventing the wheel by writing the JSONP call code myself I used jQuerys ajax() method. By defining the 'dataType' the call will ends into a JSONP call with one problem, it does not know anything about error handling a timeout has to be used to simulate this behavior. If the call was successfully the timeout gets stopped otherwise a chat client onError() will be risen.

var request = function (msg) {
    var timeoutId = setTimeout(function () {
        new module.exports.ChatClient().stopInterval();
        new module.exports.ChatClient().onError();
    }, 1000);

    var jsonpRequest = {
        url: new module.exports.ChatClient().host,
        cache: false,
        dataType: 'jsonp',
        jsonp: 'ChatResponse',
        async: false,
        success: function(jsonp){
            clearTimeout(timeoutId)
            processResponse(jsonp)
        },
    };

    if (msg) {
        jsonpRequest.data = {q: encodeURIComponent(JSON.stringify(msg).toString())};
    }

    $.ajax(jsonpRequest);
};

The ChatClient class

The client server communication gets handled by the ChatClient class. To keep the code portable the dom manipulation and form handling is separated the jQueryMobileChatClient.js script which calls the this.sendMessage(msg) method for sending new messages and overrides one the following events fired by the ChatClient:

this.onLogin = function() {};
this.onMessage = function(msg) {};
this.onPrivateMessage = function(msg) {};
this.onSend = function() {};
this.onDoLogin = function() {};
this.onUsers = function(users) {};
this.onNameChange = function(params) {};
this.onNewUser = function(params) {};
this.onError = function() {};

Since Ajax or JSONP is limited to polling an interval runs three times a second requesting the server for new messages, if the server has no new messages he will return an empty array. The interval gets started by a 'welcome' command send be the server, to prevent multiple interval running at the same time startInterval() will always stop an existing interval.

this.startInterval = function(time) {
    var time = time ? time : 333;

    this.stopInterval();

    interval = setInterval(this.getMessages, time);
};

The ChatClient is build as singleton which makes the handling easier and prevents that multiple clients running at the same time. The code below causes all instantiations with new module.exports.ChatClient() will work with the same instance of the ChatClient class.

if (module.exports.ChatClient.instance) {
    return module.exports.ChatClient.instance;
}

module.exports.ChatClient.instance = this;

The processResponse() and processCommand() methods are very similar to the server process() and command() methods and raises the events corresponding with the message received from the server.

Here is the whole ChatClient code:

jQueryMobile HTML

The "app.html" file contains the whole client layout. The different pages are defined in div elements with the attribute data-role="page", displaying a different page can be done using the following command $.mobile.changePage('#page_login');. Some pages are displayed as a dialog, this has been achieved by stetting the data-rel="dialog" to the button. This means every page can be displayed as full screen or dialog, too. If you plan to play around with jQuery Mobile on their page you might find a really cool editor, based on codiqa, which helped me allot building the page skeleton. jQuery Mobile Chat Client page flow

The diagram above show the page flow beginning with the login (page_login), chatting (page_chat) displaying the logged in users (page_users), sending private messages (page_private_message) and changing the user name (page_nickname). The screen shots where taken using the Opera Mobile Emulator, a really nice tool.

Here is the whole chat client html code, please note that the library's like jQuery should be loaded from a CDN during productive usage, but is really unhandy being depended to a Internet connection when developing in the train :P.

The jQueryMobileChatClient.js script

The last part is connecting the ChatClient class with the static html from the app.html file.

Keeping the code clean has been achieved using my sprintf in JavaScript implementation and predefining some templates like this one: var newUserTemplate = '<li><p>User: <strong>%</strong> logged in.</p></li>';.

The forms submitting is disabled, they will be only checked for being valid and some custom JS gets executed like in the form below.

$('#change_name_form').submit(function () {
    if (!$('#change_name_form').valid()) {
      return false;
    }

    chatClient.sendMessage(new module.exports.Command('change_name', {
      newName: $('#new_nickname').val()
    }))

    $.mobile.changePage('#page_chat');

    return false;
  });

The event stubs from the ChatClient are overwritten with some custom code fitting to the jQuery Mobile client. The onPrivateMessage and onMessage events are handled equally. The msg param of the event gets used in the template. The new html code gets appended to the chat_area listview which has to be refreshed after appending a new element. The $(document).scrollTop($(document).height()); just scrolls to the page bottom.

 chatClient.onPrivateMessage = chatClient.onMessage = function (msg) {
    $('#chat_area').append( msgTemplate.sprintf(
                  msg.type == 'normal' ? 'c' : 'b',
                  new Date(msg.time).prettyTime(),
                  msg.user.id,
                  msg.user.name,
                  msg.text));
    $('#chat_area').listview('refresh');
    $(document).scrollTop($(document).height());
  }

Here is the complete code from the jQueryMobileChatClient.js file.

Thats all folks, I hope you enjoyed reading this tutorial.

The whole client as gist