- options.log - a function that is called with logging info, default is noop. Pass in
true/falseor a custom logging function. - options.logError - default is true. A function that is called when transitions error (except for the special
TransitionRedirectedandTransitionCancellederrors). Pass intrue/falseor a custom error handling function. - options.pushState - default is false, which means using hashchange events. Set to
trueto use pushState. - options.root - default is
/. Use in combination withpushState: trueif your application is not being served from the root url /. - options.interceptLinks - default is true. When pushState is used - intercepts all link clicks when appropriate, prevents the default behaviour and instead uses pushState to update the URL and handle the transition via the router. You can also set this option to a custom function that will get called whenever a link is clicked if you want to customize the behaviour. Read more on intercepting links below.
- options.qs - default is a simple built in query string parser. Pass in an object with
parseandstringifyfunctions to customize how query strings get treated. - options.Promise - default is window.Promise or global.Promise. Promise implementation to be used when constructing transitions.
Configure the router with a route map. E.g.
router.map(function (route) {
route('app', {path: '/'}, function () {
route('about')
route('post', {path: ':postId'}, function () {
route('show')
route('edit')
})
})
})Nested paths are concatenated unless they start with a '/'. For example
router.map(function (route) {
route('foo', {path: '/foo'}, function () {
route('bar', {path: '/bar'}, function () {
route('baz', {path: '/baz'})
});
})
})The above map results in 1 URL /baz mapping to ['foo', 'bar', 'baz'] routes.
router.map(function (route) {
route('foo', {path: '/foo'}, function () {
route('bar', {path: 'bar'}, function () {
route('baz', {path: 'baz'})
});
})
})The above map results in 1 URL /foo/bar/baz mapping to ['foo', 'bar', 'baz'] routes.
Paths can contain dynamic segments as described in the docs of path-to-regexp. For example:
route('foo', {path: '/hello/:myParam'}) // single named param, matches /hello/1
route('foo', {path: '/hello/:myParam/:myOtherParam'}) // two named params, matches /hello/1/2
route('foo', {path: '/hello/:myParam?'}) // single optional named param, matches /hello and /hello/1
route('foo', {path: '/hello/:splat*'}) // match 0 or more segments, matches /hello and /hello/1 and /hello/1/2/3
route('foo', {path: '/hello/:splat+'}) // match 1 or more segments, matches /hello/1 and /hello/1/2/3By default, both leaf and non leaf routes can be navigated to. Sometimes you might not want it to be possible to navigate to certain routes at all, e.g. if the route is only used for data fetching and doesn't render anything by itself. In that case, you can set abstract: true in the route options. Abstract routes can still form a part of the URL.
router.map(function (route) {
route('application', {path: '/'}, function () {
route('dashboard', {path: 'dashboard/:accountId', abstract: true}, function () {
route('defaultDashboard', {path: ''})
route('realtimeDashboard', {path: 'realtime'})
});
})
})Abstract routes are especially useful when creating index subroutes as demonstrated above. The above route map results in the following URLs:
/ - ['application']
/dashboard/:accountId - ['application', 'dashboard', 'defaultDashboard']
/dashboard/:accountId/realtime - ['application', 'dashboard', 'realtimeDashboard']
Navigating to an abstract route that has an index route is equivalent to navigating to the index route. E.g. these are equivalent:
router.transitionTo('dashboard')
router.transitionTo('defaultDashboard')Generating links is also equivalent
router.generate('dashboard') === router.generate('defaultDashboard')However, if the abstract route does not have an index route, then it's not routable and can't have URLs generated.
It's also common to redirect from non leaf routes. In this example we might want to redirect from application to the defaultDashboard route. If each of your routes are backed by some route handler object, you can achieve the redirect with the following middleware:
router.use(function redirect (transition) {
var lastRoute = transition.routes[transition.routes.length - 1]
if (lastRoute.handler.redirect) {
lastRoute.handler.redirect(transition.params, transition.query)
}
})If a route path is not specified, it defaults to the name of the route, e.g.:
route('foo')
// equivalent to
route('foo', {path: 'foo'})If a route has a name with dots and no path specified, the path defaults to the last segment of the path. This special "dot" behaviour might be removed in the next major version of Cherrytree.
route('foo.bar')
// equivalent to
route('foo.bar', {path: 'bar'})Add a transition middleware. Every time a transition takes place this middleware will be called with a transition as the argument. You can call use multiple times to add more middlewares. The middleware function can return a promise and the next middleware will not be called until the promise of the previous middleware is resolved. The result of the promise is passed in as a second argument to the next middleware. E.g.
router.use(function (transition) {
return Promise.all(transition.routes.map(function (route) {
return route.options.handler.fetchData()
}))
})
router.use(function (transition, datas) {
transition.routes.forEach(function (route, i) {
route.options.handler.activate(datas[i])
})
})The transition object is itself a promise. It also contains the following attributes
idroutespathpathnameparamsqueryprevroutespathpathnameparamsquery
And the following methods
thencatchcancelretryfollowRedirectsredirectTo
During every transition, you can inspect transition.routes and transition.prev.routes to see where the router is transitioning to. These are arrays that contain a list of route descriptors. Each route descriptor has the following attributes
name- e.g.'message'path- the path segment, e.g.'message/:id'params- a list of params specifically for this route, e.g{id: 1}options- the options object that was passed to theroutefunction in themap
After the router has been configured with a route map and middleware - start listening to URL changes and transition to the appropriate route based on the current URL.
When using location: 'memory', the current URL is not read from the browser's location bar and instead can be passed in via listen: listen(path).
Transition to a route, e.g.
router.transitionTo('about')
router.transitionTo('posts.show', {postId: 1})
router.transitionTo('posts.show', {postId: 2}, {commentId: 2})Same as transitionTo, but doesn't add an entry in browser's history, instead replaces the current entry. Useful if you don't want this transition to be accessible via browser's Back button, e.g. if you're redirecting, or if you're navigating upon clicking tabs in the UI, etc.
Generate a URL for a route, e.g.
router.generate('about')
router.generate('posts.show', {postId: 1})
router.generate('posts.show', {postId: 2}, {commentId: 2})It generates a URL with # if router is in hashChange mode and with no # if router is in pushState mode.
Check if a given route, params and query is active.
router.isActive('status')
router.isActive('status', {user: 'me'})
router.isActive('status', {user: 'me'}, {commentId: 2})
router.isActive('status', null, {commentId: 2})The state of the route is always available on the router.state object. It contains activeTransition, routes, path, pathname, params and query.
Use this to inspect all the routes and their URL patterns that exist in your application. It's an array of:
{
name,
path,
routes
}listed in the order that they will be matched against the URL.
Cherrytree will extract and parse the query params using a very simple query string parser that only supports key values. For example, ?a=1&b=2 will be parsed to {a: 1, b:2}. If you want to use a more sophisticated query parser, pass in an object with parse and stringify functions - an interface compatible with the popular qs module e.g.:
cherrytree({
qs: require('qs')
})Transitions can fail, in which case the transition promise is rejected with the error object. This could happen, for example, if some middleware throws or returns a rejected promise.
There are also two special errors that can be thrown when a redirect happens or when transition is cancelled completely.
In case of redirect (someone initiating a router.transitionTo() while another transition was active) and error object will have a type attribute set to 'TransitionRedirected' and nextPath attribute set to the path of the new transition.
In case of cancelling (someone calling transition.cancel()) the error object will have a type attribute set to 'TransitionCancelled'.
If you have some error handling middleware - you most likely want to check for these two special errors, because they're normal to the functioning of the router, it's common to perform redirects.
Cherrytree can be configured to use differet implementations of libraries that manage browser's URL/history. By default, Cherrytree will use a very versatile implementation - cherrytree/lib/locations/browser which supports pushState and hashChange based URL management with graceful fallback of pushState -> hashChange -> polling depending on browser's capabilities.
Configure BrowserLocation by passing options directly to the router.
var router = cherrytree({
pushState: true
})- options.pushState - default is false, which means using hashchange events. Set to true to use pushState.
- options.root - default is
/. Use in combination withpushState: trueif your application is not being served from the root url /.
MemoryLocation can be used if you don't want router to touch the address bar at all. Navigating around the application will only be possible programatically by calling router.transitionTo and similar methods.
e.g.
var router = cherrytree({
location: 'memory'
})You can also pass a custom location in explicitly. This is an advanced use case, but might turn out to be useful in non browser environments. For this you'll need to investigate how BrowserLocation is implemented.
var router = cherrytree({
location: myCustomLocation()
})Cherrytree intercepts all link clicks when using pushState, because without this functionality - the browser would just do a full page refresh on every click of a link.
The clicks are intercepted only if:
- router is passed a
interceptLinks: true(default) - the currently used location and browser supports pushState
- clicked with the left mouse button with no cmd or shift key
The clicks that are never intercepted:
- external links
javascript:linksmailto:links- links with a
data-bypassattribute - links starting with
#
The default implementation of the intercept click handler is:
function defaultClickHandler (event, link, router) {
event.preventDefault()
router.transitionTo(router.location.removeRoot(link.getAttribute('href')))
}You can pass in a custom function as the interceptLinks router option to customize this behaviour. E.g. to use replaceWith instead of transitionTo.
There are a couple of ways to handle URLs that don't match any routes.
You can create a middleware to detects when transition.routes.length is 0 and render a 404 page.
Alternatively, you can also declare a catch all path in your route map:
router.map(function (route) {
route('application', {path: '/'}, function () {
route('blog')
route('missing', {path: ':path*'})
})
})In this case, when nothing else matches, a transition to the missing route will be initiated with transition.routes as ['application', 'missing']. This gives you a chance to activate and render the application route before rendering a 404 page.