Composer.js
- Start
- Class system
- Eventing
- Utilities
- Base
- Model
- Collection
- Controller
- Controller xdom
- Relational model
- Filter collection
- List controller
- Router
- Best practices
- Building/load order
- Tests
Router
The router makes it easy to map the current browser URL to an object/function. It supports regular expression group parameters (routes are written in regular expressions) and provides some convenience functions.
Note: the Composer.Router object requires History.js in order to function properly. It can be loaded without it, but History.js must be present to use it.
Events
These are the events fired by routers.
statechange
Triggered when History.js fires its ‘statechange’ event (happens when using
History.pushState
et al). Arguments passed:
- the URL we routed to
- the
force
parameter (whether or not we’re forcing a route load)
destroy
Triggered when a router object is destroyed. No arguments.
fail
Triggered when there is a problem routing. Arguments passed:
- description: a JS object that contains the
url
of the route that failed, theroute
that was found (if any), whether or not thehandler_exists
for the route that was found, and whether or not theaction_exists
for the route that was found.
preroute
Triggered right before a route actually happens. This event is unique because it allows modifying the URL that the route will be triggred with (without changing the URL in the browser bar). This essentially gives you URL rewriting abilities (think mod_rewrite in Apache). Arguments passed:
- boxed path: an object in the format
{path: url_path_str}
. If you change thepath
key in the boxed object, the router will use the path you provided to route on instead of the on in the URL bar.
route
Triggered when a route happens in the router. The router itself listens to this event to determine when to run the route and its matching code. This happens before the matching route (if any) is run. Arguments passed:
- The (string) path being routed on
route-success
Triggered when a route happens successfully (no errors, found a matching route). Note that this happens just before the found route function is called. Arguments passed:
- the matching route object
Composer.Router
The main router class. Extends Composer.Base, giving it access to all of Base’s abilities.
It’s important to note that unlike most other Composer objects, the router has global state (in order to bind to various URL events). As such, it is advised to only create one instance of the Composer.Router object at a time.
routes :: attribute({})
Holds the route’s routing table. Although routes are generally passed to the router’s constructor, we’ll document the format here because it deserves its own section.
Routes are specified in the {"/url/regex/specifier": ['object', 'function_name']}
format:
Routes follow some basic rules:
- Routes are loaded in the order they are specified in the routing table. This is important, because it allows you to do what we did above: create a catch-all route at the bottom of the table that runs if no other routes match.
- URL specifications (the object keys) are always enclosed between
^
and$
, so the route your specify, by default, must be a full match. A URL specifier of “/” will only match a URL of “/”, not match “/mypage” (but “/.*” will match “/mypage”). - The value of the URL specifier key is an array with exactly two elements: a top-level object name and a key within that object that points to a function to run.
- The URL specifiers can use regular expression groups to capture values out of the routed URL and will pass the values in order to the routing function.
So given our above routing table, we might set up our code like so:
Note that our top-level objects are usually just that: simple objects. You can create them as controllers, but it might introduce unneeded complexity. You really only need to use controllers if a particular object is displaying something, which we’re not doing in this case, we’re just loading other objects!
options :: attribute({…})
suppress_initial_route
tells the router that when it’s created it should not
try to route the current URL (it will try to do so by default). So if your
browser is at the URL “/users” the router will try to load the route for
“/users” when it’s created unless you set this option to true.
enable_cb
is a function that will cancel the current route if it returns a
value other than true. It is called with the URL being routed on. This lets you
do simple per-URL routing logic within your app depending on its state.
process_querystring
tells the router that we want to take the querystring
parameters into account when routing. This makes writing your routes a lot
trickier because you have to account for URL query parameters now, but also
gives you the power to route on them (and capture their values) if that’s what
your app requires.
base
tells the router that when you call route, you want it to
prepend the string options.base
to the URL when its passed to pushState
.
Note that if you set a base, it is removed post-URL-change, and will be ignored
by your routing table. So you write your routes as if base
isn’t in the URL.
This is for applications where you absolutely need all resources to fall under a
certain path. While this seems useful, it is mainly for apps that run in a
container such as a Firefox or Node-webkit app that needs all pushState
URLs
to fall under a predetermined folder (ie /content
) that otherwise would cause
a SecurityError
. In other words, don’t use base
unless you really need it.
It won’t hurt things, but it might make them harder to debug.
default_title
is used by route() to set the brwoser window’s title
on route if one is not given to it directly.
initialize :: function(routes, options)
Router’s constructor. routes
is your routing table, and options
is an object containing some or all of the allowed router options.
destroy :: function()
Destroys the router and any bindings it has (either other objects’ bindings to it or its bindings to the History object).
get_param :: function(search, key)
Convenience function to get the value of a URL query parameter out of the
current URL by its key
.
search
is the query string (preusmably you got this from window.location.search
.
route :: function(url, options)
Route to a URL. This function is provided as an abstraction around setting your
URL directly via pushState. Note that if base
is set to a string in your
options, that base is prepended to the URL that’s routed. So a base
of “/content” would case router.route('/users')
to change the URL in the
address bar to “/content/users”. However you would still write your routing
table as though “/content” didn’t exist.
It’s advised that when using the router object, you use this function to change URLs.
options
can contain the following items:
replace_state
- If true, will callHistory.replaceState
instead ofHistory.pushState
to change the URL.state
- An object that is passed as thestate
parameter toHistory.pushState
orHistory.replaceState
.title
- A string title to push to the browser’s header. If not specified,Router.options.default_title
is used instead (otherwise a blank string).
find_matching_route :: function(url, routes)
Although this function is mostly used internally, you may also sometimes need to match a URL against your routing table manually. This lets you do that:
last_url :: function()
Convenience function that returns the last successfully routed URL. Can be useful for managing various state within your application.
bind_links :: function(options)
This function makes all links in your app behave as pushState links that change
the browser’s URL without actually reloading the page. bind_links
uses event
delegation to bind an event to the window document
that listens for clicks on
<a> tags. When it detects a click, it routes the href
of the element via
History.pushState
. This lets you write your links in your app as normal links
like you would in a static app, but then use pushState to change pages.
options
can contain the following items:
do_state_change
- A function that takes one argument (the <a> tag element that was clicked) that if it returnsfalse
, will cancel the route and let the browser perform the default action.filter_trailing_slash
- If true, will remove trailing slashes from <a> tags before routing on theirhref
attribute. Note that this doesn’t affect the element itself, just the URL after it’s pulled from the element.global_state
- Any object that is used as thestate
value in the pushState call.selector
- Use a specific selector when binding to links. Note that the selector can be anything, not just links. So “ul.trees li” would bind to any <li> within a <ul class=”trees”> tag.exclude_class
- A classname used to exclude specific links from pushState navigation. So a value of “nolink” would exclude any <a class=”nolink”> elements from being bound. This option is ignored if theselector
options is provided.rewrite
- An optional function that is given the final URL to be routed on by a link bound withbind_links
. The value it returns is used as the new URL passed to route.
bind_links
has a few criteria it must meet before triggering a
route/History.pushState
:
- Control/Shift/Alt keys must not be pressed, otherwise the browser is allowed to do its default action (open a new tab, for instance).
- The <a> tag must not have a
target="_blank"
attribute, otherwise the browser default is allowed (new tab/window opened). - The <a> tag must have an
href
must either be a relative URL, or must have a host that matches the current host the app is on. In other words, if your app is hosted on “http://myapp.com” and you click a link in the app to “http://slizzard.com”,bind_links
will detect this and treat it like a normal external link (the browser switches pages normally).
These criteria ensure that your users have a smooth experience and can expect accepted conventions while using your app. I cannot count how many apps completely break control+click for opening a link in a new tab because they want to bind every link on the site (and suck at doing so).
Composer’s router does it right. If you experience any problems where the router breaks conventions, please let us know!