Sandbox

Gives your Modules the power to communicate with the Application.

The Sandbox instance is shared among all Modules and is the only way for them to communicate with the Application. It provides a set of functionality that needs knowledge of either all involved Module instances or the Application itself.

addModules

Thinking of Ajax and the module markup you may retrieve - How would you apply JavaScript functionality to these snippets? addModules provides you exactly that - the possibility to easily add modules dynamically.

/**
 * Adds (register and start) all modules in the given context scope.
 *
 * @method addModules
 * @param {jQuery|Zepto} $ctx the context.
 * @return {Array} a list containing the references of the registered modules.
 */
addModules: function($ctx) {

addModules is a very handy method that allows you to register and start new modules by simply passing a jQuery/Zepto object containing the module(s) DOM as the one and only parameter.

With ie. widget systems or lazy loading techniques in mind this turns into a very powerful utility.

Example: Add new widgets

This code could ie. be placed in the on phase of your widget chooser module.

// Make the module instance available for closures
var self = this;

// Bind the click event to add a new widget
$('.addWidget', this.$ctx).on('click', function() {
    var modules = [];

    $.ajax({
        url: '<url to get the markup of the widget(s)>',
        success: function(data) {
            var $data = $(data);

            // if needed: connect the widget(s) to the appropriate connector channels
            $('.mod', $data).data('connectors', ["1"]);

            // register and start the modules in $data
            modules = self.sandbox.addModules($data);

            // add them to the DOM
            $('.body').append($data);
        }
    });

    return false;
});

The snippet above registers and starts the found modules in $data. addModules returns an array containing the instances of the found Modules. Isn't that simple?

removeModules

/**
 * Removes (stop and unregister) the modules by the given module instances.
 *
 * @method removeModules
 * @param {Array} modules a list containting the module instances to remove.
 */
removeModules: function(modules) {

removeModules is the opposite of addModules. As parameter you have to pass in an array containing the Module instances (ie. the return value of addModules or getModuleById) or the jQuery/Zepto object to look for registered modules in.

The given modules are stopped and unregistered. In other words: everything gets cleaned up - all references to the Modules are being deleted and all attached data and events are being removed from the Module DOM. So after that, the modules are only plain markup again - without any functionality.

Example: A widget removes itself

This is code could ie. be placed in the on phase of the widget.

// Make the module instance available for closures
var self = this;

// Bind the click event to remove the widget itself
$('.removeWidget', this.$ctx).on('click', function() {
    // stop and unregister the module itself
    self.sandbox.removeModules([self]);

    // remove it from the DOM
    self.$ctx.remove();

    return false;
});

After running the above, it is like the widget never existed.

Notice: Removing all attached data and events from the module DOM is an expensive operation, because every single Node has to be checked! This expensive operation takes place in the stop method of the base Module (the one that all of your Modules inherit from). If, like in the above example, the module is getting removed from the DOM afterwards, this operation is not necessary because your library cleans up automatically.

In this case it would be better to overwrite the stop method in the widget. As a best practice we recommend you to overwrite the stop method in your Module and unbind your bound events and attached data manually.

Example: Overwriting the stop method

In your module could do something like:

stop: function() {
    var $ctx = this.$ctx;

    // unbind the previously bound click event
    $('.removeWidget', $ctx).off('click');

    // remove all data attributes from the module node
    $ctx.removeData();
}

This unbinds your previously bound click event. Furthermore, all TerrificJS specific data attributes get removed from the module node.

subscribe / unsubscribe

Normally connection channels are established by using the Connector Naming Conventions. However, in some cases a Module needs full control over its communication channels.

Example: Manage your connection channels

This code could ie. be placed in the on phase of your module.

// Make the module instance available for closures
var self = this;

// Click event on subscribe button
$('.subscribe', this.$ctx).on('click', function() {
    // subscribe to channel 1
    self.sandbox.subscribe(1, self);
});

/// Click event on unsubscribe button
$('.unsubscribe', this.$ctx).on('click', function() {
    // unsubscribe from channel 1
    self.sandbox.unsubscribe(1, self);
});

getModuleById

Connectors allow you to loosely couple your Modules. Although, in some rare cases you need a bit more flexibility and control. If so - getModuleById is your choice!

/**
 * Gets the appropriate module for the given id.
 *
 * @method getModuleById
 * @param {int} id the module id
 * @return {Module} the appropriate module
 */
getModuleById: function(id) {

In most cases your Modules will communicate with each other over Connector channels (or custom events if they better fit your needs). Using Connectors lets you couple your Modules loosely, which is great because your application will not break if one of the Modules does not exist on a page.

Despite the fact that getModuleById breaks with the loose coupling principle, it can be very useful in some cases where you need a bit more flexibility and control over your Modules

getModuleById makes it possible to access other Module instances directly without communicating over connectors.

Example: Status polling

Think of an application where one module (ie. modStatus) takes care of all states of the involved Modules. One solution for this problem could be:

  1. Wire all Modules together by using the same Connector id
  2. As soon as a Module changes its state, it sends the new state to the status Module

This is perfectly fine and there is nothing wrong with this solution. But there are a few things to consider:

  • You have to take care, that every Module has the correct Connector id.
  • If you are using the default Connector, the state changes will not only be sent to the status Module. In fact it is sent to all connected Modules. Though this problem would be avoidable by using a custom Connector that only send the changes to the status Module.
  • Each Module is responsible for notifying the status Module about its state changes.

And now the solution with getModuleById. This solution does not need any Connectors, custom events or something similar. Instead you only have to make sure, that every Module implements a getStatus method that returns the current state. So in your observable Module you could write something like:

getStatus: function() {
    return this.state;
}

All of the other work is done from the state Module itself. It loops over all Modules and polls their current state when needed.

collectStates: function() {
    var self = this,
    $ctx = this.$ctx,
    states = [];

    // loop through all modules on the page
    $('.mod', $ctx).each(function() {
        var $mod = $(this);
        var instance = self.sandbox.getModuleById($mod.data('id'));

        // check whether the module is observable
        if($.isFunction(instance.getStatus)) {
            // add the current state to the states array
            states[$mod.data('id')] = instance.getStatus();
        }
    }

    return status;
}

the Connector solution is not better or worse than the one using getModuleById – though personally i would prefer the former for this kind of problem – its simply different and both of them have their pros and cons.

getConfig

This method simply allows you to access your config from within your Modules.

/**
 * Gets the application config.
 *
 * @method getConfig
 * @return {Object} the configuration object
 */
getConfig: function() {

getConfigParam

Unsurprisingly, getConfigParam returns the corresponding config param for the given identifier.

/**
 * Gets an application config param.
 *
 * @method getConfigParam
 * @param {String} name the param name
 * @return {mixed} the appropriate configuration param
 */
getConfigParam: function(name) {
Close