Building a Chat application in less than 100 lines of SXML code

Introduction

The smartsite iXperion Smartlet Architecture and Client Framework (SCF) introduce new and exciting tools for developers to build state-of-the-art, flexible, robust and scalable applications. To prove this point, let's take it to the test and build something that truly takes advantage of this new platform: a Chat Room.

Now, where to start? The point of a Chat Room is actually quite simple: present the user with a responsive and easy to use user interface where he/she can type lines of text that appear in a conversation pane. This conversation pane should update frequently to reflect activity by other users in the same Chat Room.

It will be clear that a Chat application over HTTP will require heavy use of Ajax in order to be responsive and scalable.

User Interface

Ok, let's start by simply building the user interface and figure it all out from there...

Somewhat simplified, here's the form we will use, that presents the user interface elements we need:

Smartsite SXML CopyCode image Copy Code
<form method="post" class="CoolForm">
<div>
    <label class="Control" for="history">Conversation</label>
      <div class="Control ChatHistory" id="history"></div>
      <br />
      <label class="Control" for="speak">Speak up</label>
      <input size="70" class="Control Speak" name="speak" id="speak" />
      <br /><label></label>
      <input class="Control Send" type="submit" id="send" value="Refresh" />
      <input class="Control Clear" type="submit" id="clear" value="Clear" />
    </div>
</form>

Required Components

What we need to do now is build some system that will allow sending the data to the server, maintain state on the server, and poll the server regularly in order to check for new entries posted by other chat-users.

How are we going to tackle this? Let's find out which components will be needed to fulfil the tasks:

  • Storage
    We can use very flexible datatable storage for keeping track of the conversation. Datatables provide the structure needed to store the conversation lines with all their metadata, such as user and timestamp of the entry.
  • State
    The cache macro, with its site-global state, can be used for keeping the conversation datatable in memory over time.
  • Traffic
    It's clear that Ajax will be used for data interchange between the browser and the server.
  • Interaction
    The glue needed to send the Ajax requests and update the user interface will of course be jQuery scripting.

Storage

First, let's build up the data structure. The placeholder macro provides an easy interface to datatable storage, with the added convenience of delayed rendering. We can set up a datatable, then add data to it and render (parts of) it later on.

Now, what fields do we need to store?

  1. Date/time of each user-entry
  2. User name
  3. Message text

Also, for keeping track of each user's last message added, we need a numbering field. This field can be increased with each entry, passed to each client, and when polling for new messages in the conversation, only newer records need to be retrieved from the server:

Finally, we need to add administrative messages, so let's add a boolean field that indicates that.

The placeholder will then look something like this:

Smartsite SXML CopyCode image Copy Code
<se:placeholder id="chat" >
   <se:parameters>
      <se:parameter name="fieldnames"><se:collection>
        <se:member name="Nr" type="integer" primarykey="true" />
        <se:member name="Created" type="datetime" />
        <se:member name="User" type="string" />
        <se:member name="Text" type="string" />
        <se:member name="SystemMsg" type="boolean" />
       </se:collection></se:parameter>
     </se:parameters>
</se:placeholder>

State

To keep the conversation datatable in scope, we use a cache macro that just wraps around the placeholder:

Smartsite SXML CopyCode image Copy Code
<se:cache save="key = this.getcachekey()" 
   maxage="00:30:00" resulttype="none" trim="both" >
<se:placeholder localid="chat" render="false" >
    <se:parameters>
       <se:parameter name="fieldnames"><se:collection>
         <se:member name="Nr" type="integer" primarykey="true" />
         <se:member name="Created" type="datetime" />
         <se:member name="User" type="string" />
         <se:member name="Text" type="string" />
         <se:member name="SystemMsg" type="boolean" />
        </se:collection></se:parameter>
      </se:parameters>
   </se:placeholder>
</se:cache>

The cache will live for a maximum of 30 minutes and we save a key buffer to

Traffic

Smartsite iXperion SCF has implemented Ajax in a very efficient way, using jQuery as client interface and JSON (JavaScript Object Notation) for data packaging. JSON allows for compact yet very flexible data transfer. This is ideal for this type of application.

Interaction

Clientside jQuery scripting will drive the user interface. An SCF behavior will be attached to the form, allowing for easy interaction with the server-side Smartlet.

Before we can dive deeply into the Behavior code used for the Chat Room, we'll have to understand the basics of SCF Behavior.  

Smartsite SXML CopyCode image Copy Code
<div class="enlarge">Click here!</div> 

<se:placeholder.addjavascript> 
$j.scf.behavior.add("EnlargeBehavior", {
attach: function(elm, settings, jq){
    jq.css({cursor: "hand"});

    jq.click(function(){
     jq.animate({height:100, border: "2px solid"}, 200);
   });
   }, 
   autoAttach: "div.enlarge"
});
</se:placeholder.addjavascript>

The behavior shown here automatically attaches to all divs with a class enlarge.

The $j variable is the global reference to the jQuery object, and $j.scf is the entry point for all client-side functionality in the SCF library, which is written as jQuery extensions.
Behaviors are written as calls to $j.scf.behavior.add(), and have a unique name in the first argument, and a JSON object with a number of properties in the second argument. The required attach property is used to write code that will be executed when the behavior is attached to any element, the optional autoAttach property can be used to directly state which elements to attach to.

Looking at the attach property, we can see that it must be a function, and when the Behavior Runtime calls it, 3 arguments are passed to it: element, settings and jQuery object. The jQuery object passed to the attach function gives direct access to the jQuery sub-selection.

In this case, divs with the enlarge class are enlarged using jQuery animation when a user clicks on them.

The autoAttach property can be handy, since it allows you to directly attach to the given jQuery selector, but you have to take into account that this advantage also comes with less flexibility: you cannot specify any settings for the behavior when automatically attaching.

Here's an alternative to the example above, without autoAttach, but with settings:

Smartsite SXML CopyCode image Copy Code
<div class="enlarge">Click here!</div> 

<se:placeholder.addjavascript> 
$j.scf.behavior.add("EnlargeBehavior", {  
  attach: function(elm, settings, jq){   
    jq.css({cursor: "hand"});
    jq.click(function(){
     jq.animate({height: settings.height, border: "2px solid"}, 200);
    });
   }
});
</se:placeholder.addjavascript>

{scf.behavior.attach('div.enlarge', 
'EnlargeBehavior', 'height', 200)}

There is an attach() Viper method in the scf.behavior class that accepts a list of settings passed as key-value pairs. These settings are then converted into a client-side JSON object and passed to the attach function.

The complete picture

We've seen some of the components we'll use for the app, but how to put it all together in the form of a Smartlet? Here goes:

First, we'll have to create a Smartlet item and write a Smartlet macro. Any Smartlet macro will have the following skeleton:

Smartsite SXML CopyCode image Copy Code
<se:smartlet>
 <se:parameters>
  <se:parameter name="properties">
   <se:collection>
    <se:member name="..." />
    <se:member name="..." />
   </se:collection>
  </se:parameter>
  <se:parameter name="xml">
   <se:if expression="smartlet.isajaxcallback()">
    <se:then>
      (SXML code to execute on Ajax callbacks)
    </se:then>
    <se:else>
      (SXML code to execute on initial rendering)
    </se:else>
   </se:if>
  </se:parameter>
 </se:parameters>
</se:smartlet> 

The Chat macro is no different. First, the interface of the Smartlet is described in the properties parameter. This is a collection holding the parameters that may be passed to the Smartlet call (server-side), in the form of translation arguments, and parameters that can travel over the line (via Ajax) to and from the browser. In the case of the Chat Room Smartlet, a parameter that could be passed to the Smartlet in the Viper call would be the refreshinterval to use for polling for new messages on the server.

Smartsite SXML CopyCode image Copy Code
<se:member name="refreshinterval" type="integer" >5000</se:member>

Parameters that travel between client and server must be declared as such, with the type of access specified. An example would be the phrase parameter, which holds the text line that a user adds to the conversation. In this case, the client should be able to read and write it, and the server should have read access. This is then specified like this:

Smartsite SXML CopyCode image Copy Code
<se:member name="phrase" callbackaccess="read" clientaccess="readwrite"></se:member> 

Download

View the complete code for the Smartlet, and the SCF scripting that is needed.

Download the complete import file for the Chat Room application.

Topics