Smartlet Event Model

Release 1.3 - ...

Smartlets can communicate state to other Smartlets, plain SXML and Smartlet instances living in a web client, using the Smartlet Event model.

In a web client, especially using libraries such as jQuery, sending and receiving custom events (custom as in 'non-DOM') is very straightforward. If only rich clients were to be supported, one might easily be tempted to keep it at that. Just use the trigger() and bind() methods in jQuery, and all is well...

In the Progressive Enhancement world however, there has to be a pure server-side counterpart. Without client-scripting, we have to be able to send and receive event data in order to open up a communication channel.

The solution we have come up with offers the best of both worlds: in rich clients, you will be able to trigger and bind to events just as easily as with plain jQuery, but without scripting, communication is still possible at the server level: Smartlets can make data available to other Smartlets that are executed at a later stage during server processing, and these Smartlets can then 'pick up' the event data:

Smartlet Event Flow

Basic Setup

When you want a Smartlet to create an event, you can do so server-side and/or client-side. We start by doing it server-side, because of the best practice of Progressive Enhancement: make it work without any scripting, then enhance it so that a richer user experience is created, but without making script required.

To simply make available a number of properties to the outside world, use smartlet.events.sendproperties(eventName, params string[] propertyNames). This method will make the properties available.

For example, a treeview Smartlet could send its current path and selected item:

Smartsite SXML CopyCode image Copy Code
{smartlet.events.sendproperties(smartlet.name(PathChange), path, activeitem)}

If you want more control, use smartlet.events.send(eventName, params object[] keyValuePairs)

Another Smartlet can then use smartlet.events.receive(eventName), which returns a DataTable with all exposed properties, or smartlet.events.receivevalue(eventName, propertyName), which gives access to a single event property:

Smartsite SXML CopyCode image Copy Code
{buffer.set(url, smartlet.events.receive('sml_treeview_PathChange', activeitem, default='/res/pixel.gif'))}

Other Smartlets, as well as SXML code outside Smartlets, can receive the events.

The only requirement for SXML code to receive events from Smartlets, is that the event sender must be rendered before any event listeners.

Rich Clients (Scripting)

With SCF installed, You can create an event from your Smartlet instance in the browser using smartlet.trigger()

For example, a treeview Smartlet can bind to a DOM click event on a node and send the PathChange event:

  CopyCode image Copy Code
var path=$j(this).attr('href');
sml.set('activeitem', path);
sml.trigger('PathChange', {activeitem: path});

The sml variable being a client Smartlet object.

Another piece of client code, whether in another Smartlet, or in a hosting page, can bind to the event:

  CopyCode image Copy Code
$j.scf.smartlet.bind('sml_treeview_PathChange', function(e){
   alert(e.args.activeitem);
});
  

Complex scenarios

You can place the smartlet.events.send() and smartlet.events.sendproperties() calls anywhere in your Smartlet SXML. The calls will simply make the properties passed available to the outside world, but what happens when the Smartlet sending the event is currently processing an Ajax callback request? Well, the Smartlet will incorporate the event data to send in the JSON response it will send back to the browser. SCF will then trigger a client side event automatically once the Ajax/JSON result is received.

Why doesn't this happen in normal rendering mode? Well, the reason is simply that your basic SXML handling can already capture the event data, as shown above, whereas it can not when an Ajax call is being made to a Smartlet, because no full roundtrip is made.

Resending event data

Consider a scenario where your Smartlet reacts on a client side event by another Smartlet, and needs to do an Ajax callback when the event comes in. This is actually not so strange: in the treeview case, consider a preview pane that lives side by side and has to lookup some data from the server each time the treeview changes its active item.

  CopyCode image Copy Code
$j.scf.smartlet.bind('sml_treeview_PathChange', function(e){
   jQ.parents('div.sml_box').find('.smlboxcapt').html(e.args.activeitem);
    sml.ajax({
      event: e,
      busy: true,
      ready: function(){
        jQ.html(sml.get('html'));
      }
   });
 });

The event property in the Ajax options passed to Smartlet.ajax() is used to directly pass the current event (passed to the event handler of the bind() method) to the Ajax call, allowing the server SXML code to use smartlet.events.receive() to get the data:

  CopyCode image Copy Code
{buffer.set(url, smartlet.events.receivevalue("sml_treeview_PathChange", activeitem))}

Return values in event listeners

When binding to an event, you may notify the event sender by returning a value in the bind() handler:

Send an event before closing a Smartlet box.

  CopyCode image Copy Code
If(sml.trigger('sml_MySmartlet_Close'))!=false){
   j.parents('.sml_box').slideUp();
};

In a listener, return false if the user says not to close the box...

  CopyCode image Copy Code
$j.scf.smartlet.bind('sml_MySmartlet_Close', function(e) {
   return confirm('Are you sure you want to close MySmartlet?');
});