Module Hooks

Modules are the real stars of your TerrificJS application. All of your JavaScript code will find its place in one of the modules. In regard to this fact you will find yourself spending most of your time writing modules.

Each module has two opportunities to hook itself into the module initializing process. All hook methods (called phases) are optional – so you don't have to implement them, if you don't need them. They are called in the same order as below.

on

/**
 * Hook function to do all of your module stuff.
 *
 * @method on
 * @param {Function} callback function
 * @return void
 */
on: function(callback) {
    callback();
}

This phase is the most common and important hook method. All code related you your module is going inside this method. Here you can load additional scripts and styles, manipulate your DOM, bind events to it and breathe life into your module. The power is yours!

The on hook is the first module hook. It is used all over the place. Often it is even the only implemented hook in a module.

To ensure proper module encapsulation – and therefore to avoid side effects – you only have to use $ctx as the second parameter in all of your selectors.

on: function(callback) {
    var $ctx = this.$ctx,
        self = this;

    $('.encapsulated', $ctx).on('click', function() {
        console.log('I am the encapsulated button of module ' + self.id);
    });

    callback();
}

after

You want to provide a default state for your module? This is the phase for triggering your bound events.

/**
 * Hook function to trigger your events.
 *
 * @method after
 * @return void
 */
after: function() {

}

The after hook is the second and last module hook. The purpose of the after phase is to provide a default state to your module by triggering the bound event handlers.

Why should you trigger your event handlers in the after and not directly in the on phase? To answer this question you need to know a little bit more about the module initialization process.

The on phase is not synchronized among all modules, so the initialization of the modules happens pseudo parallel until the last stage of the process – the after phase – has been reached. In other words: until the after phase has been reached, each module runs isolated and do not care about the state of the other modules.

Why do we need the after phase to be synchronized among all modules? In some cases, a triggered event notifies other modules over a Connector about its state. Depending on the action to perform, an error may occur, if one of the notified modules has not finished its on phase yet. To prevent such problems, this last initialization stage is synchronized among all modules.

That's it…

Other Methods

fire - stay in touch with your connected modules

/**
 * Notifies all attached connectors about changes.
 *
 * @method fire
 * @param {String} event The event name
 * @param {Object} data The data to provide to your connected modules (optional)
 * @param {Array} channels  A list containing the channel ids to send the event to (optional)
 * @param {Function} defaultAction The default action to perform (optional)
 */
fire: function(event, [data], [channels], [defaultAction]) {

The fire method is your communication device. Stay in touch with your connected modules!

Connectors provides you an easy and concise way to connect your modules. But once connected, how can they talk with each other? The fire method is your communication device!

Example: Talk about the current mood

Given the case that connected modules want to make smalltalk about their current mood. Smalltalk should be easy… so it is with TerrificJS ;-)

In your «talking» module

on: function(callback) {
    var self = this,
    $ctx = this.$ctx;

    $('.talk', $ctx).bind('click', function() {
        self.fire('changeMood', { mood : 'perfect' }, function(data) { $.log.debug('yeah... really'); });
    });

    callback();
}

In your «receiving» module

onChangeMood: function(data) {
    $.log.debug('... oh you are in a ' + data.mood + ' mood?');
}

The logging output of the conversation above is:

... oh you are in a perfect mood?
yeah... really

Example: Shut up your smalltalk partner

Sometimes you want to prevent – as with native JS events – the default action of an event. For example if you are not in the mood for smalltalk ;-)

In your «receiving» module

onChangeMood: function(data) {
    $.log.debug('... oh you are in a ' + data.mood + ' mood? So, because everything seems to be ok - there is no need for a smalltalk...');
    return false;
}

Assuming that the talking module remains the same, the logging output is:

... oh you are in a perfect mood?
So, because everything seems to be ok - there is no need for a smalltalk...

So by simply returning false, the receiving module can make a veto to suppress the execution of the defaultAction.

Example: Be selective with your smalltalk partners

If you have a look at the signature of the fire method again, you see that there is an optional channels parameter. In some cases you are connected to several connection channels, but you only want to talk over a specific one.

In your «talking» module

on: function(callback) {
    var self = this,
    $ctx = this.$ctx;

    this.sandbox.subscribe('VIP', this); // subscribe to the VIP channel

    $('.talk', $ctx).bind('click', function() {
        self.fire('changeMood', { mood : 'perfect' }, ['VIP'], function(data) { $.log.debug('Yes sir!'); });
    });

    callback();
}

By specifing that you only want to talk over the VIP channel, you can be seletive by choosing your smalltak partners.

Additional Infos

Module Properties

Every module has a small set of generally available properties. Ever wanted to know how TerrificJS ensures capsulation? Then this is for you!

/**
 * Initializes the Default module.
 *
 * @method init
 * @constructor
 * @param {jQuery|Zepto} $ctx the context
 * @param {Sandbox} sandbox the sandbox to get the resources from
 * @param {Number} id the unique module id
 */
init: function($ctx, sandbox, id) {
    // call base constructor
    this._super($ctx, sandbox, id);
}

In the snippet above you see that the three originally received params – $ctx, sandbox and id – are passed straight to the constructor of the base Module. This properties are accessible from your other module methods by simply calling this.$ctx, this.sandbox or this.id. But what are this properties containing?

  • $ctx

    The $ctx property contains the Module context. Accordingly to the TerrificJS naming conventions this is the DOM Node with the CSS class .mod. To ensure proper module encapsulation – and therefore to avoid side effects – you only have to use $ctx as the second parameter in all of your selectors. Please have a look at the example in the on phase.

  • sandbox

    The sandbox property contains the Sandbox instance that is shared amongst all of your TerrificJS modules. See the Sandbox in action!

  • id

    The id property contains the unique module id. This unique identifier is additionally exposed in the DOM as data-id attribute. This can be useful ie. in combination with the getModuleByIdmethod of the sandbox.

Inheritance

All modules extends the base class Tc.Module. The base class ensures that your Module is a welcomed guest in your TerrificJS application and provides you several useful methods and opportunities to hook yourself into the module initializing process.

Basic structure of a TerrificJS Module

(function($) {
    /**
     * Default module implementation.
     *
     * @author Remo Brunschwiler
     * @namespace Tc.Module
     * @class Default
     * @extends Tc.Module
     */
    Tc.Module.Default = Tc.Module.extend({

        /**
         * Initializes the Default module.
         *
         * @method init
         * @constructor
         * @param {jQuery|Zepto} $ctx the jquery context
         * @param {Sandbox} sandbox the sandbox to get the resources from
         * @param {String} modId the unique module id
         */
        init: function($ctx, sandbox, modId) {
            // call base constructor
            this._super($ctx, sandbox, modId);
        },

        /**
         * Hook function to do all of your module stuff.
         *
         * @method on
         * @param {Function} callback function
         * @return void
         */
        on: function(callback) {
            callback();
        },

        /**
         * Hook function to trigger your events.
         *
         * @method after
         * @return void
         */
        after: function() {
        }
    });
})(Tc.$);

Looking at the default structure you see that TerrificJS use the Simple JavaScript Inheritancefrom the jQuery inventor John Resig. This inheritance pattern has a very small footprint and is very intuitive to use. In the next section I only explain the TerrificJS relevant aspects oft this pattern. If you want to know more details please read the original blog post from John.

init

/**
 * Initializes the Default module.
 *
 * @method init
 * @constructor
 * @param {jQuery|Zepto} $ctx the context
 * @param {Sandbox} sandbox the sandbox to get the resources from
 * @param {String} modId the unique module id
 */
init: function($ctx, sandbox, modId) {
    // call base constructor
    this._super($ctx, sandbox, modId);
}

The init method takes the role of the constructor in Johns pattern. So all code that you normally write in your constructor function goes here.

_super

Most often – at least in TerrificJS – the only line of code in your constructor will be the call to the constructor of the base Module. In Johns pattern you can – like in other languages – access a parent method by simply calling _super. This not only works for the constructor as in the snippet above, but also for every other method that needs access to its parent functionality. Though in TerrificJS you will not need this feature quite often – it is at least good to know ;-)

Close