It is often desirable in single page application to have a form of state management.
In this exercise we'll example one such way using routing. You'll be implementing a router that allows clear state management in the application.
Implement a Router
object with the following methods:
router.addRoute(routeName, handler)
Adds the given route to the router.
routeName
is the name of the route given, for example/Home
.handler
is a function to call when routing to the route is done with the belowroute
method.
router.route(routeName, handler);
routeName
is the name of the route given, for example/Home
.
Calling route
after a call to addRoute
with the same route should perform the actions in the handler. For example:
var router = new Router;
router.addRoute("/Home", function() {
console.log("HI");
});
router.route("/Home"); // will log "HI" to the console
Once you're done with the basic functionality (you may use your event emitter for this). We'll want to support route parameters.
We'll now be allowing routes to have parameters with the following format:
router.addRoute("/Home/:name", handler(params) {
params.name;
});
router.route("/Home/Gilad"); // params.name is "Gilad" in the handler.
In case two handlers match the same route, an Error should be thrown.
For extra credit (not required) you may want to use the HTML5 history API to change the displayed URL in the address bar to the current route, support the back button and instant routing on navigation. Please do not start on thos before you've finished the rest of the exercise.
Here are some tests to get the point across:
describe("The router",function() {
var router;
beforeEach(function() {
router = new Router();
});
it("supports adding a route", function(done) {
router.addRoute("/Home", function() {
done();
});
router.route("/Home");
});
it("supports adding two routes", function(done) {
router.addRoute("/Home", function() {
done();
});
router.addRoute("/Foo", function() {});
router.route("/Home");
});
it("conflicts on two conflicting routes", function(done) {
router.addRoute("/Home", function() {});
router.addRoute("/Home", function() {});
assert.throws(function(){
router.route("/Home");
});
done();
});
it("Supports passing a parameter", function(done){
router.addRoute("/Home/:foo", function(params){
assert.equal(params.foo, "Bar");
done();
});
router.route("/Home/Bar");
});
it("Supports passing two parameter", function(done) {
router.addRoute("/Home/:foo/:bar", function(params) {
assert.equal(params.foo,"Bar");
assert.equal(params.bar,"Baz");
done();
});
router.route("/Home/Bar/Baz");
});
it("Supports passing three parameter", function(done) {
router.addRoute("/Home/:foo/:bar/:baz", function(params) {
assert.equal(params.foo,"Bar");
assert.equal(params.bar,"Baz");
assert.equal(params.baz,"Foo");
done();
});
router.route("/Home/Bar/Baz/Foo");
});
it("Contains the URL as params.url", function(done) {
router.addRoute("/Home/:foo/:bar/:baz", function(params) {
assert.equal(params.url,"/Home/Bar/Baz/Foo");
done();
});
router.route("/Home/Bar/Baz/Foo");
});
it("Stores the handler in the state", function(done) {
var counter = 0;
router.addRoute("/Home", function curState(params) {
if(counter === 1){
console.error(router.state);
assert.equal(router.state, curState, "not equal");
done();
}
counter++;
});
router.route("/Home");
router.route("/Home");
});
it("supports many calls", function(done) {
var counter = 1;
router.addRoute("/Home", function curState(params) {
if(counter === 12) {
done();
}
counter++;
});
for(var i = 0; i < 120; i++) {
router.route("/Home");
}
});
it("supports routes calling each other", function(done) {
var counter = 1;
router.addRoute("/Bar", function curState(params) {
if(counter === 20){
done();
}
counter++;
router.route("/Foo");
});
router.addRoute("/Foo", function() {
router.route("/Bar");
});
router.route("/Bar");
});
});