Introduction to Marionette.js

"All your backbone's belong to Marionette.js"




META


"Backbone.Marionette is a composite application library for Backbone.js that aims to simplify the construction of large scale JavaScript applications.

It is a collection of common design and implementation patterns found in the applications that we have been building with Backbone, and includes pieces inspired by composite application architectures, event-driven architectures, messaging architectures, and more."

~ 68kb unminified
~ 23kb minified
~ 6kb gzip

Why do we care?

  • Scale applications out with modular, event driven architecture
  • Sensible defaults, such as using Underscore templates for view rendering
  • Easy to modify to make it work with your application's specific needs
  • Reduce boilerplate for views, with specialized view types
  • Build on a modular architecture with an Application and modules that attach to it
  • Compose your application's visuals at runtime, with Region and Layout
  • Nested views and layouts within visual regions
  • Built-in memory management and zombie killing in views, regions and layouts
  • Built-in event clean up with the EventBinder
  • Event-driven architecture with the EventAggregator
  • And much, much more...

Why Do I Care?


  • Modular

  • Scalable

  • Maintainable

  • Testable

Design Patterns

"All Of This Has Happened Before And Will Happen Again"

  • Composite
  • Event Driven
    • Command/Execute¬†(Pub/Sub)
    • Request/Response

What does it provide?



  • application
  • application.module
  • approuter
  • callbacks
  • collectionview
  • commands
  • compositeview
  • configuration
  • controller
* this presentation's focus
  • functions
  • itemview
  • layout
  • region
  • regionmanager
  • renderer
  • requestresponse
  • templatecache
  • view
BASIC SETUP









<script src='./underscore.js'></script><script src='./jquery.js'></script> <script src='./underscore.js'></script> <script src='./backbone.js'></script> <script src='./marionette.js'></script>

REQUIRE Setup









require.config({
    paths : {
        'backbone'   : './backbone',
        'jquery'     : './jquery',
        'marionette' : './marionette',
        'underscore' : './underscore'
    },
    shim : {
        'backbone' : {
            deps    : ['jquery', 'underscore'],
            exports : 'Backbone'
        },
        'jquery' : {
            exports : 'jQuery'
        },
        'marionette' : {
            deps : ['jquery', 'underscore', 'backbone'],            exports : 'Marionette'
        },
        'underscore' : {
            exports : '_'
        }
    }
});

Backbone vs marionette



  • present a typical backbone way ( or my way at least ) of implementation or discuss the typical pitfalls I ran into

  • present a Marionette solution and code example





My Application


  • Non existant support in Backbone
  • Left to OO Javascript implementation
  • Controller and Application tightly coupled
  • Application configuration harder than in it should be
  • Rudimentary initialize method to start the application and set up route driven architecture

Marionette.Application



  • addInitializer() method for convenience
  • Application level events ( onInitializeBefore, onInitializeAfter, onStart )
  • They all get access to the configuration options passed into App.start() and addInitializer() methods
  • Also Regions, Layouts, etc.

Example



 var App = new Marionette.Application({
    onInitializeBefore : function(options) {
        // execute before initializers...
    },

    onInitializeAfter : function(options) {
        // execute after initializers
    },    onStart : function(options) {
        // execute after initializers & events
    }
});
App.addInitializer(function(options) { // code to be run on application init});

Application.Module



  • Auto start from App.start()
  • Added seperation of concerns and basic module structure which can be used with or without AMD support
  • `this` keyword bound to module object scope
  • Hierarchical module loading ( Mod, Mod.Thing )
  • start and beforeStart events
  • startWithParent flag for suspending module execution until explicitly started by Mod.start()
  • Getter provided from App with App.module('moduleName')
  • stop and beforeStop events provided to help with memory management and other module level cleanup items
  • callback definition for easy access to... ( ModuleName, AppName or parent ModuleName, Backbone, Marionette, jQuery, Underscore, Custom Arguments

Example



App.module("MyModule", function(MyModule, App, 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";
  this.someFunction = function(){
    console.log(MyModule.someData);
  }
});
App.module('MyModule').addInitializer(function() {});



My Controller


  • To coupled with the router
  • To involved in View memory management

Example


_.extend(Controller, {
    someRouteController : function() {
        var views = [];
        // check that our router is ready
        // and other things...
        this.auth();        // destroy previous views        this.destroyViews();        // insert view, model, etc code here...        // render new views views
        this.renderViews(views);
    },    destroyViews : function() {        if (this.currentView) {
            while (this.currentView.length > 0) {
                this.currentView.pop().destructor();
            }
        }
    },    renderViews : function(views) {
        this.currentView = views;
        for (var i = self.currentView.length - 1; i >= 0; i--) {
            this.currentView[i].render();
        }
    },    auth : function() {
        // simply code that needs to be asserted before anything else can run
    }
});

Marionette.Controller



  • State mediator object
  • Can be utilized inside modules or app level
  • Convenience hooks for AppRouter
  • Standard initialize() method
  • built in event aggregator can be utilized using the listenTo() method
  • close method and onClose event

Example



 // define a controller
var MyController = Marionette.Controller.extend({

  initialize: function(options){
    this.stuff = options.stuff;
  },

  doStuff: function(){
    this.trigger("stuff:done", this.stuff);
  }

});

// create an instance
var c = new MyController({
  stuff : "some stuff"
});

// use the built in EventBinder c.listenTo(c, "stuff:done", function(stuff){ console.log(stuff); }); // do some stuff c.doStuff();

My Router



  • Just basic Backbone.Router's
  • Usually helps drive a good deal of the application


Example


    var IndexRouter = Backbone.Router.extend({

        initialize : function(options) {
            this.app = options.app;
        },

        routes : {
            ''              : 'tilesBackbone',
            'tilesbackbone' : 'tilesBackbone'
        },

        tilesBackbone : function() {
            this.app.tilesBackbone();
        }
    });

AppRouter



  • Extends Backbone.Router
  • Provides a hook for your Marionette.Controller
  • Allows the addition of routes at runtime ( MyRouter.appRoute('/foo', 'bar' )

Example



 MyRouter = Backbone.Marionette.AppRouter.extend({
    controller : MyController,
    appRoutes : {
        "some/route": "someMethod"
    },

// standard backbone routes routes : { "some/otherRoute" : "someOtherMethod" }, someOtherMethod : function(){ // do something here. } });

My View


  • Standard Backbone.View, but usually extended from a baseView with common functionality
  • baseView.destructor() provided that would try and do memory cleanup for that View ( could be overridden )
  • memory management wasn't automatic, required execution ( usually automated by my Controller )
  • Decoupled from other views by using a Mediator Object which allowed pub/sub and cleanup methods

Example


    var Tile = BaseView.extend({
        el : '.tile-wrap',        events : {
            'click .btn' : 'flip'
        },
        subscriptions : {
            // add pub/sub subscriptions here...
        },        initialize : function(options) {
            _.bindAll(this);
            this.templar = options.templar;
        },
        flip : function(e) {
            this.$el.find('.tile').toggleClass('flipped');
        },
        render : function() {
            this.templar.render({
                path : 'tile',
                el   : this.$el,
                data : {}
            });
        }
    });

ItemView


  • Extension of Backbone.View
  • Works with Backbone Models and Collections
  • Reduces boilerplate of having to JSONify your models & collections
  • serializeData method for custom serialization for your ItemView
  • template method can be configured for use with other Templating solutions other than _.template's
  • Setup and breakdown methods ( onBeforeRender, onRender, onBeforeClose, and onClose )
  • ui pragma to set up references to needed DOM elements

Example



Backbone.Marionette.ItemView.extend({
    tagName : 'fieldset',

    ui: {
        checkbox : 'input[type=checkbox]'
    },

    onRender: function() {
        if (this.model.get('selected')) {
            this.ui.checkbox.addClass('checked');
        }
    }
});

let's look @ Code...

Conclusions


  • Pros
    • I like Marionette's continuation of Backbone's extend/use if needed policy
    • Sets up consistency and better memory management
    • Reduces common Backbone boilerplating
    • Good documentation and examples
    • Pretty well adopted and supported ( v1.1.0 came out 6 days ago )
  • Cons
    • Still MV*, Controller is a little ambiguous still
    • Still not a framework¬†

Questions



Resources
http://marionettejs.com/
https://github.com/marionettejs/backbone.marionette/tree/master/docs
books and more...

Presentation Slides
http://slid.es/ianqueue/marionettejs

Contact
https://github.com/ianqueue
@ianqueue

MarionetteJS

By ianqueue

MarionetteJS

A brief presentation on what Backbone.Marionette tries to help us fix with large Backbone applications...

  • 705