Reputation: 3070
I have a setup something like the following:
<nav>
<li>
<a someClassToggleDirective>Menu 1</a> <!-- This anchor element toggle determines if ul.sub-menu should be expanded (displayed) -->
<ul class="sub-menu"> <!-- This is displayed when it's first sibling 'a' has 'open' class -->
<li routerLinkActive="active"><a routerLink="childComp1">Child Component 1</a></li>
<li routerLinkActive="active"><a routerLink="childComp2">Child Component 2</a></li>
...
</ul>
</li>
<li>
<a someClassToggleDirective>...</a>
<ul class="sub-menu">
...
</ul>
</li>
...
</nav>
<router-outlet></router-outlet>
To load any component, a li
item is clicked which then expands it's sub menu ul
. Clicking on one of the sub-menu items loads the corresponding component in <router-outlet></outlet>
and set the sub-menu item as active. The route would be then [root]/childComp1
for example. But if I directly navigate to [root]/childComp1
using browser's address bar, the component will be loaded but the parent ul
of the active child component is not expanded.
So, how do I get it expanded? For a sub menu (ul
) to expand, I need to add a class open
to its sibling a
. How do I do this? A way that comes to my mind is using this.router.url
to determine the component and based on the route name expand the corresponding sub-menu
. But I feel this is not the best way to do this. Is there any better way? I want to avoid any third-party libraries like jQuery.
Upvotes: 1
Views: 412
Reputation: 1827
You can use @ContentChildren
decorator to get all instances of RouterLinkActive
inside submenu. And detect whether there is active links.
For example, you can create directive, which keeps list of links. And on every route change toggle expand
prop.
Directive
import {Directive, HostListener, HostBinding, ContentChildren, QueryList} from '@angular/core';
import {Router, RouterLinkActive, NavigationEnd} from '@angular/router';
import 'rxjs/Rx';
import { Subscription } from 'rxjs/Subscription';
@Directive({ selector: '[listToggle]' })
export class ListToggleDirective {
// Collect RouterLinkActive instances
@ContentChildren(RouterLinkActive) links: QueryList<RouterLinkActive>;
// If expand=true - show submenu
@HostBinding('class.expand')
expand: boolean;
routerSub: Subscription;
constructor(private router: Router) {
}
ngAfterContentInit(): void {
// Detect if there is active links
this.routerSub = this.router.events
.filter(e => e instanceof NavigationEnd)
.subscribe(() => this.detectActiveLink());
}
ngOnDestroy(): void {
if (this.routerSub) {
this.routerSub.unsubscribe();
}
}
detectActiveLink(): void {
setTimeout(() => {
const hasActive = this.links.some(link => link.isActive);
this.expand = hasActive;
});
}
}
Usage
<ul>
<li listToggle>
<a>Open menu 1</a>
<ul class="sub-menu">
<a routerLink="/cmp1" routerLinkActive="active">cmp1</a>
<a routerLink="/cmp2" routerLinkActive="active">cmp2</a>
</ul>
</li>
<li listToggle>
<a>Open menu 2</a>
<ul class="sub-menu">
<a routerLink="/cmp3" routerLinkActive="active">cmp3</a>
</ul>
</li>
</ul>
Styles
li[listtoggle] {
&.expand .sub-menu {
display: block;
}
.sub-menu {
display: none;
}
}
Hope this helps
Upvotes: 1