Marionette allows you to define a module within your application, including sub-modules hanging from that module. This is useful for creating modular, encapsulated applications that are split apart in to multiple files.
Marionette's modules allow you to have unlimited sub-modules hanging off of your application, and serve as an event aggregator in themselves.
- Basic Usage
- Starting And Stopping Modules
- Defining Sub-Modules With . Notation
- Module Definitions
- The Module's
this
Argument - Custom Arguments
- Splitting A Module Definition Apart
A module is defined directly from an Application object as the specified name:
var MyApp = new Backbone.Marionette.Application();
var myModule = MyApp.module("MyModule");
MyApp.MyModule; // => a new Marionette.Application object
myModule === MyApp.MyModule; // => true
If you specify the same module name more than once, the first instance of the module will be retained and a new instance will not be created.
Modules can be started and stopped independently of the application and of each other. This allows them to be loaded asynchronously, and also allows them to be shut down when they are no longer needed. This also facilitates easier unit testing of modules in isolation as you can start only the module that you need in your tests.
Starting a module is done in one of two ways:
- Automatically with the parent module (or Application)
.start()
method - Manually call the
.start()
method on the module
In this example, the module will be started automatically with the parent
application object's start
call:
MyApp = new Backbone.Marionette.Application();
MyApp.module("Foo", function(){
// module code goes here
});
MyApp.start();
Note that modules loaded and defined after the app.start()
call will still
be started automatically.
When starting a module, a "before:start" event will be triggered prior to any of the initializers being run. A "start" event will then be triggered after they have been run.
var mod = MyApp.module("MyMod");
mod.on("before:start", function(){
// do stuff before the module is started
});
mod.on("start", function(){
// do stuff after the module has been started
});
.start
takes a single options
parameter that will be passed to start events and their equivalent methods (onStart
and onBeforeStart
.)
var mod = MyApp.module("MyMod");
mod.on("before:start", function(options){
// do stuff before the module is started
});
mod.on("start", function(options){
// do stuff after the module has been started
});
var options = {
// any data
};
mod.start(options);
If you wish to manually start a module instead of having the application start it, you can tell the module definition not to start with the parent:
var fooModule = MyApp.module("Foo", function(){
// prevent starting with parent
this.startWithParent = false;
// ... module code goes here
});
// start the app without starting the module
MyApp.start();
// later, start the module
fooModule.start();
Note the use of an object literal instead of just a function to define
the module, and the presence of the startWithParent
attribute, to tell it
not to start with the application. Then to start the module, the module's
start
method is manually called.
You can also grab a reference to the module at a later point in time, to start it:
MyApp.module("Foo", function(){
this.startWithParent = false;
});
// start the module by getting a reference to it first
MyApp.module("Foo").start();
There is a second way of specifying startWithParent
in a .module
call, using an object literal:
var fooModule = MyApp.module("Foo", { startWithParent: false });
This is most useful when defining a module across multiple files and
using a single definition to specify the startWithParent
option.
If you wish to combine the startWithparent
object literal
with a module definition, you can include a define
attribute on
the object literal, set to the module function:
var fooModule = MyApp.module("Foo", {
startWithParent: false,
define: function(){
// module code goes here
}
});
Starting of sub-modules is done in a depth-first hierarchy traversal.
That is, a hierarchy of Foo.Bar.Baz
will start Baz
first, then Bar
,
and finally `Foo.
Submodules default to starting with their parent module.
MyApp.module("Foo", function(){...});
MyApp.module("Foo.Bar", function(){...});
MyApp.start();
In this example, the "Foo.Bar" module will be started with the call to
MyApp.start()
because the parent module, "Foo" is set to start
with the app.
A sub-module can override this behavior by setting it's startWithParent
to false. This prevents it from being started by the parent's start
call.
MyApp.module("Foo", function(){...});
MyApp.module("Foo.Bar", function(){
this.startWithParent = false;
})
MyApp.start();
Now the module "Foo" will be started, but the sub-module "Foo.Bar" will not be started.
A sub-module can still be started manually, with this configuration:
MyApp.module("Foo.Bar").start();
A module can be stopped, or shut down, to clear memory and resources when
the module is no longer needed. Like starting of modules, stopping is done
in a depth-first hierarchy traversal. That is, a hierarchy of modules like
Foo.Bar.Baz
will stop Baz
first, then Bar
, and finally Foo
.
To stop a module and it's children, call the stop()
method of a module.
MyApp.module("Foo").stop();
Modules are not automatically stopped by the application. If you wish to
stop one, you must call the stop
method on it. The exception to this is
that stopping a parent module will stop all of it's sub-modules.
MyApp.module("Foo.Bar.Baz");
MyApp.module("Foo").stop();
This call to stop
causes the Bar
and Baz
modules to both be stopped
as they are sub-modules of Foo
. For more information on defining
sub-modules, see the section "Defining Sub-Modules With . Notation".
When stopping a module, a "before:stop" event will be triggered prior to any of the finalizers being run. A "stop" event will then be triggered after they have been run.
var mod = MyApp.module("MyMod");
mod.on("before:stop", function(){
// do stuff before the module is stopped
});
mod.on("stop", function(){
// do stuff after the module has been stopped
});
Sub-modules or child modules can be defined as a hierarchy of modules and sub-modules all at once:
MyApp.module("Parent.Child.GrandChild");
MyApp.Parent; // => a valid module object
MyApp.Parent.Child; // => a valid module object
MyApp.Parent.Child.GrandChild; // => a valid module object
When defining sub-modules using the dot-notation, the parent modules do not need to exist. They will be created for you if they don't exist. If they do exist, though, the existing module will be used instead of creating a new one.
You can specify a callback function to provide a definition
for the module. Module definitions are invoked immediately
on calling module
method.
The module definition callback will receive 6 parameters:
- The module itself
- The Parent module, or Application object that
.module
was called from - Backbone
- Backbone.Marionette
- jQuery
- Underscore
- Any custom arguments
You can add functions and data directly to your module to make them publicly accessible. You can also add private functions and data by using locally scoped variables.
MyApp.module("MyModule", function(MyModule, MyApp, Backbone, Marionette, $, _){
// Private Data And Functions
// --------------------------
var myData = "this is private data";
var myFunction = function(){
console.log(myData);
}
// Public Data And Functions
// -------------------------
MyModule.someData = "public data";
MyModule.someFunction = function(){
console.log(MyModule.someData);
}
});
console.log(MyApp.MyModule.someData); //=> public data
MyApp.MyModule.someFunction(); //=> public data
Modules have initializers, similarly to Application
objects. A module's
initializers are run when the module is started.
MyApp.module("Foo", function(Foo){
Foo.addInitializer(function(){
// initialize and start the module's running code, here.
});
});
Any way of starting this module will cause it's initializers to run. You can have as many initializers for a module as you wish.
Modules also have finalizers that are run when a module is stopped.
MyApp.module("Foo", function(Foo){
Foo.addFinalizer(function(){
// tear down, shut down and clean up the module, here
});
});
Calling the stop
method on the module will run all that module's
finalizers. A module can have as many finalizers as you wish.
The module's this
argument is set to the module itself.
MyApp.module("Foo", function(Foo){
this === Foo; //=> true
});
You can provide any number of custom arguments to your module, after the module definition function. This will allow you to import 3rd party libraries, and other resources that you want to have locally scoped to your module.
MyApp.module("MyModule", function(MyModule, MyApp, Backbone, Marionette, $, _, Lib1, Lib2, LibEtc){
// Lib1 === LibraryNumber1;
// Lib2 === LibraryNumber2;
// LibEtc === LibraryNumberEtc;
}, LibraryNumber1, LibraryNumber2, LibraryNumberEtc);
Sometimes a module gets to be too long for a single file. In this case, you can split a module definition across multiple files:
MyApp.module("MyModule", function(MyModule){
MyModule.definition1 = true;
});
MyApp.module("MyModule", function(MyModule){
MyModule.definition2 = true;
});
MyApp.MyModule.definition1; //=> true
MyApp.MyModule.definition2; //=> true