5 minutes read

In this article we’ll explore events, keypaths and observers in RactiveJS environments. The application we’re going to build will comprise of a single RactiveJS instance which calls a remote web-service and injects received data into a predefined template.

The source code for this article is here.

And a LiveDemo.

This is how our app looks like:

RactiveJS Events

Unsurprisingly, RactiveJS offers a Publish/Subscribe system to react to, or trigger events. A typical event declaration would be like this:

<script id=”some-template type=”text/ractive”>

<input type=”button” on-click=”save-button-was-clicked“>

</script>

The part in red declares a RactiveJS event directive which reacts to DOM’s click-event by forwarding the information through a proxy-event “save-button-was-clicked“. Additionally we need some JavaScript logic to react to it:

ractive.on(‘save-button-was-clicked‘, function(e){

e.original.preventDefault(); //the proxy event contains the “original” event object.

console.log(“Save Button was clicked!”);

});

The method on is one of many built-in Ractive instance methods and is used to register event handlers.

There exist another possibility to react to events: by directly calling methods from Ractive instances. In such cases we’d write the call within our template:

<input type=”button” on-click=”callMe()”>

Our Ractive instance must contain a method of the same name:

var ractive = new Ractive({

callMe: function(){

console.log(“Hello”);

}

});

It’s also possible to pass arguments. For example:

<input type=”text” value={{someValue}}>

<input type=”button” on-click=”consumeValue(someValue)“>

Here the value someValue would be passed via direct call of Ractive’s consumeValue method.

The difference between the two event types is that event handling via proxy events uses the publish-subscribe infrastructure while direct method calls avoid it. The publish-subscribe scheme acts like a bridge between raw DOM events and Ractive’s event system. This is not the case when a raw event is used to call an instance method.

Custom Events

In most situations the built-in events are sufficient but Ractive offers a way to define and register additional events. These can be declared via the same on-* pattern like other event directives. Ractive maintains a collection of events in the Ractive.events structure and whenever it reads an on-* declaration entry it performs a lookup for its definition in Ractive.events. If the search was unsuccessful it assumes the name part (without “on-“) in the declaration refers to a raw DOM event. The advantage of using custom events is that they can be used like any other events without treating them differently.

Let’s create a new mouse click event called click2.

First we register a new custom event by adding its logic to Ractive.events. Every custom event definition must provide a function with two arguments: the node in the HTML document that receives the event and the callback function to be triggered for this particular event.

Ractive.events . click2 = function( node , fire ){ var click2Handler ;

click2Handler = function( event ){ //take the original event event .preventDefault(); //prevent default behaviour

fire ({ //call the provided callback and feed it with original event data node: node , //+ some custom stuff only this event can provide original : event , x: event.clientX, y: event.clientY, extra: 'this is some extra data only I can provide' }); };

node . addEventListener ('click', click2Handler); //let the node know we take care of clicks, too return { //finally, return an object with a clean-up-method onteardown : function(){ //which will be called when DOM node is removed console.log('click2 event removed'); node . removeEventListener ('click2', click2Handler); } }; };

In our template we declare the new custom event like any other:

<input class=”query-button” type=”button” value=”Click2″ on-click2=”click2-in-action“>

The Ractive definition contains a proper event handler for click2:

ractive.on(‘click2-in-action‘, function(e){

e.original.preventDefault();

console.log(‘Extra data: ‘ + e.extra);

});

Keypaths

As we saw in the examples above RactiveJS retrieves information about certain pieces of data by using keypaths. For example the automatic update of DIVs inside the template happens via keypaths results.user.firstName, results.user.lastName and so on. Later we’ll see that the same technique is used to observe data changes. By definition a keypath is a string representing the location of some piece of data. The location we point to can either be represented via dot-notation or with square brackets (array notation). The latter is useful when we deal with arrays, for example:

data: function(){

return {

entries: [“one”,”two”,”three”,”four”,”five”]

}

}

To retrieve the second entry we could use: ractive.get(‘entries[1]’); or ractive.get(“entries.1”);

Observers

Unsurprisingly, data changes over time. But sometimes we’d like to be informed about certain changes or even automatically react to changes. To implement such behavior RactiveJS utilizes observers. An observer in RactiveJS is a mechanism that monitor changes within a certain keypath. To define an observer we use ractive.observe() API:

ractive.observe(‘results.user.firstName’, function(newValue, oldValue, keypath){

console.log(“Value in keypath: ” + keypath + ” was changed from: ” + oldValue + ” to: ” + newValue);

});

Now any change of results.user.firstName will trigger the registered callback. You can also change a value via browser console by executing ractive.set() API.

It is worth noting that there exist specialized variants of “on” and “observe” methods: once and observeOnce. These are used when a particular event or data change should only be treated once.