Cilantro comes with a router class which supports a few different use cases. Routes are registered using a set of configuration options (below) that automatically setup a route handler to show all registered views that match and hide all views that do not.

It's primary use case is for easily registering page-level views:

define(['cilantro.ui'], function(c) {
    var views = {
        query: new c.ui.QueryWorkflow,
        results: new c.ui.ResultsWorkflow,
    };

    c.router.register([{
        id: 'query',
        route: 'query/',
        navigable: true,
        view: views.query
    }, {
        id: 'results',
        route: 'results/',
        navigable: true,
        view: views.results
    }]);
});

When the app navigates to /query/ for instance, the views.query view will be rendered and displayed on the page. Where on the page it is rendered is determined by two options.

  • The router itself has an el option that defines the scope of where views will be rendered
  • The route options can supply an el option for further limiting the scope within the router's el

The Cilantro ui.main option is used for the el option when initializing the primary router (it defaults to body).

Example

Given a page:

<body>
    ...
    <div id=main>
        <div id=region1></div>
        ...
    </div>
    ...
</body>

The pre-load Cilantro options would be defined like:

var cilantro = {
    ...
    ui: {
        main: '#main'
    }
};

This would initialize the primary router c.router with it's el set to #main. Any views registered would be rendered in #main or in a sub-element of #main.

The next step is registering the routes:

var views = {
    query: new c.ui.QueryWorkflow,
    results: new c.ui.ResultsWorkflow,
};

c.router.register([{
    id: 'query',
    el: '#region1',
    route: 'query/',
    navigable: true,
    view: views.query
}, {
    id: 'results',
    el: '#region1',
    route: 'results/',
    navigable: true,
    view: views.results
}];

The el option defined on each of the routes above define where the view will be rendered. If left undefined the view would be rendered in #main itself. The behavior of these two routes would simply show/hide the contents of the two views when toggling between /query/ and /results/.

Route Options

id

A string or number that acts as a unique identifier for referencing this particular route config. This is used internally for registering routes and is used when unregistering routes.

view

A Backbone.View instance that will be rendered when the route is navigated to. For lazily loading a view, an AMD module path can be defined instead that will be loaded when the route is navigated to for the first time. This is useful for views or pages that are expensive to load up front or if it's not a common route that is navigated to. This must return a Backbone.View instance that will be rendered once loaded.

route

The URL route fragment that will trigger the view to be loaded or conversely unloaded.

Flags a route as directly navigable. If true, the id can be passed to c.router.navigate and the fragment that is navigated to will be route:

// The route with id 'query' is navigable and the fragment that
// will actually be routed to is 'query/'.
c.router.navigate('query', {trigger: true});

Certain Cilantro views require specific routes to be navigable. For example the ContextActions has a button that can be clicked to view the results. However, the button needs to know which route to navigate to for showing the results. It depends on a route being defined with an id: 'results' to navigate.

el

Optional DOM selector that is contained within the router's el.

Events

The views associated with a route are loaded and unloaded automatically when they match their respective route URL. There are corresponding router:load and router:unload events that are triggered on the view itself when the respective events occur. Both take the router instance and the current route that triggered the load/unload cycle.

var MyView = Backbone.View.extends({
    initialize: function() {
        this.on('router:load', this.load);
        this.on('router:unload', this.unload);
    },

    load: function(router, route) {
        // something like.. resolving deferreds
    },

    unload: function(router, route) {
        // something like.. queue/pend deferreds
    }
});

Multiple Routers

For very dynamic pages where a lot of views are loaded for a given route, it may be desirable to break up the routes per region of the page. A router can be defined that targets a specific region of the page (such as #region1 from the example above) so all routes registered to that router are inherently scoped to that element.

From the above example, a second router could be created already scoped to #region1 which to prevent needing to supply the el option on the routes themselves.

var router1 = new c.Router({
    el: '#region1'
});

router1.register([{
    id: 'query',
    route: 'query/',
    navigable: true,
    view: views.query
}, {
    id: 'results',
    route: 'results/',
    navigable: true,
    view: views.results
}];

Auto-Routing

Cilantro is designed to be a drop-in set of views for graphically interacting with a Serrano-compatible API. It does not assume it is the only component on the page, nor does it assume the site as a whole is a single-page app with a single entry point.

The biggest difficulty is differentiating between in-app links/routes and links that go to another page on the site. The most common example of this is a navigation bar. Some of the links may be used by Cilantro's router to show a new set of views, but other links such as the home page, account management page, logout, etc. navigate away to a separate webpage on the site.

Fortunately Cilantro comes with simple solution for handling this and is turned on by default with the autoroute option. The processing works as follows:

  • click events that bubble up to the document from a elements will be caught
  • the a.href will attempt to be navigated to using Backbone.history.navigate
  • only if the return value is true, the normal behavior of the click will be prevented

This ensures all non-Cilantro links process as normal. Also, handling these links at the document level prevents prematurely processing the event in case a different parent of the a intended to catch the event.