Backbone, Marionette and other puppets

alejandroelinformatico.com/slideshows/en/marionette-js/

Alejandro El Informático(@ainformatico) - 2014

Creative Commons by-nc-sa.

About me

Alejandro El Informático, Web developer

Twitter (@ainformatico) Github (@ainformatico) Bitbucket (@ainformatico)

What is this about?

Creating webapps in a Composite way. Using Marionette.

Before

  • app is getting bigger and bigger
  • hard to maintain
  • hard to extend
  • keep global objects or create a namespace
  • object references between components
  • useless code

After

  • decoupled components
  • independent components
  • easy to extend
  • easy to maintain
  • do not use global objects
  • pub/sub
  • building blocks...

Marionette

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

Tell me more

  • listeners
  • bind/unbind
  • application container
  • no global objects
  • well documented
  • automatic render
  • iterators
  • composite
  • modules

Zombies?

  • Automatically unbinds all events
  • Recursively unbinds all events
  • Do not keep memory references

Backbone vs Marionette

model


var Todo = Backbone.Model.extend({
  defaults : {
    content : 'no content defined'
  }
});
            

Backbone / Marionette

collection


var Todos = Backbone.Collection.extend({
  model : Todo
});
            

Backbone / Marionette

view


var TodoView = Backbone.View.extend({
  events: {
    'click .open': 'open',
    'click .save': 'save'
  },
  open : function(e) {
    e.prevenDefault();
  }
  save : function(e) {
    e.prevenDefault();
  }
  initialize: function() {
    var _this = this;
    _this.listenTo(_this.model, 'change', _this.render);
  },
  render: function() {
    var _this = this;
    _this.$el.html(_this.template(_this.model.toJSON()));
    return _this;
  }
});
            

Backbone

view


var TodoView = Marionette.ItemView.extend({
  modelEvents : {
    'change' : 'render'
  },
  initialize : function(){
    this.listenTo(foo, 'bar', this.bar); //automatic unbind
  },
  triggers : {
    'click .open': 'open',
    'click .save': 'save'
  },
  onOpen : function() {
    //actions
  }
  onSave : function() {
    //actions
  }
});
            

Marionette

Marionette components

  • ItemView
  • CollectionView
  • CompositeView
  • Region
  • Layout
  • Application
  • Module
  • Controller
  • Command
  • Request

ItemView

  • #render automatically passes model properties
  • unbinds all events when removing
  • triggers : {}, prevent by default
  • modelEvents : {} and collectionEvents : {}
  • ui : {}, references /html elements

var TodoView = Marionette.ItemView.extend({
  template : '#tpl-item',
  //this.ui.inputElement
  ui : {
    inputElement : '.edit'
  },
  collectionEvents: {
    'add': 'itemAdded'
  },
  modelEvents: {
    'change:name': 'nameChanged'
  },
});
              

<div id="tpl-item">
  <span class="edit"> <%= content %> </span>
</div>
              

CollectionView

  • extends from ItemView
  • loops through and #renders all of the models in the specified collection
  • #renders an ItemView
  • uses emptyView for empty collections.

var TodoListView = Marionette.CollectionView.extend({
  itemView : TodoView,
  emptyView : EmptyTodoView,
  collection : collection
});
              

<div id="tpl-empty">
  <div class="empty">No data found</div>
</div>
                

CompositeView

  • extends from CollectionView
  • needs a template
  • tree view, recursive by default

var TodoListView = new Marionette.CompositeView({
  template : '#tpl-list',
  itemView : TodoView,
  itemViewContainer : '.list'
});
              

<div id="tpl-list">
  <h1>Title</h1>
  <ul class="list"></ul>
</div>
              

Are you still there?

Region

  • manage, show and close views
  • show() and close()

var region = new Marionette.Region({
  el: '#tpl-region',
  regions : {
    menuRegion : '#menu',
    contentRegion : '#content'
  }
});

region.menuRegion.show(menuView);
region.contentRegion.show(menuView);
                

<div id="tpl-region">
  <div id="menu"></div>
  <div id="content"></div>
</div>
                

Layout

  • hybrid of an ItemView and a collection of Region objects
  • multiple sub-regions that can contain other layouts
  • recursively closes views and unbinds its events

var layout = new Backbone.Marionette.Layout({
  template: '#tpl-layout',
  regions: {
    menu : '#menu',
    content : '#content'
  }
});

layout.menuRegion.show(menuView);
layout.contentRegion.show(menuView);
                

<div id="tpl-layout">
  <div id="menu"></div>
  <div id="content"></div>
</div>
                

application

  • hub of your composite application
  • organizes, initializes and coordinates the various pieces of our app
  • region manager
    • <body></body>

app = new Marionette.Application();

app.addRegions({
  headerRegion  : '#header-region',
  contentRegion : '#content-region'
});

app.addInitializer(function(){
  //init your stuff
});
              

<body>
  <div id="app">
    <div id="header-region"></div>
    <div id="content-region"></div>
  </div>
  <!-- templates -->
</body>
              

module

  • modular encapsulated logic
  • split large applications into multiple files
  • build individual components

app.module('Views', function(Views, app){
  Views.TodoView = Marionette.ItemView.extend({});
});

new app.Views.TodoView();
            

controller

  • multi-purpose object to use as a controller for modules and routers
  • mediator for workflow and coordination of other objects, views, and more

var controller = new Marionette.Controller({
  initialize: function(options){
    //load views, listeners...
  }
});
            

command

  • application-level command execution system
  • no response, it's "fire-and-forget"

app.commands.setHandler('main:show', function(view){
  app.mainRegion.show(view);
});

app.execute('main:show', view);
            

request

  • application-level request/response system
  • request some information or work to be done by another part of the application
  • a return response is expected when making a request

app.reqres.setHandler('views:todo', function(options){
  return app.Views.TodoView(options || {});
});

var noteView = app.request('views:todo');
            

Demo time

Recap

  • know your app
  • do not create dependencies between components
  • javascript is event oriented, use it!
  • DRY
  • composite your application
  • try new stuff!

Q&a