Chapter 1: Application

In the introduction, the library’s author mentions something about “a composite application architecture”. This composite architecture refers to two different things:

Application components built in a hierarchical module structure.

Hierarchical view structures through nested (sub)views.

This chapter will discuss the root piece of that first point: the Application object. We won’t be going into details about how to add subcomponents to your applications until the next chapter, when we talk about modules, but we’ll learn just about everything else there is to know about Application.

The Central Application Object

Most of the time, when someone creates a Backbone application, they make a central object that everything is attached to, which is often referenced as App or Application. Backbone doesn’t offer anything to make this object from, so most people just create a main router or view and make that the app object. While it’s great that people are attaching things to a central object so that the global namespace isn’t so convoluted, the router was not intended to handle this task and it violates the single responsibility principle.

This can be a problem. Without a central application to use as a namespace (or a module system that keeps variables out of the global scope), most objects and variables will be made globally accessible. While this may seem like it makes things simpler, it poses a substantial risk because variables can easily and unwittingly be overwritten, which can cause bugs that are difficult to track down.

This is why so many people convert their routers and views into central application objects and attach all of their components as properties of this central application. As I mentioned earlier, though, this violates the single responsibility principle. A router is designed to convert URLs to actions, and a view is designed to display information — and that is all they should do. Those are their single responsibilities. Turning them into the central hubs of activity for your application is giving them new responsibilities, which makes them more verbose, makes them more difficult to understand and maintain, and convolutes their roles. You can find more information on this principle around the web1.

Derick Bailey decided to give users a prebuilt solution to these issues: the Application class. Application takes over the namespacing responsibility and is a mediator for your entire application. Now your routers can be routers and your views can be views.

Extending Application

As you’ll find out quite soon, Application has plenty to offer right out of the box, but every application is different, so you’ll probably want to add your own functionality to your Application object. You have two options to accomplish this:

Use the built-in extend function to create a subclass of Application, the same way you do with Backbone’s components.

Add properties to the already instantiated object.

Application.extend

Just like almost everything in Backbone — and also nearly everything in Marionette — you can call extend on the class to create a subclass with your own functionality. Application is no different.

var MyApp = Marionette.Application.extend({
    // add your own properties and methods here
});

Unlike some classes, when you extend Application, there are no properties you can add that will be used in a special way (such as the events property for Backbone.View). This simply allows you to create a new subclass of Application.

Adding Properties to the Object

Just like any other object in JavaScript, you can dynamically add properties to an instantiated Application object any time you want. Simply plop a period and a property name onto the object and assign something to it. Obviously, you can use the square bracket notation too, but the point is there’s nothing stopping you from adding functionality after your Application has already been instantiated.

var App = new Marionette.Application();

// Add property with dot notation
App.foo = function() {...};
// Add property with square bracket notation
App['bar'] = function() {...};

Either of these ways of extending Application works just fine. While using extend seems more correct, the fact that an Application generally only has a single instance and is rarely reused makes it seem superfluous. Do whatever suits you.

Initializers

One of the coolest things about Marionette’s Application is the initializers. When your code is modular, several pieces will need to be initialized when the application starts. Rather than filling a single file with a load of code to initialize all of these objects, you can just set the modules up for initialization within the code for the module. You do this using addInitializer. For example:

var SomeModule = function(o){
    // Constructor for SomeModule
};

// App is an instantiated Application object
App.addInitializer(function(options) {
    App.someModule = new SomeModule(options);
});

All of the initializers added this way will be run when App.start is called. Notice the options argument being passed into the initializer. This is the very same object that is passed in when you call App.start(options). This is great for allowing a configuration to be passed in so that every module can use it.

A few events are also fired when running through these initializers:

initialize:before fires just before the initializers are run.

initialize:after fires just after the initializers have all finished.

start fires after initialize:after.

You can listen for these events and exert even more control. Listen for these events like this:

App.on('initialize:before', function(options) {
    options.anotherThing = true; // Add more data to your options
});
App.on('initialize:after', function(options) {
    console.log('Initialization Finished');
});
App.on('start', function(options) {
    Backbone.history.start(); // Great time to do this
});

Pretty simple, and it gives you a ton of flexibility in how you start up your applications.

Event Aggregator

The Application object brings even more possibilities for decoupling a Backbone application through the use of an event aggregator. A while back I wrote an article about scalable JavaScript applications2, in which I mentioned that modules of a system should be completely ignorant of one another, and that the only way they should be able to communicate with each other is through application-wide events provided by a mediator, which in this case is the Application object. This way, every module that cares can listen for the changes and events they need to, so that they can react to them without anything else in the system even realizing it exists.

Marionette makes this kind of decoupling largely possible via the event aggregator (provided by the Backbone.Wreqr plugin) that is automatically attached to the application object. While this is only one of the mechanisms I wrote about in that article, it is a start and can be very useful even in small applications.

The event aggregator is available through a property in the application called vent. You can subscribe and unsubscribe to events simply via the on and off methods, respectively (or bind and unbind, if you prefer). These functions might sound familiar, and that’s because the event aggregator is simply an extension of Backbone’s Event object3. Really, the only thing new here that you need to worry about is that we’re using the events on an object that should be accessible everywhere within your app, so that every piece of your application can communicate through it. The event aggregator is available as a separate component too, so you can add it to any object you want, just like Backbone’s Event.

There are a few other components that Backbone.Wreqr provides that are attached to Application objects, but we’ll discuss those more in chapter 8. For now, just realize that Application can be used as a mediator for communication throughout the system.

Regions

Region is another component from Marionette that enables you to easily attach views to different regions of an HTML document. I won’t go into detail about how regions work here — they will be discussed in greater detail in chapter 7 — but I’ll cover it briefly and explain how they integrate with Application.

A region is an object — normally created with new Marionette.Region({ el: 'selector'}) — that manages the insertion and removal of views from a certain location in the DOM. Add a view and automatically render it by using show and then close out that view (meaning it will remove it from the DOM and clean up any event bindings) and render a different view simply by calling show again, or you can just close the view by calling close. Regions can do more than that, but the fact that they handle the rendering and closing for you with a single function call makes them extremely useful. Here’s a code sample for those who speak better in code than in English:

// Create a region. It will control what's in the #container element.
var region = new Marionette.Region({
    el: "#container"
});

// Add a view to the region. Immediately renders the view.
region.show(new MyView());

// Close out the current view and render a different view.
region.show(new MyOtherView());

// Close out the view and display nothing in #container.
region.close();

If you want a Region directly on your application object (e.g. App.someRegion), Application provides a simple way to add one quickly: addRegions. There are three ways to use addRegions. In every case, you would pass in an object whose property names will be added to the application as regions, but the value of each of these may be different depending on which way you wish to accomplish this.

Selector

Simply supply a selector, and a standard region will be created that manages views being attached to the DOM node that matches that selector.

App.addRegions({
    container: "#container",
    footer:    "#footer"
});

// This is equivalent to
App.container = new Marionette.Region({el:"#container"});
App.footer    = new Marionette.Region({el:"#footer"});

Custom Region Type

You can extend Region to create your own types of regions. If you want to use your own type of region, you can use the syntax below. Note that, with this syntax, el must already be defined within your region type, which is a selector used to determine which DOM node the region manages.

var ContainerRegion = Marionette.Region.extend({
    el: "#container", // Must be defined for this syntax
    // Whatever other custom stuff you want
});

var FooterRegion = Marionette.Region.extend({
    el: "#footer", // Must be defined for this syntax
    // Whatever other custom stuff you want
});

// Use these new Region types on App.
App.addRegions({
    container: ContainerRegion,
    footer:    FooterRegion
});

// This is equivalent to:
App.container = new ContainerRegion();
App.footer    = new FooterRegion();

Custom Region Type with Selector

If you don’t define el — or you want to override it — in your custom region type, then you can use this syntax, which specifies both the region type and the selector:

var ContainerRegion = Marionette.Region.extend({});

var FooterRegion = Marionette.Region.extend({});

// Use these new Region types on App.
App.addRegions({
    container: {
        regionType: ContainerRegion,
        selector:   "#container"
    },
    footer: {
        regionType: FooterRegion,
        selector:   "#footer"
    }
});

// This is equivalent to:
App.container = new ContainerRegion({el:"#container"});
App.footer    = new FooterRegion({el:"#footer"});

As you can see, adding application-wide regions is dead simple (especially if you’re using the normal Region type). You’ll get a better sense of how useful this is when we discuss regions in more detail and put them to work in an application.

Summary

We’ve learned about the Application object, which is used as the root of an application and a mediator between the other modules of the system. These are powerful features that offer a simple means for organizing and decoupling your JavaScript applications.

Marionette also adds many other great features to make Backbone development simpler. So far we’ve only covered one of the many components Marionette offers — though we have quickly touched on a couple other components used by Application — and there is a lot more to learn, so keep reading!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset