KnockoutJS HTML5 History router.
Supports template lazy-load, (a)synchronous pre-, post-hooks and is just under 250 lines.
- knockout v3.4+
- HTML5 History
- URL
- Promise
- fetch
- es6-shim
- polyfill.io recommended ( URL, Promise, fetch, es6-shim )
Upon load creates window.router
.
index.js
// on ready
window.ko.applyBindings( app );
// optional loader animation
if ( window.someLoader ) {
window.router.loader.start = window.someLoader.start;
window.router.loader.done = window.someLoader.done;
};
window.router.root.guard = function ( route ) {
// if location == '/home/foo', then route == [ 'home', 'foo' ]
// only root guard will receive whole route
// navigate to /home if location == '/'
if ( !route.length ) return Promise.reject( window.router.navigate( '/home' ) ); // reject current navigation
};
return window.router.start();
index.html
<body>
<!-- some page content -->
<!-- /home -->
<section data-bind="page: {
route: 'home',
template: 'tmpl-page-home',
guard: function( id ) {
// id === 'home'
if ( !app.me._id ) return Promise.reject( router.navigate( '/login' ) ); // reject current navigation
},
src: '/pages/home.html',
title: 'home'
}">
</section>
<!-- /login -->
<section data-bind="page: {
route: 'login',
template: 'tmpl-page-login',
guard: function( id ) {
// id === 'login'
if ( app.me._id ) return Promise.reject( router.navigate( '/home' ) ); // reject current navigation
},
src: '/pages/login.html',
title: 'login'
}">
</section>
<!-- some page content -->
</body>
page/home.html
<script type="text/html"
id="tmpl-page-home">
<!-- some page content -->
<!-- link to /home/foo -->
<a data-bind="nav: 'foo'">foo</a>
<!-- /home/foo -->
<div data-bind="page: {
route: '*',
template: 'tmpl-page-content',
guard: function ( id ) {
// guard called on any location change that involved the page
// so resolve if already was opened
if ( id === this.current ) return Promise.resolve();
// reject if misnavigation
if ( !app[ id ] ) return Promise.reject();
// reload foo table from server
return app[ id ].table.reload(); // returns Promise
},
src: '/pages/content.html'
}">
</div>
<!-- some page content -->
</script>
ko.observableArray containing pathname splitted by "/".
Updated after successful navigation.
href
is either a relative or absolute local path.
Returns a Promise.
Will resolve paths
in relation to location.origin
Will enable router and return a Promise.
Dummy function, called when navigation starts.
Assign it something that will show some loader.
Dummy function, called when navigation ends.
Assign it something that will hide some loader.
Called if path not found. Override it.
A root Page, created upon load.
Page class.
Element page bound to, none for root.
Route to match, none for root.
Values:
- '/' - will match empty route, usefull for setting a default page at some level.
- string - actual string to match.
- '*' - match anything none empty.
Title to set to the document, defaults to parent's title.
Template value to pass to Knockout's template binding.
Function to be called on an attempt to open the page.
Function accepts it's piece of route as first argument and is boud to a page, should return a Promise. Root page is the exception as it accepts whole array of routes. Useful for any kind of (a)synchronous checks/prehooks.
If you want to return a window.router.navigate( href )
wrap it into Promise.reject
so that previous navigation stops.
Function to be called on an attempt to close the page.
Bound to a page.
If you want to return a window.router.navigate( href )
wrap it into Promise.reject
so that previous navigation stops.
Current page route.
Template source to load if not found.
Knockout's bindingContext of the parent's element.
References to child pages. Filled automatically.
Return a path to the page.
Return parent.
Return child page matched to a route
.
Internal function.
Return a Promise.all
result of the page's guards.
Internal function.
Literally the function name.
Internal function.
Apply template binding to the page's elemtent.
Expands Knockout's bindingContext of the element with _page
as a reference to this page.
Internal function.
Yeah.
Internal function.
Yep.
Internal function.
Hmm.
Creates Page instance, passing value to the constructor, and bounds it to a parent's to
upon init.
Or updates existing one.
Pass either relative or absolute local path to navigate
to upon element click event.
Would not call navigate
and will pass an event, so a new tab is opened, if clicked with either MMB or Ctrl + LMB, to preserve native browser behavior.
Internal functions should not be called manually, but hey!