Durandal activating to specific route not working - durandal

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"
});

Related

Using the Aurelia router, how do I define two routes to the same view?

I have a sub-router. Let's say it's for fancy fruits. In that sub-router, I want to define routes to specific fruits that will show up in the navigation using RouteConfig nav: true as such:
config.map([
{ route: [":fruit"], name: "AppleFruit", moduleId: PLATFORM.moduleName("./fruity-fruit-fruit"), nav: true, title: "Apples are yum!", layoutModel: { fruit: "Apple" } },
{ route: [":fruit"], name: "LemonFruit", moduleId: PLATFORM.moduleName("./fruity-fruit-fruit"), nav: true, title: "Lemons are for booze!!", layoutModel: { fruit: "Lemon" } }
]);
Specifically, I'm wanting to do it this way because there are navigation elements on the page that rely on other routes in this sub-router, and I would like these to appear in the navigation with the others. My thought was that the layoutModel would get passed into the activate params in fruity-fruit-fruit, but it just blows up before making it to that point.
Is this possible, or what am I doing wrong?
As often happens, I have found the answer. My problem was that I was using layoutModel instead of just specifying the href. It should have been:
config.map([
{ route: [":fruit"], name: "AppleFruit", moduleId: PLATFORM.moduleName("./fruity-fruit-fruit"), nav: true, title: "Apples are yum!", href: "/fruits/Apple" },
{ route: [":fruit"], name: "LemonFruit", moduleId: PLATFORM.moduleName("./fruity-fruit-fruit"), nav: true, title: "Lemons are for booze!!", href: "/fruits/Lemon" }
]);

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

Define the starting module in durandal 2.0 and navigate to it

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.