Simple Backbone Marionette Example With AppRouter

This one took a little longer than the rest to put together, because I nearly broke my brain trying to figure out how routing worked. I found myriad code samples for setting up and using the router, but no one sample had everything I needed to put together a bare-bones app. Once i finally figured it out, the rest snapped together immediately. So without further ado – First, the Javascript file:

/* define the application */
var app = new Backbone.Marionette.Application();

/* add the main region to the application */
app.addRegions({
	appRegion: '#AppBase'
});

/* define the module we will be using to create this app */
app.module('RouteTest',function(module, App, Backbone, Marionette, $, _){
	"use strict";

	/* the layout for the main view */
	module.AppLayoutView = Marionette.LayoutView.extend({
		tagName: 'div',
		id: 'AppLayoutView',
		template: '#template-AppLayoutView',

		regions: {
			'contentRegion' : '#ContentRegion'
		},
		ui: {
			'navHome' : '#nav-home',
			'navInfo' : '#nav-info'
		},
		events: {
			'click #nav-home' : 'onNavHomeClicked',
			'click #nav-info' : 'onNavInfoClicked'
		},

		/* when the view initializes, call initRouter to */
		initialize: function() {
			this.initRouter();
		},

		/* once the DOM is ready, start the Backbone history manager.
			This will cause the application to synch up with the 
			current route of the browser, e.g. #home or #info.
			This must be called onRender instead of on initialize
			because it immediately tries to render the appropriate view
			into the contentRegion. Also: If you don't start the backbone
			history, the router won't work. */
		onRender: function() {
			if( ! Backbone.History.started) Backbone.history.start();
		},

		/* initialize the AppRouter, which synchs the application
			to the browser navigation */
		initRouter: function() {

			// cache reference to 'this' in the module scope
			var capturedThis = this;

			// assign route handlers to specific routes.
			// In this case, 'home' is triggered when the browser
			// visits index.html#home. Likewise index.html#info.
			// the empty set is for an address with no hash.
			var appRouteHandler = {
					'' : 'onHomeRoute',
				'home' : 'onHomeRoute',
				'info' : 'onInfoRoute'
			}

			// controller which contains the methods which
			// are used as route handlers. These are referenced
			// in the appRoutes object above.
			var appRouterController = {
				onHomeRoute: function() {
					capturedThis.onHomeNavigated();
				},
				onInfoRoute: function() {
					capturedThis.onInfoNavigated();
				}
			};

			// define an AppRouter constructor
			var router = Marionette.AppRouter.extend({});

			// create a new instance of the AppRouter
			// and assign the routes and controller
			var appRouter = new router({
				appRoutes: appRouteHandler, 
				controller: appRouterController
			});
		},

		/* called when the router sees that we have met the criteria
			to trigger the 'onHomeRoute' handler */
		onHomeNavigated: function() {

			// define and display an instance of the HomeLayoutView
			var homeLayoutView = new module.HomeLayoutView();
			this.contentRegion.show(homeLayoutView);

			// update the navigation
			this.$el.find('.navButton.active').removeClass('active');
			this.ui.navHome.addClass('active');
		},

		/* called when the router sees that we have met the criteria
			to trigger the 'onInfoRoute' handler */
		onInfoNavigated: function() {
			var infoLayoutView = new module.InfoLayoutView();
			this.contentRegion.show(infoLayoutView);
			this.$el.find('.navButton.active').removeClass('active');
			this.ui.navInfo.addClass('active');
		}
	});

	/* view definition for the 'Home' screen */
	module.HomeLayoutView = Marionette.LayoutView.extend({
		tagName: 'div',
		id: 'HomeLayoutView',
		className: 'contentLayout',
		template: '#template-HomeLayoutView'
	});

	/* view definition for the 'Info' screen */
	module.InfoLayoutView = Marionette.LayoutView.extend({
		tagName: 'div',
		id: 'InfoLayoutView',
		className: 'contentLayout',
		template: '#template-InfoLayoutView'
	});

	/* add initializer, which fires when the app starts */
	module.addInitializer(function(){
		var layout = new module.AppLayoutView();

		/* show the layout in the region we created at the top of this file */
		app.appRegion.show(layout);
	});
});

/* when the DOM for this page is available, start the application */
$(document).ready(function() {
	app.start();
});

And here is the HTML file, including the templates.

<!doctype html>
<html>
	<head>
		<title>Backbone/Marionette Routing Example Using AppRouter</title>
		<style type="text/css">
			body {
				background: #ffffff;
			}
			#AppBase {
				width: 600px;
				margin: 10px auto;
				padding: 0;
			}
			#AppLayoutView {
				float: left;
				width: 598px;
				margin: 0;
				padding: 0;
				border: 1px solid #808080;
			}
			h1 {
				margin: 0;
				padding: 0;
				float: left;
				width: 100%;
				height: 30px;
				text-align: center;
				font: 24px/30px courier;
			}
			#Navigation {
				float: left;
				width: 100%;
				height: 30px;
				background: #ededed;
				text-align: center;
			}
			#Navigation a {
				font: 16px/30px courier, monospace;
				color: #000000;
				text-decoration: none;
			}
			#Navigation .navButton.active {
				color: #ff0000;
			}
			#ContentRegion {
				float: left;
				width: 100%;
			}
			.contentLayout {
				float:left;
				width: 100%;
				margin: 0;
				padding: 0;
				
			}
			#HomeLayoutView {
				background: #edffed;
			}
			#InfoLayoutView {
				background: #ededff;
			}
			.contentLayout h2 {
				margin: 0;
				padding: 0;
				text-align: center;
				border-bottom: 1px solid #808080;
				background: #ffffff;
			}
			.contentLayout p {
				padding: .5em 1em;
			}
		</style>
	</head>
	<body>
		<!-- Base element for app -->
		<div id="AppBase"></div>

		<!-- main layout template -->
		<script type="text/template" id="template-AppLayoutView">
			<h1>Backbone/Marionette Routing Example</h1>
			<div id="Navigation">
				<a id="nav-home" class="navButton" href="#home">Home</a> : : : 
				<a id="nav-info" class="navButton" href="#info">Information</a>
			</div>
			<div id="ContentRegion">HELLO!</div>
		</script>

		<!-- home screen template -->
		<script type="text/template" id="template-HomeLayoutView">
			<h2>Home</h2>
			<p>This is the home view</p>
		</script>

		<!-- info screen template -->
		<script type="text/template" id="template-InfoLayoutView">
			<h2>Information</h2>
			<p>This is a page of information</p>
		</script>

		<!-- libraries -->
		<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
		<script type="text/javascript" src="js/underscore.js"></script>
		<script type="text/javascript" src="js/backbone.js"></script>
		<script type="text/javascript" src="js/backbone.marionette.js"></script>

		<!-- app code -->
		<script type="text/javascript" src="js/script.js"></script>
	</body>
</html>

By the way, the missing piece which ended up costing me the better part of a day was the Backbone.history.start() call in the onRender method in the main Layout view.