Define the starting module in durandal 2.0 and navigate to it - durandal

It seems to me... that this way the initial route is defined via:
{ route: '', moduleId: 'viewmodels/customers', title: 'customers', nav: true },
When the application is loaded with the route '' which must be oddly set to empty then this route is loaded initially.
When I navigate now to mysite/#/customers nothing is loaded.
How can I give my route a starting module which I can use to navigate to it?
In the old router I used startModule but I can not find it in durandal 2.0.

You probably need to setup a second route with the same moduleId. Here's a live example for child routes that uses this http://dfiddle.github.io/dFiddle-2.0/#hello and http://dfiddle.github.io/dFiddle-2.0
define(['plugins/router', 'durandal/system', 'global', 'knockout'], function( router, system, global, ko ) {
var childRouter = router.createChildRouter()
.makeRelative({
moduleId: 'hello',
route: 'hello'
}).map([
{route: '', moduleId: 'default/index', title: 'Hello World', type: 'intro'},
{route: 'default', moduleId: 'default/index', title: 'Hello World', type: 'intro', nav: true},
{route: 'dFiddle', moduleId: 'dFiddle/index', title: 'Hello World', type: 'fiddle', nav: true}
]).buildNavigationModel();
// .on is mixed in an not meant to be chainable
childRouter.on('router:navigation:complete').then(global.createSampleLink);
return {
global: global,
router: childRouter,
getItemsByCategoryId: function( categoryId ) {
return ko.utils.arrayFilter(childRouter.navigationModel(), function( route ) {
return route.type === categoryId;
});
},
binding: function() {
system.log('Lifecycle : binding : hello/index');
return { cacheViews: false }; //cancels view caching for this module, allowing the triggering of the detached callback
}
};
});
This specific setup that has child routes on all top routes use router.guardRoute in shell.js to handle the empty root case. There's an open ticket https://github.com/BlueSpire/Durandal/issues/240 that discusses better handling of these kind of edge cases.
define(['plugins/router'], function (router) {
// Redirecting from / to first route
router.guardRoute = function(routeInfo, params, instance){
if (params.fragment === ''){
return routeInfo.router.routes[0].hash;
}
return true;
};
return {
router: router,
activate: function () {
router.map([
{ route: '', moduleId: 'hello/index', title: 'Hello World' },
{ route: 'hello*details', hash: '#hello', moduleId: 'hello/index', title: 'Hello World', nav: true },
...
]).buildNavigationModel();
return router.activate();
}
};
});

You can declare your routes like this:
routes = [
{ route: ['welcome', ''], title: 'Welcome', moduleId: 'welcome', nav: true },
{ route: 'Visits', title: 'Visits', moduleId: 'Visits', nav: true },
{ route: 'VisitAddEdit', title: 'Add New Vsit', moduleId: 'VisitAddEdit', nav: true }
];
Notice how the welcome route is declared as an array (['welcome', '']) that includes an empty string in addition to a name.

This may not be the best long-term solution, but my fix was to pass in a startUrl in the options object of router.activate() and then change one line of history.js (line 163, the end of the activate function):
return history.loadUrl(options.startUrl);
I call it like this after I set up the router mapping and buildNavigationModel:
router.activate({startUrl: 'hello'});
Hope that helps as a quick fix.
EDIT
To get the URL to update as well, I changed it to history.navigate(options.startUrl) instead of loadUrl.

Related

What's the correct aurelia router config for routes #/items/ AND #/items/:id/summary?

I'm trying to get a parent/child router working for this basic hierarchy:
#/items (show options to select a specific item)
#/items/:id/summary (specific item summary)
#/items/:id/detail (specific item detail)
Detail
I have the following routes defined in the parent router:
[{
route: '', redirect: 'items'
}, {
route: ['items'],
name: 'Items',
viewPorts: {
itemNavigation: {moduleId: '....'},
currentItem: { moduleId: '......' }
},
nav: true,
title: 'Items'
},
{
route: ['items/:id'],
viewPorts: {
itemNavigation: {moduleId: '......'},
currentItem: { moduleId: '......' }
},
nav: false
}...
I have the following routes defined in the 'currentItem' module child router:
[{
route: '',
moduleId: '....',
name: 'noRouteSelected',
nav: false
}, {
route: 'summary',
moduleId: '....',
name: 'summary',
nav: true,
title: 'Summary'
}, {
route: 'detail',
moduleId: '....',
name: 'detail',
nav: true,
title: 'Detail'
}...
This looks ok but if I navigate to a child route (e.g. '#/items/123/summary'), because nav is false, there's nothing shown as 'active' in the main nav menu (the red circle in the image). That's fair enough but if I merge the 2 parent configs along the lines of:
route: ['items', 'items/:id']
I get a shed load of exceptions being thrown:
Error: A value is required for route parameter 'id' in route 'investments'.
If I user the optional config:
route: ['items/:id?']
then I need to specify the href parameter in the config, but I've no idea what that should be in this scenario. Should I instead be using a Navigation Strategy? (examples would be great!)
Can anyone help? The documentation is somewhat lacking on this front.
Here's how I would structure your app:
In the top-level page:
{
route: '',
redirect: 'items'
},
{
route: 'items',
name: 'items',
moduleId: './items',
title: 'Items',
nav: true
}
In the Items page:
{
route: '',
name: 'no-item-selected',
moduleId: './some-empty-view',
nav: false
},
{
route: ':id',
name: 'item',
moduleId: './item',
nav: false
}
In the Item page:
{
route: '',
redirect: 'summary'
},
{
route: 'summary',
moduleId: './item-summary',
name: 'item-summary',
title: 'Summary',
nav: true
},
{
route: 'detail',
moduleId: './item-detail',
name: 'item-detail',
title: 'Detail',
nav: true
}
In the Item view model itself you could store the Router you get during configureRouter, and then during activate() set the title accordingly if you want to, since "Items | Item | Detail" is probably a bit silly (so I left the title out for the Item page)
You could also completely omit the nested child router in item page and just have both summary and detail in there, showing/hiding them according to some other logic. Though it can be neat to have the back/forward navigation capabilities and be able to directly link to something.
Original answer (this was addressing what turned out to be a typo, but i'll just leave it for context)
Change your route from items:id to items/:id. Basically every "segment" of a route (be it static, optional, dynamic) needs to be separated by a slash or route-recognizer doesn't map them correctly.
You don't need to make the route optional with a setup like this. You won't be selecting an item without an id anyway (and if you create a new one, you'd typically just pass new or something similar as the id so your view can distinguish)

Child route doesn't work with empty route path

I'm trying to create a route to the profile page of a user, which must be "/:username", but it doesn't work. It is described on the last line. It seems that the child routes don't work with parents that have an empty path.
app.js
configureRouter(config, router){
config.title = 'Dreampper';
//config.options.pushState = true;
config.map([
{ route: '', moduleId: 'home'},
{ route: 'login', moduleId: './components/account/login', name: 'login'},
{ route: 'register', moduleId: './components/account/register', name: 'register'}
]);
In my home.js I have:
{ route: '', moduleId: './components/timeline/timeline', name: 'timeline' },
{ route: 'welcome', moduleId: './components/account/welcome', name: 'welcome' },
{ route: 'account/EmailConfirmation/:user/:code', moduleId: './components/account/email-confirmation', name:'Email confirmation' },
{ route: ':username', moduleId: './components/profile/profile', name: 'profile' }
Someone could help me? I don't want to use redirect, because the redirect makes the URL doesn't be empty "localhost/".
Use the mapUnknownRoutes function when you configure your router. I didn't test this but you should be able to do something like this. Please also take into account Fabio's suggestion -- it's a good one. But if you really want to do it...
config.mapUnknownRoutes({ redirect: '#/' });
router.configure(config => {
config.mapUnknownRoutes(instruction => {
//check instruction.fragment
//set instruction.config.moduleId
});
});
Basically you'll want to look at the instruction.fragment to get the actual route that was specified, and then redirect it to a particular route with the parameter as whatever route was given.

Aurelia: Child Router Navigation: Route not found

Below are my views:
1. app - standard
2. home - Has a list of items on left, on selection of any, will display some content on the right side in router-view (contract-view to be loaded).
3. contract-view
app.ts: route Config:
configureRouter(config: RouterConfiguration, router: Router) {
config.title = 'Contracts Portal';
config.map([
{ route: ['', 'home'], name: 'home', moduleId: 'home', nav: true, title: 'Home' },
{ route: 'resources', name: 'resources', moduleId: 'resources', nav: true, title: 'Resources' },
{ route: 'tools', name: 'tools', moduleId: 'tools', nav: true, title: 'Tools' }
]);
this.router = router;
}
Home.ts Router Config:
configureRouter(config: RouterConfiguration, router: Router) {
config.title = "test";
config.map([
{ route: ['', 'contract-view/:id'], name: 'contract-view', moduleId: 'contract-view', nav: true, title: 'Test' }
]);
this.router = router;
}
on selection of a item in home page list, I am trying to navigate as below to load content in the right pane's router-view, in home.ts:
this.router.navigateToRoute("contract-view", { id: 4090 });
However it throws the error: Route not found: /contract-view/4090
At this point, it's still home page and default route, hence the url reads: http://localhost:9000/#/
and so it fails.
But, if I manually change the url to http://localhost:9000/#/home and then select a list item, navigation to contract-view works.
What I am I missing here?
I am looking for absolute path navigation. Tried navigating to home/contract-view but fails with error:
A route with name 'home/contract-view' could not be found. Check that name: home/contract-view was specified in the route's config.
The default route of Home.ts has a parameter:
config.map([
{ route: ['', 'contract-view/:id'], name: 'contract-view', moduleId: 'contract-view', nav: true, title: 'Test' }
]);
This might be a problem because the parameter :id is not referenced in the first name. So, I suggest you change the route as follow:
config.map([
//create another route with no parameters,
//this route will represent an empty selection of contract
{ route: [ '', 'contract-view' ], name: 'contract-view-empty', moduleId: 'contract-view-empty', Title: 'Select a Contract' }
{ route: 'contract-view/:id', name: 'contract-view', moduleId: 'contract-view', nav: true, title: 'Test' }
]);
Then, to generate a navigation link you can use route-href attr. Like this:
<a route-href="route: contract-view; params.bind: { id: 4090 }">Navigate</a>
Hope it helps!
It is an issue with Aurelia Router framework. Discussion and workaround here:
https://github.com/aurelia/skeleton-navigation/issues/230

durandal 3rd level child router

Once I declare 3rd level router, I am facing problem in navigation between modules defined in 2nd level router.
The issue is: when I navigate in between different modules at 2nd level router, activate is fired but attached is not fired for the module.
Code snippet for routers is as follow:
//1st level router
landing.router = router.map([
{ route: 'resourcebrowser*splat', title: 'Resource', moduleId: 'viewmodels/resourcebrowser', nav: true },
]).buildNavigationModel();
//2nd level router
var childRouter = parentView.router = landing.router.createChildRouter()
.makeRelative({
moduleId: 'viewmodels/tab/',
fromParent: true
}).map([
{ route: ':id/details', moduleId: 'details', title: 'Details', type: 'intro', nav: true },
{ route: ':id/docs*splat', moduleId: 'documents', title: 'Documents', type: 'intro', nav: true }
]).buildNavigationModel();
//3rd level router
var childRouter = parentView.router.createChildRouter()
.makeRelative({
moduleId: 'viewmodels/viewer/',
route: ':id/docs/'
})
.map([
{ route: 'documentviewer/:docid', moduleId: 'documentviewer', title: 'Viewer', type: 'intro', nav: true }
]).buildNavigationModel();
Kindly let me know where am I going wrong.
Check the documentation for canReuseForRoute : http://durandaljs.com/documentation/Using-The-Router.html
If you define the canReuseForRoute function and return false, I believe you will find that 2nd level router viewmodel is recreated completely as you intend.

Durandal activating to specific route not working

I have the following:
<div class="page-host" data-bind="router: { transition:'entrance' }"></div>
And with the standard router configuration, I call:
return router.activate('browse');
however, I get "hello" routed every time. I need to conditionally select which route to navigate to from within shell - how can this be done?
My routeConfig:
router.map([
{ route: ['', 'hello'], title: 'hello', moduleId: 'hello/hello', nav: true, allowedUsers: ['authenticated', 'unauthenticated'] },
{ route: 'browse', moduleId: 'browse/browse', nav: true, allowedUsers: ['authenticated'] },
{ route: 'resetpassword/:id', moduleId: 'forgot/reset', nav: false, allowedUsers: ['unauthenticated'] }
]).buildNavigationModel();
I ended up extracting the routes array and manipulating it to set the default route before calling router.map - though I'll leave this open in case there's a better way.
You can specify which route to use on activate with the startRoute argument, as follows:
return router.activate({
startRoute: "myStartRoute"
});