Durandal 2.0 routing parameterized routes (same route different parameter) - durandal

I am probably missing something basic, but when building navigation I am attempting to define multiple parameterized routes in the shell. The idea is that all of these routes will pass the user through to the same view/vm, but the parameter will determine content that is displayed after an ajax call). The routing itself works well, but the title is always passed through from the first route in the list.
activate: function () {
router.makeRelative({moduleId: 'viewmodels'}).map([
{
route: 'grid/:page',
title: 'Title 1',
moduleId: 'grid',
nav: 3,
hash: '#grid/param1'
},
{
route: 'grid/:page',
title: 'Title 2',
moduleId: 'grid',
nav: 2,
hash: '#grid/param2'
},
{
route: 'grid/:page',
title: 'Title 3',
moduleId: 'grid',
nav: 4,
hash: '#grid/param3'
},
{
route: 'grid/:page',
title: 'Title 4',
moduleId: 'grid',
nav: 5,
hash: '#grid/param4'
}
]).buildNavigationModel();
};
So, regardless of which of the generated links a user clicks on, the Title is always returned as 'Title 1'. Nav order does not matter. The first physical object in the list will supply the title for all links. If I hard code the links into shell.html and use a splat in the router I do not have this problem, however, hard coding the links is not feasible nor desirable for the app.
So, the question is, what am I doing wrong?

In the code above there's truly only one route 'grid/:page'. By defining a parameterized route the router maps everything the match grid/:page to the first route.
See more about that in the router documentation http://durandaljs.com/documentation/Using-The-Router/.
Instead of using the router.navigationModel() build your own small navigation model.
Top level down approach:
Step 1 Defining a grid route with an optional parameter (/:page).
router
.makeRelative({moduleId: 'viewmodels'})
.map([
{
route: 'grid(/:page)',
title: 'grid page',
moduleId: 'grid',
hash: '#grid'
}
])
.buildNavigationModel();
Step 2 Navigation model
define(['plugins/router', 'knockout', './navItem'], function( router, ko, NavItem ) {
var ctor = function(){
this.childRouter = router;
this.param = ko.observable('');
this.navigation = ko.observableArray([
new NavItem({title: 'Title 1', param: 'param1'}),
new NavItem({title: 'Title 2', param: 'param2'}),
new NavItem({title: 'Title 3', param: 'param3'}),
new NavItem({title: 'Title 4', param: 'param4'})
]);
};
ctor.prototype.activate = function(param){
this.param('Param: ' + (param || 'no param!'));
};
return ctor;
});
Step 3 Navigation item model
define(['plugins/router'], function( router ) {
var ctor = function(options){
this._options = options || {};
this.init(this._options)
};
ctor.prototype.init = function(options){
this.title = options.title;
this.param = options.param;
this.hash = '#extras/optional/' + this.param;
};
ctor.prototype.isActive = function(){
return router.activeInstruction().fragment === this.hash.replace('#', '');
};
return ctor;
});
Step 4 Navigation view
<div class="navbar">
<div class="navbar-inner">
<ul class="nav" data-bind="foreach: navigation">
<li data-bind="css: { active: isActive() }">
<a data-bind="attr: { href: hash }, html: title"></a>
</li>
</ul>
<div class="loader pull-right" data-bind="css: { active: childRouter.isNavigating }">
<i class="icon-spinner icon-2x icon-spin"></i>
</div>
</div>
<div>
<h3 data-bind="text: param"></h3>
</div>
</div>
Live version can be seen at: http://dfiddle.github.io/dFiddle-2.0/#extras/optional.
Feel free to fork and adjust to your liking.

Related

Vue JS Nested Lists: Pass Props to Child, and to Child of Child

I'm trying to create nested lists 3 levels deep with Vue.js 2.x. I'm passing props from the outer list to the middle list and from there to the inner list.
However, only the outer list renders. No errors are thrown. The inner lists seem not to receive any data.
Fiddle of my code Js fiddle
Markup
<div id="app">
<ul>
<outer-item v-for="item in items" v-bind:item="item">
<ul>
<mid-item v-for="mid in item.mids" v-bind:mid="mid">
<ul>
<inside-item v-for="inside in mid.insides"
v-bind:inside="inside"></inside-item>
</ul>
</mid-item>
</ul>
</outer-item>
</ul>
</div>
JavaScript
Vue.component('outer-item', {
props: ['item'],
template: '<li>{{item.text}}</li>'
})
Vue.component('mid-item', {
props: ['mid'],
template: '<li>{{mid.text}}</li>'
})
Vue.component('inside-item', {
props: ['inside'],
template: '<li>{{inside.text}}</li>'
})
var app = new Vue({
el: '#app',
data: function () {
return {
items: [{
text: 'Outer A',
mids: [{
text: 'Mid A',
insides: [{
text: 'Inside A'
}, {
text: 'Inside B'
}]
}, {
text: 'Mid B',
insides: [{
text: 'Inside C'
}, {
text: 'Inside D'
}]
}]
},
]
}
}
})
The problem is quite simple: You have components which all have lis as templates. Now you're trying to bind other components into these lis - that doesn't make sense I think. :)
The fix: Change the templates to use Vue's <slot></slot> and add the text output:
// Do this for all other components
Vue.component('outer-item', {
props: ['item'],
template: '<div><li>{{item.text}}</li><slot></slot></div>'
})

trying to create aurelia routing in plunker

I am working on creating a page in plunker to demo aurelia routing. Here is the link. For some reason, I am unable to show the route in the page. I am able to run similar code in my local environment just fine. I think it is something in plunker that has to be done differently.
Here is the code:
app.html
<template>
<h1>Hello</h1>
<div class="row col-lg-6 col-lg-offset-3">
<div class="btn-group col-sm-offset-1" role="group" aria-label="...">
<a repeat.for="row of router.navigation" class="${row.isActive ? 'active btn btn-primary' : 'btn btn-default'}" href.bind="row.href">
${row.title}
</a>
</div>
<router-view></router-view>
</div>
</template>
app.ts
import { Aurelia, PLATFORM } from "aurelia-framework";
import { Router, RouterConfiguration } from "aurelia-router";
export class App {
router: Router;
// Configure Routing
configureRouter(config: RouterConfiguration, router: Router): void {
console.log("Aurelia routing");
config.title = "Aurelia Routing";
// config.options.root = "/";
config.map([
{
route: "",
redirect: "home",
settings: { icon: "home" }
},
{
route: "home",
moduleId: "./Home",
nav: true,
title: "Home",
settings: { icon: "home" }
},
{
route: "/support",
moduleId: "./Support",
nav: true,
title: "Support Request",
settings: { icon: "home" }
}
]);
this.router = router;
console.log(router);
}
// console.log(this.router);
}
Further details, such as bootstrapping, etc can be found in plunker link.
There's a typo/error in your main.ts:
aurelia.use.basicConfiguration()
Should be:
aurelia.use.standardConfiguration()
Changing this, I saw the console.log messages you put in the configuration, but I got another error, but the routing works now.

active class handle for parent/child routers in aurelia

Am new to aurelia, i have a main menu in left side, one of the menu(mail) is having sub-menus (inbox,sent,trash). i need to do if the sub menu is active(#current URL, #activeclass, #CSS ) needs to keep active class for the parent menu (mail).
app.js
export class App {
configureRouter(config, router){
config.title = 'DMS';
config.map([
{ route: ['dashboard',''], name: 'Dashboard',
moduleId: './templates/dashboard/dashboard', nav: true, title:'Dashboard',settings:{'img' : 'ic-dashboard.png'} },
{ route: ['settings'], name: 'Settings',
moduleId: './templates/settings/settings', nav: true, title:'Settings' ,settings:{'icon' : 'settings'} },
{ route: ['inbox'], name: 'inbox',
moduleId: './templates/mail/inbox/inbox', nav: true, title:'Mail' ,settings:{'img' : 'mail.png'} },
{ route: ['inbox/trash'], name: 'trash',
moduleId: './templates/mail/trash/trash', title:'Mail' },
{ route: ['inbox/sent'], name: 'sent',
moduleId: './templates/mail/sent/sent', title:'Mail'},
]);
this.router = router;
}
}
Menu list also applying active class
<div class="row col s12 ${row.isActive ? 'active' : ''}" repeat.for = "row of router.navigation" >
<a href.bind = "row.href">
<div class="col s2 " >
<div if.bind="row.settings.img">
<img src="src/assets/${row.settings.img}">
</div>
<div if.bind="row.settings.icon">
<i class="tiny material-icons">${row.settings.icon}</i>
</div>
</div>
</a>
</div>
sub-menu url
<div class="col s8 offset-s2 mail_actionLst">
<ul>
<li class="inbox mail_active"> Inbox <span>(43)</span> </li>
<li class="sent">Sent </li>
<li class="trash">Trash</li>
</ul>
</div>
How to set parent class active.
Because these are different routes, not parent + children.
You should describe child route in parent class.
You can add code to inbox.js:
configureRouter(config, router){
config.map([
{ route: ['trash'], name: 'trash',
moduleId: 'path_to_trash', title:'Mail' },
{ route: ['sent'], name: 'sent',
moduleId: 'path_to_sent', title:'Mail'},
]);
this.router = router;
}
Don't forget to add
<router-view></router-view>
to inbox.html.

how to defime routing for multiple subviews

I am developing and application using durandal. I have two sections in my page with different navigation menu. I am using composition to keep make these subviews. But I am not sure how to define routes for these sections.
In your shell.js
{ route: 'work*details', moduleId: 'profile/index', title: 'Profil', nav: 1, hash: '#profil', authorize: ["User", "Administrator"] },
in the subfolder index.js
var childRouter = router.createChildRouter()
.makeRelative({
moduleId: 'viewmodels/marketplace',
fromParent: true
}).map(userRoute[
// put you sub nav here
]).buildNavigationModel();
return {
router: childRouter,
work: ko.computed(function () {
return ko.utils.arrayFilter(childRouter.navigationModel(), function (route) {
return route.type == 'intro';
});
})
};
the markup
<ul class="nav navbar-nav">
<!--ko foreach: work-->
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }, text: title"></a>
</li>
<!--/ko-->
</ul>
find out more here.

Durandal 2.0 - Child routers intended for nested menus?

I've building an app, and wanting to show a 2-tier menu, with both tiers always available. Durandal 2.0 introduced their new router, which supports 'child routers', which allow for easier deeplinking.
My question - Can I have my 'child' navigation routes permanently loaded (and a sub menu rendered when the parent is not active), or is the 'child router' design intended to lazy-load them to evaluate them once a deeplink is to be evaluated?
The Durandal examples show main navigation register a splat route, then when that view model is loaded/activated, the child router is registered.
E.g.: In the example provided with Durandal 2.0, the mian nev is registered in shell.js
router.map([
{ route: ['', 'home'], moduleId: 'hello/index', title: 'Validation test', nav: true },
{ route: 'knockout-samples*details', moduleId: 'ko/index', title: 'Knockout Samples', nav: true, hash: '#knockout-samples' }
]).buildNavigationModel()
.activate();
And the ko/index.js view model then registers the children (on activate())
var childRouter = router.createChildRouter()
.makeRelative({
moduleId:'ko',
fromParent:true
}).map([
{ route: '', moduleId: 'helloWorld/index', title: 'Hello World', type: 'intro' },
{ route: 'helloWorld', moduleId: 'helloWorld/index', title: 'Hello World', type: 'intro', nav: true},
{ route: 'clickCounter', moduleId: 'clickCounter/index', title: 'Click Counter', type: 'intro', nav: true}
]).buildNavigationModel();
Whereas I'm looking to define my routes in one place, e.g.:
var routes = [
{ route: ['', 'home'],
moduleId: 'hello/index',
title: 'Validation test',
nav: true },
{ route: 'knockout-samples*details',
moduleId: 'ko/index',
moduleRootId: 'ko', // Custom property to make child routes easier
title: 'Knockout Samples',
nav: true,
hash: '#knockout-samples',
childRoutes: [
{ route: '', moduleId: 'helloWorld/index', title: 'Hello World', type: 'intro' },
{ route: 'helloWorld', moduleId: 'helloWorld/index', title: 'Hello World', type: 'intro', nav: true},
{ route: 'clickCounter', moduleId: 'clickCounter/index', title: 'Click Counter', type: 'intro', nav: true}
]}
])
And the part I'm struggling with is registering the child routes, and have them evaulate correctly when navigating to them. (Rendering the menu would come later...)
// Load routes router.map(routes.navRoutes).buildNavigationModel().mapUnknownRoutes(function(instruction)
{
logger.logError('No Route Found', instruction.fragment, 'main', true);
});
// Try load child routes...
$.each(routes.navRoutes, function(index, parentRoute) {
if (!parentRoute.childRoutes) {
return;
}
var childRouter = router.createChildRouter()
.makeRelative({
moduleId: parentRoute.moduleRootId,
fromParent: true // also tried false + non-splat routes...
}).map(parentRoute.childRoutes).buildNavigationModel();
});
I've encountered various errors in getting this nav to render. Mostly internal router.js errors when trying to calculate the hash (either from a non-activated parent, or the child) - so all hashes have been defined.
Once I've got it to map the navigation, the child routes don't seem to be accessible - they just load the main splat page without error.
So I'm wondering if I'm going about this the right way at all? (Registering all child routes up front, with the goal of being about to render a 2-tier menu).
I'm thinking the fallback is using something like the Durandal 1.2 answer about subrouting, using a combination of flat routing registration, custom 'isSameItem' function & computed observables to render the 2-tier navigation.
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.