I do hope I can help You a bit.
Can You have 'child' routers permanently loaded?
As far I know You can't ( or rather there is not simple way to achieve that). Child routers are designed to provide sub-routing capability for particular view - You can think about them as a Durandal navigation inside Durandal view. If we check the example code on Durandal page we can easily see that child routing life-time is connected with given view. What is more if we check the code of function creating child routes we will see that it creates new router and only store reference to parent router - the parent router ( in most cases main router) does not have references to its childs
router.createChildRouter = function() {
var childRouter = createRouter();
childRouter.parent = router;
return childRouter;
};
What can be done to achieve multilevel routing in main router?
I had similar problem in the past but with old version of Durandal. For this problem I had started from scratch You gave and modifed it a bit - I get rid of splat route as its intend to use with child routes and my solution will not use them.
var routes = [
{
route: ['', 'home'],
moduleId: 'viewmodels/home',
title: 'Validation test',
nav: true
},
{
route: 'knockout-samples',
moduleId: 'viewmodels/ko/index',
moduleRootId: 'viewmodels/ko', // Custom property to make child routes easier
title: 'Knockout Samples',
nav: true,
hash: '#knockout-samples',
childRoutes: [
{ route: 'simpleList', moduleId: 'simpleList', title: 'SimpleList', nav: true, hash : 'simpleList' },
{ route: 'clickCounter', moduleId: 'clickCounter', title: 'Click Counter', nav: true, hash : 'clickCounter' }
]
}
];
Next step is converting this user-friendly definition to route table which can be easily register in main router.
$.each(routes, function(index, route) {
if (route.childRoutes === undefined)
return
$.each(route.childRoutes, function(index, childRoute) {
childRoute.route = route.route + '/' + childRoute.route;
childRoute.moduleId = route.moduleRootId + '/' + childRoute.moduleId;
childRoute.title = route.title + '/' + childRoute.title;
childRoute.hash = route.hash + '/' + childRoute.hash;
childRoute.parent = route.moduleRootId;
});
routes = routes.concat(route.childRoutes);
});
Last step is standard registering router and activating it.
return router.map(routes)
.buildNavigationModel()
.activate();
How to render register routing so multilevel layout is preserved?
My code works with Bootstrap 2.3.0 and render multilevel menu as dropdown button. When route has no child routes ( so is just simple route) and has no parent ( its 1st level navigation) is rendered as simple button. If route has child routes its rendered as dropdown button and child routes are added to dropdown list.
<div class="btn-group" data-bind="foreach: router.navigationModel">
<!-- ko if: $data.childRoutes === undefined && $data.parent === undefined -->
<a data-bind="css: { active: isActive }, attr: { href: hash }, text: title" class="btn btn-info" href="#"/>
<!-- /ko -->
<!-- ko if: $data.childRoutes !== undefined -->
<div class="btn-group btn-info">
<a data-bind="css: { active: isActive }, attr: { href: hash }, text: title" class="btn btn-info" href="#"/>
<button class="btn btn-info dropdown-toggle" data-toggle="dropdown">
<span class="caret"/>
</button>
<ul class="dropdown-menu" data-bind="foreach: childRoutes">
<a data-bind="css: { active: isActive }, attr: { href: hash }, text: title" class="btn btn-info" href="#"/>
</ul>
</div>
<!-- /ko -->
</div>
The styles probably need to be bit polished but overall logic is done.