Angular router link active nested menu










4















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.










share|improve this question
























  • 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















4















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.










share|improve this question
























  • 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













4












4








4








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.










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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

















  • 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












3 Answers
3






active

oldest

votes


















0














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!






share|improve this answer























  • 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


















0














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.






share|improve this answer

























  • 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 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












  • 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


















0














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






share|improve this answer






















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



    );













    draft saved

    draft discarded


















    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









    0














    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!






    share|improve this answer























    • 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















    0














    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!






    share|improve this answer























    • 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













    0












    0








    0







    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!






    share|improve this answer













    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!







    share|improve this answer












    share|improve this answer



    share|improve this answer










    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

















    • 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













    0














    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.






    share|improve this answer

























    • 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 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












    • 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















    0














    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.






    share|improve this answer

























    • 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 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












    • 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













    0












    0








    0







    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.






    share|improve this answer















    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.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    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 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












    • 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











    • 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











    • 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











    0














    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






    share|improve this answer



























      0














      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






      share|improve this answer

























        0












        0








        0







        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






        share|improve this answer













        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







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 25 '18 at 14:57









        SergeySergey

        989419




        989419



























            draft saved

            draft discarded
















































            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.




            draft saved


            draft discarded














            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





















































            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







            這個網誌中的熱門文章

            Barbados

            How to read a connectionString WITH PROVIDER in .NET Core?

            Node.js Script on GitHub Pages or Amazon S3