App Router Code

The code for the App Router tutorial can be used as a reference for how to build and integrate a simple application that uses the AppRouter for its navigation. Each application will have different requirements, so feel free to adapt this layout for your own application design.

Index file

index.html:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
    <div id="blog-hook"></div>
    <script src="static/js/app.js"></script>
  </body>
</html>

As with all the appendix items here, this is a simplistic HTML file. Feel free to utilize styling libraries such as Bootstrap to jazz it up.

Driver file

driver.js:

var Marionette = require('backbone.marionette');
var Router = require('./router');


var initialData = {
  posts: [
    {
      author: 'Scott',
      title: 'Why Marionette is amazing',
      content: '...',
      id: 42,
      comments: [
        {
          author: 'Steve',
          content: '...',
          id: 56
        }
      ]
    },
    {
      author: 'Andrew',
      title: 'How to use Routers',
      content: '...',
      id: 17
    }
  ]
};


var App = new Marionette.Application({
  onStart: function(options) {
    var router = new Router(options);

    /** Starts the URL handling framework */
    Backbone.history.start();
  }
});

App.start({initialData: initialData});

Router and Controller

router.js:

var LayoutView = require('./views/layout');
var BlogList = require('./collections/blog');


var Controller = Marionette.Object.extend({
  initialize: function() {
    /** The region manager gives us a consistent UI and event triggers across
        our different layouts.
    */
    this.options.regionManager = new Marionette.RegionManager({
      regions: {
        main: '#blog-hook'
      }
    });
    var initialData = this.getOption('initialData');

    var layout = new LayoutView({
      collection: new BlogList(initialData.posts)
    });

    this.getOption('regionManager').get('main').show(layout);

    /** We want easy access to our root view later */
    this.options.layout = layout;
  },

  /** List all blog entrys with a summary */
  blogList: function() {
    var layout = this.getOption('layout');
    layout.triggerMethod('show:blog:list');
  },

  /** List a named entry with its comments underneath */
  blogEntry: function(entry) {
    var layout = this.getOption('layout');
    layout.triggerMethod('show:blog:entry', entry);
  }
})

var Router = Marionette.AppRouter.extend({
  appRoutes: {
    'blog/': 'blogList',
    'blog/:entry': 'blogEntry'
  },

  /** Initialize our controller with the options passed into the application,
      such as the initial posts list.
  */
  initialize: function() {
    this.controller = new Controller({
      initialData: this.getOption('initialData');
    });
  }
});

module.exports = Router;

Collections and Models

models/blog.js:

module.exports = Backbone.Model.extend({
  /** Let us inject 0 comments in from the data set
  */
  defaults: function() {
    return {
      comments: []
    }
  }
});

collections/blog.js:

var Blog = require('../models/blog');

module.exports = Backbone.Collection.extend({
  model: Blog
});

models/comment.js:

module.exports = Backbone.Model.extend();

collections/comment.js:

var Comment = require('../models/comment');


module.exports = Backbone.Collection.extend({
  model: Comment
});

Views

views/layout.js:

var List = require('./list');
var Blog = require('./blog');


var LayoutView = Marionette.LayoutView.extend({
  template: require('../templates/blog/layout.html'),

  regions: {
    layout: '.layout-hook'
  },

  onShowBlogList: function() {
    var list = new List({collection: this.collection});
    this.showChildView('layout', list);

    /*  Remember - this only sets the fragment, so we can safely call this as
        often as we like with no negative side-effects.
    */
    Backbone.history.navigate('blog/');

  },

  onShowBlogEntry: function(entry) {
    var model = this.collection.get(entry);
    this.showBlog(model);
  },

  onChildviewSelectEntry: function(child, model) {
    this.showBlog(model);
  },

  /** Child-initiated alias to onShowBlogList */
  onChildviewShowBlogList: function() {
    this.triggerMethod('show:blog:list');
  },

  /** Share some simple logic from our subviews */
  showBlog: function(blogModel) {
    var blog = new Blog({model: blogModel});
    this.showChildView('layout', blog);

    /*  Remember - this only sets the fragment, so we can safely call this as
        often as we like with no negative side-effects.
    */
    Backbone.history.navigate('blog/' + blog.id);
  }
});

module.exports = LayoutView;

views/list.js:

var Entry = Marionette.LayoutView.extend({
  template: require('../templates/blog/item.html'),
  tagName: 'li',

  triggers: {
    click: 'select:entry'
  }
});


var BlogList = Marionette.CollectionView.extend({
  childView: Entry,
  tagName: 'ul',

  onChildviewSelectEntry: function(child, options) {
    this.triggerMethod('select:entry', child.model);
  }
});

module.exports = BlogList;

views/blog.js:

var Comment = Marionette.LayoutView.extend({
  tagName: 'li',
  template: require('../templates/blog/comment.html')
});

var CommentListView = Marionette.CollectionView.extend({
  tagName: 'ol',
  childView: Comment
});

var Blog = Marionette.LayoutView.extend({
  template: require('../templates/blog/blog.html'),

  regions: {
    comments: '.comment-hook'
  },

  ui: {
    back: '.back'
  },

  triggers: {
    'click @ui.back': 'show:blog:list'
  },

  onShow: function() {
    var comments = new CommentList(this.model.get('comments'));
    var commentView = new CommentListView({collection: comments});

    this.showChildView('comments', commentView);
  }
});

module.exports = Blog;

Templates

templates/blog/layout.html:

<h1>Marionette Blog</h1>
<div class="layout-hook"></div>

templates/blog/item.html:

<a href="/blog/<%- id %>"> <!-- This won't actually link anything -->
  <div class="title"><%- title %></div>
  by
  <span class="author"><%- author %></span>
</a>

templates/blog/blog.html:

<a href="/blog/" class="back">To Blog List</a>
<h2><%- title %></h2>
<div class="author">by <%- author %></div>

<%- content %>

<div class="comment-hook"></div>

templates/blog/comment.html:

<%- content %>

<div class="author"><%- author %></div>