UNPKG

k.backbone.marionette

Version:
506 lines (359 loc) 13.7 kB
## [View the new docs](http://marionettejs.com/docs/marionette.module.html) # Marionette.Module Marionette Modules allow you to create modular encapsulated logic. They can be used to split apart large applications into multiple files, and to build individual components of your app. ## Documentation Index * [Basic Usage](#basic-usage) * [Module Definitions](#module-definitions) * [Callback Function Definition](#callback-function-definition) * [Object Literal Definition](#object-literal-definition) * [Module Classes](#module-classes) * [Defining Sub-Modules](#defining-sub-modules) * [Starting and Stopping Modules](#starting-and-stopping-modules) * [Starting Modules](#starting-modules) * [Module Initializers](#module-initializers) * [Start Events](#start-events) * [Preventing Auto-Start Of Modules](#preventing-auto-start-of-modules) * [Starting Sub-Modules With Parent](#starting-sub-modules-with-parent) * [Stopping Modules](#stopping-modules) * [Module Finalizers](#module-finalizers) * [Stop Events](#stop-events) ## Basic Usage A module is defined directly from an Application object. To create a module all you need to do is give it a name. ```js var MyApp = new Backbone.Marionette.Application(); // Creates a new module named "MyModule" var myModule = MyApp.module("MyModule"); MyApp.MyModule; // => a new Marionette.Module object myModule === MyApp.MyModule; // => true ``` Modules cannot be overwritten once they are created. Subsequent calls to `module` with the same name argument will not create a new module, but instead return the already-created instance. ```js var MyApp = new Backbone.Marionette.Application(); // Instantiates a new Marionette.Module var myModule = MyApp.module("MyModule"); // Returns the module you just created var theSameModule = MyApp.module("MyModule"); ``` ## Module Definitions You can provide a definition for your module when you instantiate it. Definitions can either be a callback function or an object literal. ### Callback Function Definition The callback function definition will be invoked immediately on calling the `module` method. It will receive 6 parameters, in this order: * The module itself * The Application object * Backbone * Backbone.Marionette * jQuery * Underscore * Any custom arguments Within the callback you can attach both private and public functions and data directly to your module. ```js MyApp.module("MyModule", function(MyModule, MyApp, Backbone, Marionette, $, _){ // The context of the function is also the module itself this === MyModule; // => true // 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 ``` #### Additional Arguments You can provide additional arguments to the definition function, allowing you to import 3rd party libraries and other resources that you want to have locally scoped to your module. Pass the additional arguments after the definition itself in the call to `module`. ```js MyApp.module("MyModule", function(MyModule, MyApp, Backbone, Marionette, $, _, Lib1, Lib2, LibEtc){ // Lib1 === LibraryNumber1; // Lib2 === LibraryNumber2; // LibEtc === LibraryNumberEtc; }, LibraryNumber1, LibraryNumber2, LibraryNumberEtc); ``` #### Splitting A Module Definition Apart Sometimes a module definition can become quite long. You can split apart the definition by making subsequent calls to the `module` function. This can used to split the definition of your module across multiple files. ```js MyApp.module("MyModule", function(MyModule){ MyModule.definition1 = true; }); // The following could be in a separate file MyApp.module("MyModule", function(MyModule){ MyModule.definition2 = true; }); MyApp.MyModule.definition1; //=> true MyApp.MyModule.definition2; //=> true ``` ### Object Literal Definition The object literal definition of a module allows for more flexibility than the callback method. It allows you to, for instance, specify a custom class for your module. Through the object literal definition you can still set a definition function through the `define` property. ```js MyApp.module("MyModule", { define: function(MyModule, MyApp, Backbone, Marionette, $, _) { // Define your module here } }); ``` #### Specifying a Custom Module Class One of the more useful features of the object literal definition is specifying a custom module class. You can make a new class using the extend function. ``` var CustomModule = Marionette.Module.extend({ // Custom module properties }); MyApp.module("Foo", { moduleClass: CustomModule, define: function() {} // You can still use the definition function on custom modules }); ``` When `moduleClass` is omitted Marionette will default to instantiating a new `Marionette.Module`. #### Initialize Function Modules have an `initialize` function which is immediately called when the Module is invoked. You can think of the `initialize` function as an extension of the constructor. The initialize function is only available through the object literal definition of a Module. ```js MyApp.module("Foo", { startWithParent: false, initialize: function( moduleName, app, options ) { this.someProperty = 'someValue'; }, // You can still set a define function define: function( Foo ) { console.log( this.someProperty ); // Logs 'someValue' } }); ``` The `initialize` function is passed the same arguments as the constructor. * The moduleName * The app * The object literal definition of the Module itself (which allows you to pass arbitrary values to your Module) ```js MyApp.module("Foo", { initialize: function( moduleName, app, options ) { console.log( options.someVar ); // Logs 'someString' }, someVar: 'someString' }); ``` The initialize function is distinct from the `define` function. The primary difference between the two is that `initialize` is on the prototype chain, whereas `define` is not. What this means is that `initialize` can be inherited. ```js var CustomModule = Marionette.Module.extend({ define: function() {}, // This is not inherited and will never be called initialize: function() {} // This, on the other hand, will be inherited }); ``` ## Module Classes Module classes can be used as an alternative to the define pattern. The extend function of a Module is identical to the extend functions on other Backbone and Marionette classes. This allows module lifecyle events like `onStart` and `onStop` to be called directly. ``` var FooModule = Marionette.Module.extend({ startWithParent: false, constructor: function(moduleName, app, options) { }, initialize: function(moduleName, app, options) { }, onStart: function(options) { }, onStop: function(options) { }, }); MyApp.module("Foo", FooModule); ``` If all of the module's functionality is defined inside its class, then the class can be passed in directly. `MyApp.module("Foo", FooModule)` ## Defining Sub-Modules Sub-Modules (or 'child' Modules) can be defined in a single call by passing a period-separated list of Modules to be created. ```js 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'll be created for you. If a parent has already been instantiated then that instance will be used. ## Starting And Stopping Modules 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 unit testing of modules as you can start only the module that you need in your tests. ## Starting Modules Modules will, by default, start with the parent application. They also have a `.start` function that can be used to start a stopped module, or a module that's been configured to start independently from its parent. In this example, the module will exhibit the default behavior and start automatically with the parent application object's `start` call: ```js MyApp = new Backbone.Marionette.Application(); MyApp.module("Foo", function(){ // module code goes here }); MyApp.start(); ``` Note that modules loaded after the `MyApp.start()` call will be immediately started. ### Module Initializers Modules, like `Application` objects, can be configured to have initializers. And just like an Application's initializers, module's initializers are run anytime that the module is started. Further, there is no limit to the number of initializers it can have. Initializers can be added in the module's definition function. ```js MyApp.module("Foo", function(Foo){ Foo.addInitializer(function(){ // Do things once the module has started }); Foo.addInitializer(function(){ // You can have more than one initializer }); }); ``` ### Start Events 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. ```js 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 }); ``` #### Passing Data to Start Events `.start` takes a single `options` parameter that will be passed to start events and their equivalent methods (`onStart` and `onBeforeStart`.) ```js 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); ``` ### Preventing Auto-Start Of Modules The default behavior of modules is that they start with the application. If you wish to manually start a module instead, you can change this behavior with the `startWithParent` property. ```js 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(); ``` The same behavior can be accomplished with the object literal definition: ```js var fooModule = MyApp.module("Foo", { startWithParent: false }); ``` When splitting a module across multiple files, it is recommended that you set `startWithParent` to be false. ### Starting Sub-Modules With Parent As you might expect, submodules default to starting with their parent module. The 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`. ```js 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 (by default) set to start with the app. A sub-module can override this behavior by setting its `startWithParent` to false. This prevents it from being started by the parent's `start` call. ```js 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: ```js MyApp.module("Foo.Bar").start(); ``` ## Stopping Modules A module can be stopped, or shut down, to clear memory and resources when the module is no longer needed. Like the 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 its children, call the `stop` method of a module. ```js 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, or stop its parent module. When you stop any parent module, all of its children will be stopped as well. ```js 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". ### Module Finalizers Modules also have finalizers that work in an opposite manner to initializers: they are called whenever a module is stopped via the `stop` method. You can have as many finalizers as you'd like. ```js MyApp.module("Foo", function(Foo){ Foo.addFinalizer(function(){ // Tear down, shut down and clean up the module in here }); Foo.addFinalizer(function(){ // Do more things }); }); ``` ### Stop Events 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. ```js 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 }); ```