Routers and App Events

This example demonstrates how Giraffe.Router ties into the appEvents described in the App Events example.

var App, ChildView;

The App

Giraffe.App is a Giraffe.View that encapsulates an app and its routes. In this example, clicking some links will change the window location hash. The Giraffe.Router responds to this hash change and triggers an event on the app which shows a child view with a specific name.

App = Giraffe.App.extend({
  template: '#app-template',

The Giraffe.App#routes property maps URL routes to appEvents, and as a result of defining routes on the app, it will automatically create an instance of Giraffe.Router at app.router. Giraffe.Router is dependent on Giraffe.App because it uses appEvents to communicate with your objects.

  routes: {
    'childView/:name': 'route:childView'
    // 'someHashLocation/:andItsParams': 'some:appEvent'
  },
For convenience, Giraffe.App creates a router if it has a routes hash, but you can create any number of routers. They do require that a Giraffe.App exists on the page, and instead of defining routes on an Giraffe.Router, you define triggers, which avoids conflict with Backbone.Router#routes.

When a route is triggered, its corresponding appEvent is called. Any Giraffe object, including the app, can listen for appEvents. In this example the app listens to itself for the app event 'route:childView', which is defined in the routes above, and the 'all' event, so we can log everything that happens to appEvents.

  appEvents: {
    'route:childView': 'showChildView',
    'all': function() { console.log('app event', arguments); }
    // 'some:otherRoute': 'someMethodName'
    // 'some:otherAppEvent': 'someOtherMethodName'
  },
The route: prefix is just a naming convention and is not required.

The handler for 'route:childView' creates a child view named with the route parameter, and inserts it into the DOM using the attachTo method 'html', which replaces anything inside '#child-view-container'.

  showChildView: function(name) {
    var childView = new ChildView({name: name});
    this.attach(childView, {el: '#child-view-container', method: 'html'});
  }
});

Here's the app's template. It has three links to the three child views and a container for the active child view.

<script id="app-template" type="text/template">
  <p><a href="#childView/1">show child view 1</a></p>
  <p><a href="#childView/2">show child view 2</a></p>
  <p><a href="#childView/3">show child view 3</a></p>
  <div id="child-view-container"></div>
</script>

The Child View

In this example, we're going to create a child view that simply displays its name and a color.

ChildView = Giraffe.View.extend({
  className: 'child-view',
  template: '#child-template',
  initialize: function(options) {
    var color;
    if (options.name === '1')
      color = '#e99';
    else if (options.name === '2')
      color = '#9e9';
    else
      color = '#99e';
    this.$el.css('background-color', color);
  }
});
<script id="child-template" type="text/template">
  <h2>child view <%= name %></h2>
</script>

Loading the App

It's time to create and attach the app.

var app = new App();
app.attachTo('body');
The routes hash could have been passed as an option to the Giraffe.App constructor.

Almost finished! Let's start Backbone.history to get things rolling.

Backbone.history.start();

The Giraffe.Router has one more trick up its sleeve: it gives you programmatic control over your routes. The function Giraffe.Router#cause takes an appEvent and optional parameters, navigates to the corresponding route defined in the router, and then triggers the appEvent with the parameters. Here we show child view 1 as the default view by causing its appEvent.

app.router.cause('route:childView', 1);

Giraffe.Router also provides two utility functions to help you manage routes, isCaused and getRoute. We could have used getRoute to build our anchor links in the app template above, but didn't for the sake of familiarity. No longer must you build route links manually!

console.log(app.router.isCaused('route:childView', 1)); // => true
console.log(app.router.isCaused('route:childView', 2)); // => false
console.log(app.router.isCaused('route:childView'));    // => false
console.log(app.router.getRoute('route:childView', 1)); // => '#childView/1'
You will not see hash changes in your address bar because the example is in an iframe, but your browser's back and forward buttons should work!

Try It

var App, ChildView;

App = Giraffe.App.extend({
  template: '#app-template',

  routes: {
    'childView/:name': 'route:childView'
    // 'someHashLocation/:andItsParams': 'some:appEvent'
  },

  appEvents: {
    'route:childView': 'showChildView',
    'all': function() {
      console.log('app event', arguments);
    }
    // 'some:otherRoute': 'someMethodName'
    // 'some:otherAppEvent': 'someOtherMethodName'
  },

  showChildView: function(name) {
    var childView = new ChildView({
      name: name
    });
    this.attach(childView, {
      el: '#child-view-container',
      method: 'html'
    });
  }
});

ChildView = Giraffe.View.extend({
  className: 'child-view',
  template: '#child-template',
  initialize: function(options) {
    var color;
    if (options.name === '1')
      color = '#e99';
    else if (options.name === '2')
      color = '#9e9';
    else
      color = '#99e';
    this.$el.css('background-color', color);
  }
});

var app = new App();
app.attachTo('body');

Backbone.history.start();

app.router.cause('route:childView', 1);

console.log(app.router.isCaused('route:childView', 1)); // => true
console.log(app.router.isCaused('route:childView', 2)); // => false
console.log(app.router.isCaused('route:childView')); // => false
console.log(app.router.getRoute('route:childView', 1)); // => '#childView/1'
<!DOCTYPE html>
<html>
  <head>
    <link rel='stylesheet' type='text/css' href='../css/reset.css' />
    <link rel='stylesheet' type='text/css' href='routersandappevents0-style.css' />
  </head>
  <body>
    <script id="app-template" type="text/template">
  <p><a href="#childView/1">show child view 1</a></p>
  <p><a href="#childView/2">show child view 2</a></p>
  <p><a href="#childView/3">show child view 3</a></p>
  <div id="child-view-container"></div>
</script>

<script id="child-template" type="text/template">
  <h2>child view <%= name %></h2>
</script>

<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script>
<script src="../backbone.giraffe.js" type="text/javascript"></script>
    <script type='text/javascript' src='routersandappevents0-script.js'></script>
  </body>
</html>
h2 {
  font-size: 24px;
}
.child-view {
  position: relative;
  padding: 20px;
  margin: 20px;
  border: 1px dashed #999;
}