reititin (Finnish for 'router') is a small library to do essential routing. In my search for a router, I couldn't find one that matches all of the following criteria:
- does both route matching and route reversal
- match context is passed to routing function
- handles query string
- is packaged sanely (usable from any CommonJS module consumer like Webpack/Browserify without hacks)
- is a library, not a framework or global context eating monstrosity
reititin tries to do all the above.
If you use browserify or webpack
npm install reititin
If you use require.js or want to use it standalone, then UMD version is available in Releases.
If you want to build from source
git clone https://github.com/freiksenet/reititin.git
cd reititin
npm install
npm run umd(-min)
build/reititin.(min.)js
will be created.
Tests can be run with
npm test # or npm run test-watch for continious testing
Examples are using CommonJS modules, just substitute require statements with
your favorite poisonmodule system or alternatively window.Reititin.
Reititin.Router
is the main constructor function, that accepts an array of
route definitions, which are arrays of route and callback or route, name and
callback. Callbacks to be called on successful routing with match object.
All routes in Reititin have unique names. Reititin can get route in 2 different ways:
- By getting name of a named function as a routing function
- Using route path
var Reititin = require('reititin');
var routeDefs = [
// Basic route with callback and name 'routes'
['/route', function (match) {}, 'routes'],
// Route with url name, name is '/route/good'
['/route/good', function (match) {}],
// Route with parameters, name is 'route', :id is parameter
['/route/:id', function (match) {}, 'route'],
// Route with *splat, matches url fragment
['/splat/*splat', function (match) {}, 'splat'],
// Route with (optional) fragment
['/optional(/thing)', function (match) {}, 'option']
];
// Creating router
var router = new Reititin.Router(routeDefs);
// Optional catch all match
routeDefs['*'] = function (match) {};
var routerWithDefault = new Reititin.Router(routeDefs);
Tries to match the url against the router rules.
If router fails to match, and there is no catch-all rule, the method will return
false. If router matches, then method will return match object. Match object has
4 fields, name
is route name, url
is full url that was matched, params
is
object of all url parameters and their value and, finally, query
is a parsed
querystring. Priority of routes is defined by the order in the list.
router.match('/ueou');
// ==> false
routerWithDefault.match('/ueoe?foo=2');
// ==> {name: '*', url: '/ueoe', params: {url: '/ueoe'}, query: {foo: '2'}}
router.match('/route');
// ==> {name: 'routes', url: '/route', params: {}, query: {}}
router.match('/route/5');
// ==> {name: 'route', url: '/route/5', params: {id: 5}, query: {}}
router.match('/route/5?foo=baz&gar=brak');
// ==> {name: 'route', url: '/route/5?foo=baz&gar=brak', params: {id: 5}, query: {foo: 'baz', gar: 'brak'}}
router.match('/splat/foo/bar/baz');
// ==> {name: 'splat', url: '/splat/foo/bar/baz', params: {splat: 'foo/bar/baz'}, query: {}}
Tries to match the route with Route.match and calls the given callback with match object, if match is successful.
If router can't match, throws an error. If router matches, returns match object.
router.route('/ueou');
// ==> throws error
router.route('/route');
// ==> {name: 'routes', url: '/route', params: {}, query: {}}
If there is a named route by name that matches given params, returns the route reconstructed url with querystring matching the query. Returns false if no match is found.
router.find('ueauo'); // ==> false
router.find('route', {foo: 'bar'}); // ==> false
router.find('routes'); // ==> '/route'
router.find('route', {id: 5}); // ==> '/route/5'
router.find('routes', {}, {foo: [5, 2], bar: 'foo'}); // ==> '/route?foo=5&foo=2&bar=foo
router.find('/route/good'); // ==> '/route/good'
Splats are parameters too.
router.find('splat', {splat: 'baz/barg}); // >== '/splat/baz/barg'
Urls with optional fragments return with all the fragments in there.
router.find('option'); // ==> '/optional/thing'
Same as Router.find, but throws an error, if there is no match. Use this method
for reversing the urls that will be consumed as is (for href
in anchor,
$.ajax, etc.), so that browser won't go to '/false'.
$.getJSON(router.reverse('routes'));
Add a new route with given name
, route
and callback
. Returns router itself
for chaining. It will override existing routes if there is one with both name
or route definition.
Difference between 'append' and 'prepend' is whether they add the routes to the beginning or end of the route list (for priority).
Removes a route, name
is either a route name or route definition. Removes
matching both by name and route definition.
In general you can hook up router manually to any kind of url handling, be it server, client HistoryAPI or hash-url. Therefore Reititin is isomorphic (same code can be used on client in server). However, reititin provides ready made solutions for routing on client with hash urls and with History API. If you are using reititin both on client and server, you should inject client helper implementation.
Reititin.HistoryRouter
is a wrapper around Reititin.Router
that uses History
API for url handling. It has same interface as normal router, with 3 additional
methods.
Binds window.onpopstate
event, to react to browser forward/back button. Also
tries to route with current url.
Unbinds the window.onpopstate
event.
Reverses the url with given name, params and query and then updates
window.history. If replace
is true, uses replaceState, uses pushState
otherwise.
Reititin.HashRouter
is a wrapper around Reititin.Router
that uses
document.location.hash urls. It has same interface as normal router, with 3
additional methods.
Binds window.onhashchange
event, will try to route when hash changes. Also
tries to route with current url, if there is no url after hash, then '/' is
assumed.
Unbinds the window.onhashchange
event.
Reverses the url with given name, params and query and then changes page has to it.