Reputation: 113
I need to create a multilevel navigation menu. The contents of the menu vary depending on the user. I plan on pulling the collection of navigation items that can contain an array of child items via a service that will return the data as JSON. Every example of navigation/routing I've seen uses static routes or single level menus. I have read a bit about child routing but that does not seem to be what I need. The only thing I can think of is to create a custom navigation element who's model will register the routes in a plain array using the data in the navigation items collection. I would then use this collection to render the HTML instead of using router as it contains the hierarchical information. Is there an easier way to do this. This is the first JS framework I have worked with so I'm trying to get up to speed.
Upvotes: 3
Views: 1684
Reputation: 1196
I have solved similar problem in following way:
{id: 2, parentId: 1, level: 1}
at that momentconfig.map()
and create a new tree object using items from router.navigation
(remember: I saved id
and parentId
in settings
) on router.ensureConfigured().then(_ => {createNavTree(router.navigation)})
createNavTree(...)
to render list items. (level
is used just for some styling)router.navigation
array and use its items to create own tree structure to render the navigationAlso I listen for changes in router.currentInstruction
to track menu collapsing when subitem is navigated using injected ObserverLocator
.
`import {ObserverLocator} from 'aurelia-framework';`
...
this.routerCurrentInstructionSub = this.observerLocator
.getObserver(router, 'currentInstruction')
.subscribe((newValue, oldValue) => { ... });
Note that subscription should be released when not needed to avoid memory leaks this.routerCurrentInstructionSub.dispose();
The main benefit of this solution - is that you get full-featured routes, so you can render tree subitems as active while navigating.
Also it doesn't restrict using sub routers for sub items.
Upvotes: 1
Reputation: 10887
This is fairly easy, actually. You'll just add a property to the route's settings
object. You can name it whatever you want. It'll have a collection of menu items for the sub menu. Just use that to build the sub-menu.
Here's an example: https://gist.run?id=beb5ba9155fdb2ccefcf78676879b1ca
app.html
<template>
<ul>
<li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
<a href.bind="row.href">${row.title}</a>
<ul>
<li repeat.for="sub of row.settings.subRoutes">
<a route-href="route.bind: row.config.name; params.bind: sub.params">${sub.name}</a>
</li>
</ul>
</li>
</ul>
<div class="page-host">
<router-view></router-view>
</div>
</template>
app.js
import {activationStrategy} from 'aurelia-router';
export class App {
configureRouter(config, router) {
config.title = 'Aurelia';
config.map([
{
route: ['', 'one'],
name: 'one',
moduleId: 'one',
nav: true,
title: 'Page 1',
activationStrategy: activationStrategy.invokeLifecycle,
settings: {
subRoutes: [
{
name: 'Sub-choice 1',
params: {
'foo': 'bar'
}
},
{
name: 'Sub-choice 2',
params: {
'foo': 'two'
}
}
]
}
},
{ route: 'two', name: 'two', moduleId: 'two', nav: true, title: 'Page 2' }
]);
this.router = router;
}
}
one.html
<template>
Page One<br />
Params:<br />
<pre><code>${params}</code></pre>
</template>
one.js
export class One {
activate(params) {
this.params = JSON.stringify(params);
}
}
The parameters you pass can be anything you like. For example, they could be the information on what route the should be activated on a child router on the route you're going to. You could call router.navigate...
in the activate method of the page (one.js
in the example below) to navigate to the correct route on the child router. You really can do whatever you want, since you can put any old thing you'd like on that settings object.
Upvotes: 4