The Ultra-RESTful Microrouter for the Browser.
var router = new Concorde.Router(location.href);
router.aimWindow(window).writeBase();
Concorde can create routes for any link in your page to execute any code you want:
router.get('/hello', function () {
alert('Hello');
});
In the sample above, if any user clicks something href=/hello
, this route will
be executed instead.
Routes by default don't change the URL. You can easily enable reporting routes using two different configurations:
Dispatching to the window pushState and understanding its state when appropriate:
router.pushesState();
Dispatching to #!hashbang urls.
router.hashesState();
Both approaches maintain URL states and history. hashesState
is experimental.
But there is more! You can create routes for patterns of URIs:
router.get('/hello/*/*', function (eventElement, params) {
alert('Hello ' + params[0] + ', welcome to ' + params[1]);
});
Sample above will match clicks on href=/hello/Alexandre/Hotel
for example and
display an alert with "Hello Alexandre, welcome to Hotel". URLs can have any
number of parameters and if they are in the end of the url they're by default
optional for matching.
Routes for the POST method will be dispatched when a user submits the matching form:
router.post('/lorem', function () {
alert('Ipsum');
});
The sample above will dispatch if an user submits a <form action=/lorem method=post>
.
You can create routes for hashes starting with # instead of /
router.get('#bla', function () {
alert('I am shown when the user sees #bla in the end of the URL');
});
If some user clicks a #bla
url when router.hashesState()
is available, it
runs the matched route whatever it is but the URL is preserved so the
browser still scrolls to it and allows using of CSS :target
pseudo-selector
which is hard to polyfill.
Hash-only routes does not interfere with #!hash/bang states.
You can also leverage semantics of the rel=""
attribute using Concorde
routes. The following route performs additional processing when a route
is matched with a click on a link that has a relation type:
router.get('/hello', function () {
alert('Hello');
}).rel('next', function () {
alert('Next Hello');
});
If you click on href=/hello
it will display the first alert. But if you click
on any href=/hello rel=next
, it will display both alerts, since both the
main route and the sub-relation route matches.
A modern application is often composed of several background HTTP requests forked from the main page. This is why a browser router must be able to dispatch routes in background and foreground:
// Dispatches a synthetic foreground request to /foo
router.foreground({href: '/foo', method: 'GET'}).then(function(response) {
// Foreground dispatches change the browser current state via pushState
});
// Dispatches a synthetic background request to /foo
router.background({href: '/foo', method: 'GET'}).then(function(response) {
// Background dispatches do the same as foreground, except for the
// URL change
});
Concorde has a simple deferred HTTP client:
Concorde.HTTP({href: '/something'}).then(function (response) {
console.log(response);
});
The router is also bound to this component:
router.request({href: '/something'}).then(function (response) {
console.log(response);
});
Graceful and progressive requests allow mixing internal routing with XHR:
// Dispatches a synthetic foreground request to /foo
router.progressive({href: '/foo', method: 'GET'}).then(function(response) {
// Foreground dispatches change the browser current state via pushState
// Chooses between internal route and XHR automatically
});
// Dispatches a synthetic background request to /foo
router.graceful({href: '/foo', method: 'GET'}).then(function(response) {
// Background dispatches do the same as foreground, except for the
// URL change
// Chooses between internal route and XHR automatically
});
In order to make Concorde more efficient, it can divide routing in logic areas:
var areas = {analytics: {}};
router.areasFrom(function (areaName) {
return areas[areaName];
});
router.area("analytics", function loading() {
// This is run when some analytics route is loading
});
Routes can be attached to any function, so you can attach routing areas to visual areas:
router.areasFrom(document.querySelector);
router.area("profile", function loading() {
// This is run when the profile is loading for some reason
});
Visual areas play nice with route dispatches:
// Creates an "API" route for internal use
router.get('/api', function () {
return Math.random();
});
// Dispatches a synthetic background request to /foo
router.background({href: '/api', method: 'GET', area: '#profile'}).then(function(response) {
response.area.innerHTML = response.result;
});
The sample above loads the random number /api
returns into the #profile
element. It calls the loading()
function defined for that area and passes the
control to you on the then
handler. Both the target area and the background
result are now available for being processed!
router.area(/*...*/).media(" (some media: query) ")
support- Connection pool for fixed areas
- Serious testing is only taking place on Firefox.
- You'll need pushState. No polyfills have been tested.
- You'll need querySelector. Any polyfill can be used.
- Right-clicks on forms have been submitting them. Easy pick, but we didn't pay to much attention to it.