README
wrouter
Router state machine for virtual doms. It's a wrapper around single-page and catch-links for easier client side routing. It also works in Node if you pass in a route event.
install
$ npm install wrouter
example
Node:
var Router = require('wrouter');
var struct = require('observ-struct');
var value = require('observ');
var assert = require('assert');
var routeEvent = value('');
var routerState = Router({
routeHash: {
'/animals/:id': {
component: {
state: struct({ example: 'my data' }),
render: function renderMyComponent(state) {
assert(state.example === 'my data');
}
},
// do anything that needs to happen before component.render is called
routeFn: function(params, done) {
console.log(params);
done();
}
}
},
// pass in an event implemented like `observ`. If this is omitted the
// router will listen for click events on document.body
// (via `catch-links`)
event: routeEvent
});
routerState(function onChange(state) {
var view = Router.route(state);
// {
// state: { example: 'my data' },
// render: renderMyComponent
// }
view.render(view.state);
});
routeEvent.set('/animals/3'); // { id: '3' }
try {
routeEvent.set('no-match');
}
catch(err) {
console.log(err);
// [Error: router: no match found]
}
Browser:
var Loop = require('main-loop');
var vdom = require('virtual-dom');
var h = vdom.h;
var struct = require('observ-struct');
var value = require('observ');
var noop = function(){};
var Router = require('../Router.js');
var components = require('./components');
var animalItemEvent = struct({});
function fetchAnimal(id, cb) {
setTimeout(function () {
cb({
id: id,
_name: 'iguana',
type: 'reptile'
});
}, 500);
}
var app = App();
var loop = Loop( app(), renderApp, vdom);
document.getElementById('content').appendChild(loop.target);
app(loop.update);
// root component
function App() {
var routeHash = {
'/': {
component: {
state: components.Home({home: 'home state'}),
render: components.Home.render
}
},
'/animals': {
component: {
state: components.Animals(),
render: components.Animals.render
}
},
'/animals/:id': {
component: {
state: components.AnimalItem({
event: animalItemEvent
}),
render: components.AnimalItem.render
},
// do anything that needs to be done before the view is rendered
routeFn: function(params, done) {
if ( !state.loading() ) state.loading.set(true);
fetchAnimal(params.id, function(animal) {
if (state.loading) state.loading.set(false);
animalItemEvent.set(animal);
done();
});
}
}
};
var state = struct({
loading: value(false),
router: Router({
routeHash: routeHash
})
});
return state;
}
function renderApp(state) {
var page = Router.route(state.router);
return h('div', {
style: { }
}, [
h('div', {style: {
width: '50%',
display: 'inline-block'
}}, [
menu(),
page ? page.render(page.state) : '',
]),
h('div.loading', {
style: {
width: '30%',
display: state.loading ? 'inline-block' : 'none',
textAlign: 'center',
fontSize: '2em',
}
}, ['loading'])
]);
function menu() {
return h('ul', [
h('li', [
h('a', {
href: '/'
}, 'Home')
]),
h('li', [
h('a', {
href: '/animals'
}, 'Animals')
]),
h('li', [
h('a', {
href: '/animals/1'
}, 'Animal Item')
])
]);
}
}