JavaScript technologies

Refining views in a Backbone application with Marionette

Horváth, Győző
senior lecturer
horvath.gyozo@inf.elte.hu

Financed from the financial support ELTE won from the Higher Education Restructuring Fund of the Hungarian Government

Marionette

An extension layer over Backbone features

Marionette references

Basic concepts

  • Sophisticated view management
  • List operations
  • Useful extensions
  • Reusable behaviors
  • Communication between components
  • Object-oriented paradigm
  • Application frame

Views

  • ItemView
  • CollectionView
  • LayoutView
  • (CompositeView)

Notes

The following considerations about view and memory management are from the great book of Addy Osmani.

Abstraction step 1.

The template

<script type="text/html" id="my-view-template">
  <div class="row">
    <label>First Name:</label>
    <span><%= firstName %></span>
  </div>
  <div class="row">
    <label>Last Name:</label>
    <span><%= lastName %></span>
  </div>
  <div class="row">
    <label>Email:</label>
    <span><%= email %></span>
  </div>
</script>

Abstraction step 2.

The view

var MyView = Backbone.View.extend({
  template: $('#my-view-template').html(),

  render: function() {

    // compile the Underscore.js template
    var compiledTemplate = _.template(this.template);

    // render the template with the model data
    var data = _.clone(this.model.attributes);
    var html = compiledTemplate(data);

    // populate the view with the rendered html
    this.$el.html(html);
  }
});

Abstraction step 3.

Using the view

var Person = Backbone.Model.extend({
  defaults: {
    "firstName": "Jeremy",
    "lastName": "Ashkenas",
    "email":    "jeremy@example.com"
  }
});

var Derick = new Person({
  firstName: 'Derick',
  lastName: 'Bailey',
  email: 'derickbailey@example.com'
});

var myView = new MyView({
  model: Derick
});

myView.setElement("#content");
myView.render();

Abstraction step 4.

Marionette.ItemView

var MyView = Marionette.ItemView.extend({
  template: '#my-view-template'
});

Memory management 1.

The view

var ZombieView = Backbone.View.extend({
  template: '#my-view-template',

  initialize: function() {

    // bind the model change to re-render this view
    this.listenTo(this.model, 'change', this.render);

  },

  render: function() {

    // This alert is going to demonstrate a problem
    alert('We`re rendering the view');

  }
});

Memory management 2.

Using the view

var Derick = new Person({
  firstName: 'Derick',
  lastName: 'Bailey',
  email: 'derick@example.com'
});


// create the first view instance
var zombieView = new ZombieView({
  model: Derick
});

// create a second view instance, re-using
// the same variable name to store it
zombieView = new ZombieView({
  model: Derick
});

Derick.set('email', 'derickbailey@example.com');

Memory management 3.

Reparation

var ZombieView = Backbone.View.extend({
  template: '#my-view-template',

  initialize: function() {
    // bind the model change to re-render this view
    this.listenTo(this.model, 'change', this.render);
  },

  close: function() {
    // unbind the events that this view is listening to
    this.stopListening();
  },

  render: function() {

    // This alert is going to demonstrate a problem
    alert('We`re rendering the view');

  }
});

Memory management 4.

Using the repaired view

var Jeremy = new Person({
  firstName: 'Jeremy',
  lastName: 'Ashkenas',
  email: 'jeremy@example.com'
});

// create the first view instance
var zombieView = new ZombieView({
  model: Jeremy
})
zombieView.close(); // double-tap the zombie

// create a second view instance, re-using
// the same variable name to store it
zombieView = new ZombieView({
  model: Jeremy
})

Jeremy.set('email', 'jeremyashkenas@example.com');

Memory management 5.

Marionette.ItemView with automatic close() method.

var ZombieView = Marionette.ItemView.extend({
  template: '#my-view-template',

  initialize: function() {

    // bind the model change to re-render this view
    this.listenTo(this.model, 'change', this.render);

  },

  render: function() {

    // This alert is going to demonstrate a problem
    alert('We`re rendering the view');

  }
});

Memory management 6.

Rendering the view (boilerplate code)

var Joe = new Person({
  firstName: 'Joe',
  lastName: 'Bob',
  email: 'joebob@example.com'
});

var myView = new MyView({
  model: Joe
})

myView.render();

// show the view in the DOM
$('#content').html(myView.el)

Memory management 7.

Marionette.Region

// create a region instance, telling it which DOM element to manage
var myRegion = new Marionette.Region({
  el: '#content'
});

// show a view in the region
var view1 = new MyView({ /* ... */ });
myRegion.show(view1);

// somewhere else in the code,
// show a different view
var view2 = new MyView({ /* ... */ });
myRegion.show(view2);

ItemView

  • for showing a model
  • in a template
var MyView = Marionette.ItemView.extend({
  template: "#some-template"
});

new MyView({
    model: modelInstance
}).render();

CollectionView

  • showing a collection
  • automatic iteration
  • childView must be set
Marionette.CollectionView.extend({
  childView: MyChildView
});

LayoutView

  • showing multiple views
  • define regions
  • show() method
var RootView = Mn.LayoutView.extend({

  template: '#root-template'

  regions: {
    header: '#navbar',
    content: '.content-area',
    footer: 'footer'
  },

  initialize: function() { /* ... */  },

  onBeforeShow: function() {
    this.getRegion('header').show(new HeaderView());
    this.getRegion('footer').show(new FooterView());
    this.getRegion('content').show(new IndexView());
  }

});