Angular router link active nested menu
I'm trying to make a nested menu with angular routes.
What I need is to apply class to a nested route if it's active and apply class to a parent component if its child is active.
How do I achieve this? For now I'm doing recursive menu building for ease of use when I need multilevel nesting.
component.html
<a (click)="toggleActive()" [class.active]="isActive" [routerLink]="menuItem.link" *ngIf="menuItem.link; else noLink">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
<ng-container *ngIf="menuItem.children && menuItem.children.length > 0">
<ul class="nav child_menu" [class.active]="isActive" routerLinkActive="active"
*ngFor="let item of menuItem.children">
<li menu-item [menuItem]="item" (checkActive)="updateActiveState()"></li>
</ul>
</ng-container>
<ng-template #noLink>
<a (click)="toggleActive()" [class.active]="isActive">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
</ng-template>
component.ts
@Component(
selector: '[menu-item]',
templateUrl: './menu-item.component.html',
styleUrls: ['./menu-item.component.scss'],
host:
'[class.active]': 'hostActive && isActive',
'[class.active-sm]': 'hostActiveSm && isActive'
)
export class MenuItemComponent implements OnInit, AfterViewInit, OnChanges
public menuSize;
public isActive = false;
@Input() menuItem: MenuItem;
@Output() checkActive: EventEmitter<void> = new EventEmitter<void>();
@ViewChild(MenuItemComponent) menuComponent: MenuItemComponent;
private hostActive = true;
private hostActiveSm = false;
@HostBinding('class') hostClass = this.hostActive && this.isActive ? 'active' : this.hostActiveSm && this.isActive ? 'active-sm' : '';
constructor(
private router: Router,
private uss: UiStateService,
)
ngOnInit()
this.uss.menuSubject.subscribe(msg =>
this.menuSize = msg;
if (this.menuSize === MenuSizes.sm)
this.hostActive = false;
this.hostActiveSm = true;
else
this.hostActive = true;
this.hostActiveSm = false;
this.updateActiveState();
);
this.router.events.subscribe((e: Event) =>
if (e instanceof NavigationEnd)
this.updateActiveState();
);
ngOnChanges()
ngAfterViewInit()
this.updateActiveState();
public toggleActive(): void
this.isActive = !this.isActive;
// if (this.menuComponent)
// this.menuComponent.isActive = true;
//
private updateActiveState(): void
// reset previous state
this.isActive = false;
if (this.menuComponent)
this.menuComponent.isActive = false;
// Check state of item with no els
const url = this.router.url;
console.log('URL', url, 'Menu link', this.menuItem.link);
console.log(url.match('/' + this.menuItem.link + '$/'));
if (this.menuItem && this.menuItem.link && url.match('/' + this.menuItem.link + '$/'))
this.isActive = true;
this.checkActive.emit();
if (this.menuComponent)
console.log('Menu component');
console.log(this.menuComponent, this.menuComponent.menuItem.link);
this.isActive = true;
Is there a way of knowing from a component whether its route is active? The trick is that I'm using [routerLink]
and not a routerLink
so that I can pass .
as a link to the root page.
Update
The best I could recreate stackblitz
Try to visit "Dasboard". It should add an "active" class to parent li
. If you add it yourself you would see the applied class on it's element and a colored bar on the right.
angular routing
add a comment |
I'm trying to make a nested menu with angular routes.
What I need is to apply class to a nested route if it's active and apply class to a parent component if its child is active.
How do I achieve this? For now I'm doing recursive menu building for ease of use when I need multilevel nesting.
component.html
<a (click)="toggleActive()" [class.active]="isActive" [routerLink]="menuItem.link" *ngIf="menuItem.link; else noLink">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
<ng-container *ngIf="menuItem.children && menuItem.children.length > 0">
<ul class="nav child_menu" [class.active]="isActive" routerLinkActive="active"
*ngFor="let item of menuItem.children">
<li menu-item [menuItem]="item" (checkActive)="updateActiveState()"></li>
</ul>
</ng-container>
<ng-template #noLink>
<a (click)="toggleActive()" [class.active]="isActive">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
</ng-template>
component.ts
@Component(
selector: '[menu-item]',
templateUrl: './menu-item.component.html',
styleUrls: ['./menu-item.component.scss'],
host:
'[class.active]': 'hostActive && isActive',
'[class.active-sm]': 'hostActiveSm && isActive'
)
export class MenuItemComponent implements OnInit, AfterViewInit, OnChanges
public menuSize;
public isActive = false;
@Input() menuItem: MenuItem;
@Output() checkActive: EventEmitter<void> = new EventEmitter<void>();
@ViewChild(MenuItemComponent) menuComponent: MenuItemComponent;
private hostActive = true;
private hostActiveSm = false;
@HostBinding('class') hostClass = this.hostActive && this.isActive ? 'active' : this.hostActiveSm && this.isActive ? 'active-sm' : '';
constructor(
private router: Router,
private uss: UiStateService,
)
ngOnInit()
this.uss.menuSubject.subscribe(msg =>
this.menuSize = msg;
if (this.menuSize === MenuSizes.sm)
this.hostActive = false;
this.hostActiveSm = true;
else
this.hostActive = true;
this.hostActiveSm = false;
this.updateActiveState();
);
this.router.events.subscribe((e: Event) =>
if (e instanceof NavigationEnd)
this.updateActiveState();
);
ngOnChanges()
ngAfterViewInit()
this.updateActiveState();
public toggleActive(): void
this.isActive = !this.isActive;
// if (this.menuComponent)
// this.menuComponent.isActive = true;
//
private updateActiveState(): void
// reset previous state
this.isActive = false;
if (this.menuComponent)
this.menuComponent.isActive = false;
// Check state of item with no els
const url = this.router.url;
console.log('URL', url, 'Menu link', this.menuItem.link);
console.log(url.match('/' + this.menuItem.link + '$/'));
if (this.menuItem && this.menuItem.link && url.match('/' + this.menuItem.link + '$/'))
this.isActive = true;
this.checkActive.emit();
if (this.menuComponent)
console.log('Menu component');
console.log(this.menuComponent, this.menuComponent.menuItem.link);
this.isActive = true;
Is there a way of knowing from a component whether its route is active? The trick is that I'm using [routerLink]
and not a routerLink
so that I can pass .
as a link to the root page.
Update
The best I could recreate stackblitz
Try to visit "Dasboard". It should add an "active" class to parent li
. If you add it yourself you would see the applied class on it's element and a colored bar on the right.
angular routing
Can you please create stackblitz example?
– yurzui
Nov 17 '18 at 7:01
It's too complicated cause it requires almost full aplication to recreate
– Sergey
Nov 17 '18 at 8:13
@yurzui updated the question
– Sergey
Nov 18 '18 at 17:16
add a comment |
I'm trying to make a nested menu with angular routes.
What I need is to apply class to a nested route if it's active and apply class to a parent component if its child is active.
How do I achieve this? For now I'm doing recursive menu building for ease of use when I need multilevel nesting.
component.html
<a (click)="toggleActive()" [class.active]="isActive" [routerLink]="menuItem.link" *ngIf="menuItem.link; else noLink">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
<ng-container *ngIf="menuItem.children && menuItem.children.length > 0">
<ul class="nav child_menu" [class.active]="isActive" routerLinkActive="active"
*ngFor="let item of menuItem.children">
<li menu-item [menuItem]="item" (checkActive)="updateActiveState()"></li>
</ul>
</ng-container>
<ng-template #noLink>
<a (click)="toggleActive()" [class.active]="isActive">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
</ng-template>
component.ts
@Component(
selector: '[menu-item]',
templateUrl: './menu-item.component.html',
styleUrls: ['./menu-item.component.scss'],
host:
'[class.active]': 'hostActive && isActive',
'[class.active-sm]': 'hostActiveSm && isActive'
)
export class MenuItemComponent implements OnInit, AfterViewInit, OnChanges
public menuSize;
public isActive = false;
@Input() menuItem: MenuItem;
@Output() checkActive: EventEmitter<void> = new EventEmitter<void>();
@ViewChild(MenuItemComponent) menuComponent: MenuItemComponent;
private hostActive = true;
private hostActiveSm = false;
@HostBinding('class') hostClass = this.hostActive && this.isActive ? 'active' : this.hostActiveSm && this.isActive ? 'active-sm' : '';
constructor(
private router: Router,
private uss: UiStateService,
)
ngOnInit()
this.uss.menuSubject.subscribe(msg =>
this.menuSize = msg;
if (this.menuSize === MenuSizes.sm)
this.hostActive = false;
this.hostActiveSm = true;
else
this.hostActive = true;
this.hostActiveSm = false;
this.updateActiveState();
);
this.router.events.subscribe((e: Event) =>
if (e instanceof NavigationEnd)
this.updateActiveState();
);
ngOnChanges()
ngAfterViewInit()
this.updateActiveState();
public toggleActive(): void
this.isActive = !this.isActive;
// if (this.menuComponent)
// this.menuComponent.isActive = true;
//
private updateActiveState(): void
// reset previous state
this.isActive = false;
if (this.menuComponent)
this.menuComponent.isActive = false;
// Check state of item with no els
const url = this.router.url;
console.log('URL', url, 'Menu link', this.menuItem.link);
console.log(url.match('/' + this.menuItem.link + '$/'));
if (this.menuItem && this.menuItem.link && url.match('/' + this.menuItem.link + '$/'))
this.isActive = true;
this.checkActive.emit();
if (this.menuComponent)
console.log('Menu component');
console.log(this.menuComponent, this.menuComponent.menuItem.link);
this.isActive = true;
Is there a way of knowing from a component whether its route is active? The trick is that I'm using [routerLink]
and not a routerLink
so that I can pass .
as a link to the root page.
Update
The best I could recreate stackblitz
Try to visit "Dasboard". It should add an "active" class to parent li
. If you add it yourself you would see the applied class on it's element and a colored bar on the right.
angular routing
I'm trying to make a nested menu with angular routes.
What I need is to apply class to a nested route if it's active and apply class to a parent component if its child is active.
How do I achieve this? For now I'm doing recursive menu building for ease of use when I need multilevel nesting.
component.html
<a (click)="toggleActive()" [class.active]="isActive" [routerLink]="menuItem.link" *ngIf="menuItem.link; else noLink">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
<ng-container *ngIf="menuItem.children && menuItem.children.length > 0">
<ul class="nav child_menu" [class.active]="isActive" routerLinkActive="active"
*ngFor="let item of menuItem.children">
<li menu-item [menuItem]="item" (checkActive)="updateActiveState()"></li>
</ul>
</ng-container>
<ng-template #noLink>
<a (click)="toggleActive()" [class.active]="isActive">
<i *ngIf="menuItem.faClass" class="fa fa-menuItem.faClass"></i>
menuItem.name <span *ngIf="menuItem.children && menuItem.children.length > 0" class="fa fa-chevron-down"></span>
</a>
</ng-template>
component.ts
@Component(
selector: '[menu-item]',
templateUrl: './menu-item.component.html',
styleUrls: ['./menu-item.component.scss'],
host:
'[class.active]': 'hostActive && isActive',
'[class.active-sm]': 'hostActiveSm && isActive'
)
export class MenuItemComponent implements OnInit, AfterViewInit, OnChanges
public menuSize;
public isActive = false;
@Input() menuItem: MenuItem;
@Output() checkActive: EventEmitter<void> = new EventEmitter<void>();
@ViewChild(MenuItemComponent) menuComponent: MenuItemComponent;
private hostActive = true;
private hostActiveSm = false;
@HostBinding('class') hostClass = this.hostActive && this.isActive ? 'active' : this.hostActiveSm && this.isActive ? 'active-sm' : '';
constructor(
private router: Router,
private uss: UiStateService,
)
ngOnInit()
this.uss.menuSubject.subscribe(msg =>
this.menuSize = msg;
if (this.menuSize === MenuSizes.sm)
this.hostActive = false;
this.hostActiveSm = true;
else
this.hostActive = true;
this.hostActiveSm = false;
this.updateActiveState();
);
this.router.events.subscribe((e: Event) =>
if (e instanceof NavigationEnd)
this.updateActiveState();
);
ngOnChanges()
ngAfterViewInit()
this.updateActiveState();
public toggleActive(): void
this.isActive = !this.isActive;
// if (this.menuComponent)
// this.menuComponent.isActive = true;
//
private updateActiveState(): void
// reset previous state
this.isActive = false;
if (this.menuComponent)
this.menuComponent.isActive = false;
// Check state of item with no els
const url = this.router.url;
console.log('URL', url, 'Menu link', this.menuItem.link);
console.log(url.match('/' + this.menuItem.link + '$/'));
if (this.menuItem && this.menuItem.link && url.match('/' + this.menuItem.link + '$/'))
this.isActive = true;
this.checkActive.emit();
if (this.menuComponent)
console.log('Menu component');
console.log(this.menuComponent, this.menuComponent.menuItem.link);
this.isActive = true;
Is there a way of knowing from a component whether its route is active? The trick is that I'm using [routerLink]
and not a routerLink
so that I can pass .
as a link to the root page.
Update
The best I could recreate stackblitz
Try to visit "Dasboard". It should add an "active" class to parent li
. If you add it yourself you would see the applied class on it's element and a colored bar on the right.
angular routing
angular routing
edited Nov 18 '18 at 17:16
Sergey
asked Nov 14 '18 at 12:11
SergeySergey
989419
989419
Can you please create stackblitz example?
– yurzui
Nov 17 '18 at 7:01
It's too complicated cause it requires almost full aplication to recreate
– Sergey
Nov 17 '18 at 8:13
@yurzui updated the question
– Sergey
Nov 18 '18 at 17:16
add a comment |
Can you please create stackblitz example?
– yurzui
Nov 17 '18 at 7:01
It's too complicated cause it requires almost full aplication to recreate
– Sergey
Nov 17 '18 at 8:13
@yurzui updated the question
– Sergey
Nov 18 '18 at 17:16
Can you please create stackblitz example?
– yurzui
Nov 17 '18 at 7:01
Can you please create stackblitz example?
– yurzui
Nov 17 '18 at 7:01
It's too complicated cause it requires almost full aplication to recreate
– Sergey
Nov 17 '18 at 8:13
It's too complicated cause it requires almost full aplication to recreate
– Sergey
Nov 17 '18 at 8:13
@yurzui updated the question
– Sergey
Nov 18 '18 at 17:16
@yurzui updated the question
– Sergey
Nov 18 '18 at 17:16
add a comment |
3 Answers
3
active
oldest
votes
Maybe you can try access ActivatedRoute in any component
import Router, ActivatedRoute from '@angular/router';
constructor(private router: Router, private activatedRoute:ActivatedRoute)
console.log(activatedRoute.snapshot.url) // array of states
console.log(activatedRoute.snapshot.url[0].path)
Hope that will help!
Please reread the end of my question. I may pass.
since I'm using [routerLink] and I don't want to match this case manually since it could change over time.
– Sergey
Nov 19 '18 at 9:37
Sorry I got it wrong but you said very clear 'Is there a way of knowing from a component whether its route is active?'. Im answering that. Good luck
– freepowder
Nov 19 '18 at 10:15
add a comment |
StackBlitz
All of this functionality is built inside Angular's router module natively. This is the whole idea behind child routes. If a route is truly a parent component, it should be a parent in the routing. By doing this, you can simply check if the parent route is active instead of any of the routes arbitrarily assigned in the app component.
Additionally, this has the huge benefit of allowing dashboard to simply route to ['../sibling']
In my child routes specifically /home/home.routes, you will see a commented line that would enable the dashboard to automatically load if that was the desired behaviour.
You are already using routerLinkActive
, in your dynamic menu-item, but to have the most straightforward approach, it should be on the item bound to the routerLink as outlined here.
The biggest issue you will likely encounter is that nested routerLink elements do not play nicely in the DOM as discussed in this question so you need to stop propagation.
app-component.html
<ul app-menu-item
class="nav side-menu"
[menu]="menu">
</ul>
menu-item.html:
<li
*ngFor="let item of menu"
[routerLink]="item.link"
routerLinkActive="active"
(click)="stop($event)">
item.name
<ul app-menu-item
*ngIf="item.children"
class="nav side-menu"
[menu]="item.children">
</ul>
</li>
I will add, though it doesn't answer the title in your question, that this problem is most easily solved by using parent routes, and then setting up menu-item.html as follows:
<li *ngFor="let item of menu">
<a [routerLink]="item.link" routerLinkActive="active">
item.name
</a>
<ul class="nav side-menu"
*ngIf="item.children"
app-menu-item [menu]="item.children">
</ul>
</li>
and then using a sibling selector in your scss to show or hide the menu.
The idea is that "Home" that contains dashboard should have class active. In real application it's a kinda accordion and I need to expand an item when it's descendants are active. (by applying the class to the item).
– Sergey
Nov 21 '18 at 21:25
In my example, the styling was off, but it does have the active class. Take a look here. It may be more clear.
– Murphy4
Nov 22 '18 at 16:14
you've done it in a wrong way. I've told about recursion meanwhile in your example it's absent. That means that you've got only 2 levels of menu and if I pass withdashboard
children it won't render them.
– Sergey
Nov 22 '18 at 16:31
Updated answer and stackblitz given your feedback.
– Murphy4
Nov 23 '18 at 16:59
nice work but still not the same :) Your implementation now lacks a such thing as linkless item, because it's a strange thing when a parent item has link, meanwhile it's just a grouping item. Since, we cannot do a conditional route link (which exists only if a value present [routerLink]="" refers to a home page) we do have a problem with propagating this routerActive state to a parent that doesn't have a link itself.
– Sergey
Nov 23 '18 at 17:21
|
show 3 more comments
I've managed to workaround it. StackBlitz
Mainly it's achieved by using setTimeOut
without a time in navigateEnd
event so that I can check the state of isActive
and get the value(if there is no setTimeout the value is gotten "from the past" meaning it's not updated when it's taken). It works in a bit tricky way but it works
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53299952%2fangular-router-link-active-nested-menu%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Maybe you can try access ActivatedRoute in any component
import Router, ActivatedRoute from '@angular/router';
constructor(private router: Router, private activatedRoute:ActivatedRoute)
console.log(activatedRoute.snapshot.url) // array of states
console.log(activatedRoute.snapshot.url[0].path)
Hope that will help!
Please reread the end of my question. I may pass.
since I'm using [routerLink] and I don't want to match this case manually since it could change over time.
– Sergey
Nov 19 '18 at 9:37
Sorry I got it wrong but you said very clear 'Is there a way of knowing from a component whether its route is active?'. Im answering that. Good luck
– freepowder
Nov 19 '18 at 10:15
add a comment |
Maybe you can try access ActivatedRoute in any component
import Router, ActivatedRoute from '@angular/router';
constructor(private router: Router, private activatedRoute:ActivatedRoute)
console.log(activatedRoute.snapshot.url) // array of states
console.log(activatedRoute.snapshot.url[0].path)
Hope that will help!
Please reread the end of my question. I may pass.
since I'm using [routerLink] and I don't want to match this case manually since it could change over time.
– Sergey
Nov 19 '18 at 9:37
Sorry I got it wrong but you said very clear 'Is there a way of knowing from a component whether its route is active?'. Im answering that. Good luck
– freepowder
Nov 19 '18 at 10:15
add a comment |
Maybe you can try access ActivatedRoute in any component
import Router, ActivatedRoute from '@angular/router';
constructor(private router: Router, private activatedRoute:ActivatedRoute)
console.log(activatedRoute.snapshot.url) // array of states
console.log(activatedRoute.snapshot.url[0].path)
Hope that will help!
Maybe you can try access ActivatedRoute in any component
import Router, ActivatedRoute from '@angular/router';
constructor(private router: Router, private activatedRoute:ActivatedRoute)
console.log(activatedRoute.snapshot.url) // array of states
console.log(activatedRoute.snapshot.url[0].path)
Hope that will help!
answered Nov 19 '18 at 9:22
freepowderfreepowder
2896
2896
Please reread the end of my question. I may pass.
since I'm using [routerLink] and I don't want to match this case manually since it could change over time.
– Sergey
Nov 19 '18 at 9:37
Sorry I got it wrong but you said very clear 'Is there a way of knowing from a component whether its route is active?'. Im answering that. Good luck
– freepowder
Nov 19 '18 at 10:15
add a comment |
Please reread the end of my question. I may pass.
since I'm using [routerLink] and I don't want to match this case manually since it could change over time.
– Sergey
Nov 19 '18 at 9:37
Sorry I got it wrong but you said very clear 'Is there a way of knowing from a component whether its route is active?'. Im answering that. Good luck
– freepowder
Nov 19 '18 at 10:15
Please reread the end of my question. I may pass
.
since I'm using [routerLink] and I don't want to match this case manually since it could change over time.– Sergey
Nov 19 '18 at 9:37
Please reread the end of my question. I may pass
.
since I'm using [routerLink] and I don't want to match this case manually since it could change over time.– Sergey
Nov 19 '18 at 9:37
Sorry I got it wrong but you said very clear 'Is there a way of knowing from a component whether its route is active?'. Im answering that. Good luck
– freepowder
Nov 19 '18 at 10:15
Sorry I got it wrong but you said very clear 'Is there a way of knowing from a component whether its route is active?'. Im answering that. Good luck
– freepowder
Nov 19 '18 at 10:15
add a comment |
StackBlitz
All of this functionality is built inside Angular's router module natively. This is the whole idea behind child routes. If a route is truly a parent component, it should be a parent in the routing. By doing this, you can simply check if the parent route is active instead of any of the routes arbitrarily assigned in the app component.
Additionally, this has the huge benefit of allowing dashboard to simply route to ['../sibling']
In my child routes specifically /home/home.routes, you will see a commented line that would enable the dashboard to automatically load if that was the desired behaviour.
You are already using routerLinkActive
, in your dynamic menu-item, but to have the most straightforward approach, it should be on the item bound to the routerLink as outlined here.
The biggest issue you will likely encounter is that nested routerLink elements do not play nicely in the DOM as discussed in this question so you need to stop propagation.
app-component.html
<ul app-menu-item
class="nav side-menu"
[menu]="menu">
</ul>
menu-item.html:
<li
*ngFor="let item of menu"
[routerLink]="item.link"
routerLinkActive="active"
(click)="stop($event)">
item.name
<ul app-menu-item
*ngIf="item.children"
class="nav side-menu"
[menu]="item.children">
</ul>
</li>
I will add, though it doesn't answer the title in your question, that this problem is most easily solved by using parent routes, and then setting up menu-item.html as follows:
<li *ngFor="let item of menu">
<a [routerLink]="item.link" routerLinkActive="active">
item.name
</a>
<ul class="nav side-menu"
*ngIf="item.children"
app-menu-item [menu]="item.children">
</ul>
</li>
and then using a sibling selector in your scss to show or hide the menu.
The idea is that "Home" that contains dashboard should have class active. In real application it's a kinda accordion and I need to expand an item when it's descendants are active. (by applying the class to the item).
– Sergey
Nov 21 '18 at 21:25
In my example, the styling was off, but it does have the active class. Take a look here. It may be more clear.
– Murphy4
Nov 22 '18 at 16:14
you've done it in a wrong way. I've told about recursion meanwhile in your example it's absent. That means that you've got only 2 levels of menu and if I pass withdashboard
children it won't render them.
– Sergey
Nov 22 '18 at 16:31
Updated answer and stackblitz given your feedback.
– Murphy4
Nov 23 '18 at 16:59
nice work but still not the same :) Your implementation now lacks a such thing as linkless item, because it's a strange thing when a parent item has link, meanwhile it's just a grouping item. Since, we cannot do a conditional route link (which exists only if a value present [routerLink]="" refers to a home page) we do have a problem with propagating this routerActive state to a parent that doesn't have a link itself.
– Sergey
Nov 23 '18 at 17:21
|
show 3 more comments
StackBlitz
All of this functionality is built inside Angular's router module natively. This is the whole idea behind child routes. If a route is truly a parent component, it should be a parent in the routing. By doing this, you can simply check if the parent route is active instead of any of the routes arbitrarily assigned in the app component.
Additionally, this has the huge benefit of allowing dashboard to simply route to ['../sibling']
In my child routes specifically /home/home.routes, you will see a commented line that would enable the dashboard to automatically load if that was the desired behaviour.
You are already using routerLinkActive
, in your dynamic menu-item, but to have the most straightforward approach, it should be on the item bound to the routerLink as outlined here.
The biggest issue you will likely encounter is that nested routerLink elements do not play nicely in the DOM as discussed in this question so you need to stop propagation.
app-component.html
<ul app-menu-item
class="nav side-menu"
[menu]="menu">
</ul>
menu-item.html:
<li
*ngFor="let item of menu"
[routerLink]="item.link"
routerLinkActive="active"
(click)="stop($event)">
item.name
<ul app-menu-item
*ngIf="item.children"
class="nav side-menu"
[menu]="item.children">
</ul>
</li>
I will add, though it doesn't answer the title in your question, that this problem is most easily solved by using parent routes, and then setting up menu-item.html as follows:
<li *ngFor="let item of menu">
<a [routerLink]="item.link" routerLinkActive="active">
item.name
</a>
<ul class="nav side-menu"
*ngIf="item.children"
app-menu-item [menu]="item.children">
</ul>
</li>
and then using a sibling selector in your scss to show or hide the menu.
The idea is that "Home" that contains dashboard should have class active. In real application it's a kinda accordion and I need to expand an item when it's descendants are active. (by applying the class to the item).
– Sergey
Nov 21 '18 at 21:25
In my example, the styling was off, but it does have the active class. Take a look here. It may be more clear.
– Murphy4
Nov 22 '18 at 16:14
you've done it in a wrong way. I've told about recursion meanwhile in your example it's absent. That means that you've got only 2 levels of menu and if I pass withdashboard
children it won't render them.
– Sergey
Nov 22 '18 at 16:31
Updated answer and stackblitz given your feedback.
– Murphy4
Nov 23 '18 at 16:59
nice work but still not the same :) Your implementation now lacks a such thing as linkless item, because it's a strange thing when a parent item has link, meanwhile it's just a grouping item. Since, we cannot do a conditional route link (which exists only if a value present [routerLink]="" refers to a home page) we do have a problem with propagating this routerActive state to a parent that doesn't have a link itself.
– Sergey
Nov 23 '18 at 17:21
|
show 3 more comments
StackBlitz
All of this functionality is built inside Angular's router module natively. This is the whole idea behind child routes. If a route is truly a parent component, it should be a parent in the routing. By doing this, you can simply check if the parent route is active instead of any of the routes arbitrarily assigned in the app component.
Additionally, this has the huge benefit of allowing dashboard to simply route to ['../sibling']
In my child routes specifically /home/home.routes, you will see a commented line that would enable the dashboard to automatically load if that was the desired behaviour.
You are already using routerLinkActive
, in your dynamic menu-item, but to have the most straightforward approach, it should be on the item bound to the routerLink as outlined here.
The biggest issue you will likely encounter is that nested routerLink elements do not play nicely in the DOM as discussed in this question so you need to stop propagation.
app-component.html
<ul app-menu-item
class="nav side-menu"
[menu]="menu">
</ul>
menu-item.html:
<li
*ngFor="let item of menu"
[routerLink]="item.link"
routerLinkActive="active"
(click)="stop($event)">
item.name
<ul app-menu-item
*ngIf="item.children"
class="nav side-menu"
[menu]="item.children">
</ul>
</li>
I will add, though it doesn't answer the title in your question, that this problem is most easily solved by using parent routes, and then setting up menu-item.html as follows:
<li *ngFor="let item of menu">
<a [routerLink]="item.link" routerLinkActive="active">
item.name
</a>
<ul class="nav side-menu"
*ngIf="item.children"
app-menu-item [menu]="item.children">
</ul>
</li>
and then using a sibling selector in your scss to show or hide the menu.
StackBlitz
All of this functionality is built inside Angular's router module natively. This is the whole idea behind child routes. If a route is truly a parent component, it should be a parent in the routing. By doing this, you can simply check if the parent route is active instead of any of the routes arbitrarily assigned in the app component.
Additionally, this has the huge benefit of allowing dashboard to simply route to ['../sibling']
In my child routes specifically /home/home.routes, you will see a commented line that would enable the dashboard to automatically load if that was the desired behaviour.
You are already using routerLinkActive
, in your dynamic menu-item, but to have the most straightforward approach, it should be on the item bound to the routerLink as outlined here.
The biggest issue you will likely encounter is that nested routerLink elements do not play nicely in the DOM as discussed in this question so you need to stop propagation.
app-component.html
<ul app-menu-item
class="nav side-menu"
[menu]="menu">
</ul>
menu-item.html:
<li
*ngFor="let item of menu"
[routerLink]="item.link"
routerLinkActive="active"
(click)="stop($event)">
item.name
<ul app-menu-item
*ngIf="item.children"
class="nav side-menu"
[menu]="item.children">
</ul>
</li>
I will add, though it doesn't answer the title in your question, that this problem is most easily solved by using parent routes, and then setting up menu-item.html as follows:
<li *ngFor="let item of menu">
<a [routerLink]="item.link" routerLinkActive="active">
item.name
</a>
<ul class="nav side-menu"
*ngIf="item.children"
app-menu-item [menu]="item.children">
</ul>
</li>
and then using a sibling selector in your scss to show or hide the menu.
edited Nov 23 '18 at 16:58
answered Nov 21 '18 at 18:52
Murphy4Murphy4
520613
520613
The idea is that "Home" that contains dashboard should have class active. In real application it's a kinda accordion and I need to expand an item when it's descendants are active. (by applying the class to the item).
– Sergey
Nov 21 '18 at 21:25
In my example, the styling was off, but it does have the active class. Take a look here. It may be more clear.
– Murphy4
Nov 22 '18 at 16:14
you've done it in a wrong way. I've told about recursion meanwhile in your example it's absent. That means that you've got only 2 levels of menu and if I pass withdashboard
children it won't render them.
– Sergey
Nov 22 '18 at 16:31
Updated answer and stackblitz given your feedback.
– Murphy4
Nov 23 '18 at 16:59
nice work but still not the same :) Your implementation now lacks a such thing as linkless item, because it's a strange thing when a parent item has link, meanwhile it's just a grouping item. Since, we cannot do a conditional route link (which exists only if a value present [routerLink]="" refers to a home page) we do have a problem with propagating this routerActive state to a parent that doesn't have a link itself.
– Sergey
Nov 23 '18 at 17:21
|
show 3 more comments
The idea is that "Home" that contains dashboard should have class active. In real application it's a kinda accordion and I need to expand an item when it's descendants are active. (by applying the class to the item).
– Sergey
Nov 21 '18 at 21:25
In my example, the styling was off, but it does have the active class. Take a look here. It may be more clear.
– Murphy4
Nov 22 '18 at 16:14
you've done it in a wrong way. I've told about recursion meanwhile in your example it's absent. That means that you've got only 2 levels of menu and if I pass withdashboard
children it won't render them.
– Sergey
Nov 22 '18 at 16:31
Updated answer and stackblitz given your feedback.
– Murphy4
Nov 23 '18 at 16:59
nice work but still not the same :) Your implementation now lacks a such thing as linkless item, because it's a strange thing when a parent item has link, meanwhile it's just a grouping item. Since, we cannot do a conditional route link (which exists only if a value present [routerLink]="" refers to a home page) we do have a problem with propagating this routerActive state to a parent that doesn't have a link itself.
– Sergey
Nov 23 '18 at 17:21
The idea is that "Home" that contains dashboard should have class active. In real application it's a kinda accordion and I need to expand an item when it's descendants are active. (by applying the class to the item).
– Sergey
Nov 21 '18 at 21:25
The idea is that "Home" that contains dashboard should have class active. In real application it's a kinda accordion and I need to expand an item when it's descendants are active. (by applying the class to the item).
– Sergey
Nov 21 '18 at 21:25
In my example, the styling was off, but it does have the active class. Take a look here. It may be more clear.
– Murphy4
Nov 22 '18 at 16:14
In my example, the styling was off, but it does have the active class. Take a look here. It may be more clear.
– Murphy4
Nov 22 '18 at 16:14
you've done it in a wrong way. I've told about recursion meanwhile in your example it's absent. That means that you've got only 2 levels of menu and if I pass with
dashboard
children it won't render them.– Sergey
Nov 22 '18 at 16:31
you've done it in a wrong way. I've told about recursion meanwhile in your example it's absent. That means that you've got only 2 levels of menu and if I pass with
dashboard
children it won't render them.– Sergey
Nov 22 '18 at 16:31
Updated answer and stackblitz given your feedback.
– Murphy4
Nov 23 '18 at 16:59
Updated answer and stackblitz given your feedback.
– Murphy4
Nov 23 '18 at 16:59
nice work but still not the same :) Your implementation now lacks a such thing as linkless item, because it's a strange thing when a parent item has link, meanwhile it's just a grouping item. Since, we cannot do a conditional route link (which exists only if a value present [routerLink]="" refers to a home page) we do have a problem with propagating this routerActive state to a parent that doesn't have a link itself.
– Sergey
Nov 23 '18 at 17:21
nice work but still not the same :) Your implementation now lacks a such thing as linkless item, because it's a strange thing when a parent item has link, meanwhile it's just a grouping item. Since, we cannot do a conditional route link (which exists only if a value present [routerLink]="" refers to a home page) we do have a problem with propagating this routerActive state to a parent that doesn't have a link itself.
– Sergey
Nov 23 '18 at 17:21
|
show 3 more comments
I've managed to workaround it. StackBlitz
Mainly it's achieved by using setTimeOut
without a time in navigateEnd
event so that I can check the state of isActive
and get the value(if there is no setTimeout the value is gotten "from the past" meaning it's not updated when it's taken). It works in a bit tricky way but it works
add a comment |
I've managed to workaround it. StackBlitz
Mainly it's achieved by using setTimeOut
without a time in navigateEnd
event so that I can check the state of isActive
and get the value(if there is no setTimeout the value is gotten "from the past" meaning it's not updated when it's taken). It works in a bit tricky way but it works
add a comment |
I've managed to workaround it. StackBlitz
Mainly it's achieved by using setTimeOut
without a time in navigateEnd
event so that I can check the state of isActive
and get the value(if there is no setTimeout the value is gotten "from the past" meaning it's not updated when it's taken). It works in a bit tricky way but it works
I've managed to workaround it. StackBlitz
Mainly it's achieved by using setTimeOut
without a time in navigateEnd
event so that I can check the state of isActive
and get the value(if there is no setTimeout the value is gotten "from the past" meaning it's not updated when it's taken). It works in a bit tricky way but it works
answered Nov 25 '18 at 14:57
SergeySergey
989419
989419
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53299952%2fangular-router-link-active-nested-menu%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Can you please create stackblitz example?
– yurzui
Nov 17 '18 at 7:01
It's too complicated cause it requires almost full aplication to recreate
– Sergey
Nov 17 '18 at 8:13
@yurzui updated the question
– Sergey
Nov 18 '18 at 17:16