Aurelia: Child Router Navigation: Route not found - aurelia

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

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)

Aurelia Child Router redirect to specific route

I have a main App router and multiple child routers. I'd like to have the option of specifying the child route to open when navigating from the parent route.
Parent Router:
configureRouter(config, router) {
this.router = router;
config.map([
{ route: ['','home'], name: 'home', moduleId: 'home/home' },
{ route: 'students/:id?', name: 'students', moduleId: 'students/students' },
{ route: 'staff', name: 'staff', moduleId: 'staff/staff' }
]);
}
Child Router for Students:
export class Students {
configureRouter(config, router) {
config.map([
{ route: ['', 'home'], name: 'student-home', moduleId: 'students/students-home' },
{ route: 'list', name: 'student-list', moduleId: 'students/student-list' },
{ route: 'profile/:id', name: 'student-profile', moduleId: 'students/profile/overview' },
{ route: 'lockers', name: 'student-lockers', moduleId: 'students/lockers/home' }
]);
this.router = router;
}
activate(params) {
if (params.id) {
console.log("Going straight to a student record for: ", params);
this.router.navigateToRoute('student-profile', {id: params.id});
}
}
}
The above scenario (using navigateToRoute() within activate) doesn't work, nor am I sure it's the best way. How can I have the option to navigate straight from the main app router to the student-profile screen if I include an id param?
I gave up on using named routes with child routers. If someone else understands them better than me, I'd be curious. However, I have found it works perfectly to just use the URL routing from any part of the app.
this.router.navigate('#/students/profile/' + record.id);
You don't need to use active in your child route. Aurelia router will go automatically to your child route.
export class Students {
configureRouter(config, router) {
config.map([
{ route: ['', 'home'], name: 'student-home', moduleId: 'students/students-home' },
{ route: 'list', name: 'student-list', moduleId: 'students/student-list' },
{ route: 'profile/:id', name: 'student-profile', moduleId: 'students/profile/overview' },
{ route: 'lockers', name: 'student-lockers', moduleId: 'students/lockers/home' }
]);
this.router = router;
}
}
Remove active and in your module "students/profile/overview"
call active(params) to get student from api or what ever you want you can do here with provied params.

# in paramertized route breaks Aurelia routes

We have sample app with router configuration defined as follows. "id" parameter in the user details route can have # in its value, such as /users/#abc. We can navigate to the user details view from users view whose "id" is #abc with no problem. However, when refreshing the details page, it goes back to the users view. Is there a way to escape character "#"?
export class App {
configureRouter(config, router) {
this.router = router;
config.title = 'Aurelia';
config.map([
{ route: ['', 'home'], name: 'home', moduleId: 'home/index' },
{ route: 'users', name: 'users', moduleId: 'users/index', nav: true },
{ route: 'users/:id', name: 'userDetail', moduleId: 'users/detail' }
]);
}
}
Since template parameters are not being encoded/decoded, they are very restrictive. Basically, this means [a-zA-Z0-9_]. Note, that this is not from documentation, but my observation.
If you need to pass any arbitrary data, just use query parameters. To do that, set up router without defining template parameter.
export class App {
configureRouter(config, router) {
this.router = router;
config.title = 'Aurelia';
config.map([
{ route: ['', 'home'], name: 'home', moduleId: 'home/index' },
{ route: 'users', name: 'users', moduleId: 'users/index', nav: true },
{ route: 'user', name: 'userDetail', moduleId: 'users/detail' }
]);
}
}
You don't have to change anything else (navigation, parameters in activate, etc.) In this way, upon navigation, it'll be turned into query parameter:
router.navigateToRoute('userDetail', { id: '#user123' });
This will result in url: http://myserver/#user?id=%23user123.

Route doesn't work when typing directly on the browser

Why the Aurelia route doesn't work when we type it directly on the browser? I have configured the Aurelia to remove the "#" from the URL. It works well when clicking on the links in the website, but when I try to type the URL directly on the browser, it only works with "#".
That's the situation:
With website links
localhost/#/route -> works
localhost/route -> works
Typing on the browser
localhost/#/route -> works
localhost/route -> doesn't work
That's my configuration:
configureRouter(config, router){
config.title = 'Dreampper';
router.baseUrl = "/";
config.options.pushState = true;
var navStrat = (instruction) => {
instruction.config.moduleId = instruction.fragment
instruction.config.href = instruction.fragment
}
config.map([
{ route: 'login', moduleId: './components/account/login', name: 'login', nav: true},
{ route: 'register', moduleId: './components/account/register', name: 'register'},
{ route: '', moduleId: './components/timeline/timeline', name: 'timeline', title: 'Timeline' },
{ route: 'welcome', moduleId: './components/account/welcome', name: 'welcome' },
{ route: ':username', moduleId: './components/profile/profile', name: 'profile', nav: false }
]);
this.router = router;
}
I have the <base href="/"> on the index.html.
Someone could help me?
PushState navigation requires server-side configuration. You must configure your server appropriately. See the second yellow box at http://aurelia.io/hub.html#/doc/article/aurelia/framework/latest/cheat-sheet/7

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.