Module

The module is the basic unit of the Kube architecture. Modules bind to the element on the page and contain the application logic.

How to create a module #

First, you need to create the basic module wrapper and the init method, which will be launched at the module start.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;
    }
});

As you can see the init method contains two arguments:

  1. app is the object of the entire application, through which you can access the Kube services and the global methods of the application.
  2. context is the module's environment object, please see the details about Context.

Now add some method to your new module.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // build some logic
        this._build();
    },
    // public
    myMethod: function()
    {

    },
    // private
    _build: function()
    {

    }
});

It's all. The module and its logic are created.

Bind module to the element #

Now it's time to initialize the module. To do this, you must bind it to the element on the page using the data-kube attribute with the module name.

<div data-kube="mymodule"></div>

The module can be bound to any number of elements on the page. But one element can have only one module.

<div data-kube="mymodule"></div>
<span data-kube="mymodule"></span>

Ok, when the element is on the page and the application is started with $K.init method the module init method will also be launched.

Let's see how to get the module's environment and the element to which it is bound.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // getting context and the module element
        this.context = context;
        this.$element = context.getElement();

        // build
        this._build();
    },
    _build: function()
    {
        this.$element.addClass('myclass');
    }
});

Starting and stopping module #

The module init method is started if the module element is found on the page. But this method is used to initialize and declarations of properties and services of the module. To start the module logic, it's better to use the start method, which runs automatically when the application is launched, i.e. after initialization.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // getting context and the module element
        this.context = context;
        this.$element = context.getElement();
    },
    // public
    start: function()
    {
        this._build();
    },
    // private
    _build: function()
    {
         this.$element.addClass('myclass');
    }
});

To execute the module stop logic, you can add the stop method. This method will be launched automatically, if the entire application is stopped, see App API.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // getting context and the module element
        this.context = context;
        this.$element = context.getElement();
    },
    // public
    start: function()
    {
        this._build();
    },
    stop: function()
    {
        this.$element.off('.kube.mymodule');
        this.$element.removeClass('myclass');
    },
    // private
    _build: function()
    {
        this.$element.on('click.kube.mymodule', this._toggle.bind(this));
    },
    _toggle: function(e)
    {
        this.$element.toggleClass('myclass');
    }
});

Set module options #

You can specify the module settings using data attributes.

<div data-kube="mymodule" data-value="2" data-second-value="true"></div>

The settings will be read by the module and available in the module context.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // context
        this.context = context;
        this.params = context.getParams();
    },
    myMethod: function()
    {
        if (this.params.value === 2)
        {
            // do something
            console.log(this.params.secondValue);
        }
    }
});

Here is the way to set some default options for the module:

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // defaults
        var defaults = {
            value: 0,
            secondValue: false
        };

        // context
        this.context = context;
        this.params = context.getParams(defaults);
    },
    myMethod: function()
    {
        if (this.params.value === 2)
        {
            // do something
            console.log(this.params.secondValue);
        }
    }
});

In this case, the default settings will be replaced with those values that were specified in the data attributes. If data attributes were not specified, then the values will remain default.

Target element #

The target is a way to work with a specific element that is outside the module. Bind the module to the element and set the target option.

// module trigger
<button data-kube="mymodule" data-target="#mytarget">Toggle</button>

// target
<div id="mytarget" class="is-hidden"></div>

Here is the example how to work with the target element in the module.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // getting context, the module element and the target
        this.context = context;
        this.$element = context.getElement();
        this.$target = context.getTarget();
    },
    start: function()
    {
        this._build();
    },
    stop: function()
    {
        this.$element.off('.kube.mymodule');
    },
    // private
    _build: function()
    {
        this.$element.on('click.kube.mymodule', this._toggle.bind(this));
    },
    _toggle: function(e)
    {
        if (this.$target.hasClass('is-hidden'))
        {
            this.$target.removeClass('is-hidden');
        }
        else
        {
            this.$target.addClass('is-hidden');
        }
    }
});

Use services in the module #

Services are sets of methods and tools that you can use in your modules. Before using the services, they must be defined at the module initialization.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // define services
        this.utils = app.utils;
        this.animate = app.animate;

        // context
        this.context = context;
        this.$element = context.getElement();
    },
    start: function()
    {
        var $el = this.$element.find('span');

        // use service
        this.animate.run($el, 'fadeOut');
    }
});

Services are supposed to be reused by multiple modules. Therefore, if there are functions in your modules that are repeated, then it is better to put them into the service. See how to create services.

Internal events #

Modules can handle events of elements within their container.

<div data-kube="mymodule">
    <input type="text" data-type="type">
    <button data-type="close">Close</button>
</div>

Add the data-type attribute with the event type and catch the DOM event in the module.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;

        // context
        this.context = context;
        this.$element = context.getElement();
    },
    onclick: function(e, element, type)
    {
        if (type === 'close')
        {
            e.preventDefault();
            this.$element.addClass('is-hidden');
        }
    },
    onfocus: function(e, element, type)
    {
        if (type === 'type')
        {
            $K.dom(element).addClass('is-type');
        }
    }
});

Each event receives the following arguments.

Arguments
e Object event object
element Node node of the element in which the event occurred
type String the type of event specified in the data-type attribute

Here is the full list of all supported internal events.

  • click
  • mouseover
  • mouseout
  • mousedown
  • mouseup
  • mousemove
  • keydown
  • keyup
  • focus
  • submit
  • change
  • contextmenu
  • input

Cross-module events #

Cross-module events are the way of communicating between modules. To create an event, you need to use the this.app.broadcast.

$K.add('module', 'mymodule', {
    init: function(app, context)
    {
        this.app = app;
    },
    start: function()
    {
        // create event
        this.app.broadcast('mymodule.started', this);
    }
});

Module events are sent simultaneously and automatically of two types:

  1. common event
  2. named event from a particular module with an ID or with data-name, if it is specified as an attribute of the element.

To send the named event you need to set the data-name attribute or ID for the module element.

<div data-kube="mymodule" data-name="myname";></div>

Then you can capture events in another module.

$K.add('module', 'myanothermodule', {
    init: function(app)
    {
        this.app = app;
    },
    // catch event
    onmessage: {
        mymodule: {
            started: function(sender)
            {
                // caught
            }
        }
    }
});

Or the named event for the specified module.

$K.add('module', 'myanothermodule', {
    init: function(app)
    {
        this.app = app;
    },
    // catch event
    onmessage: {
        mymodule: {
            started: function(sender)
            {
                if (sender._id === 'myname')
                {
                    // caught
                }
            }
        }
    }
});

Commands #

Commands is a way to send and catch actions that should occur when you click on elements inside the module.

<div data-kube="mymodule">
    <a href="#" data-command="mymodule.add" data-id="1" data-add="New item">Add</a>
    <a href="#" data-command="mymodule.remove" data-id="1">Remove</a>
</div>

Let's see how to capture commands in your another module.

$K.add('module', 'myanothermodule', {
    init: function(app, context)
    {
        this.app = app;
    },
    // catch commands
    onmessage: {
        mymodule: {
            add: function(sender, $element, args)
            {
                console.log('Add action');
            },
            remove: function(sender, $element, args)
            {
                console.log('Remove action');
            }
        }
    }
});

In other words, a command is an option to create cross-module events on the fly when you click on items inside the module.

Scope of internal events #

Sometimes, the module element may be inside another module. To ensure that internal events do not overlap, use the module name as a prefix to determine the scope.

<div data-kube="mymodule">
    <p>
        <button data-type="mymodule.add">Add</button>
    </p>
    <p data-kube="myinternalmodule">
        <button data-type="myinternalmodule.add">Add</button>
        <button data-type="myinternalmodule.remove">Remove</button>
    </p>
</div>