角路由器链接活动嵌套菜单

时间:2018-11-14 12:11:46

标签: angular routing

我正在尝试制作带有角度路线的嵌套菜单。

我需要的是,如果嵌套路由处于活动状态,则将其应用于嵌套路由;如果其子级处于活动状态,则将其应用于父组件。

我该如何实现?现在,我正在进行递归菜单构建,以在需要多层嵌套时易于使用。

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

}

是否可以从组件中知道其路由是否处于活动状态?诀窍在于,我使用的是[routerLink]而不是routerLink,因此我可以通过.作为指向根页面的链接。

更新

最好的我可以重新创建stackblitz 尝试访问“ Dasboard”。它应该向父级li添加一个“活动”类。如果您自己添加它,您将在其元素上看到应用的类,并在右侧看到一个彩色的条。

4 个答案:

答案 0 :(得分:0)

也许您可以尝试在任何组件中访问ActivatedRoute

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

希望会有所帮助!

答案 1 :(得分:0)

StackBlitz

所有这些功能都内置在Angular的路由器模块中。这是子路线背后的整个想法。如果路由确实是父组件,则它应该是路由中的父组件。这样,您可以简单地检查父路由是否处于活动状态,而不是检查app组件中任意分配的任何路由。

此外,这具有巨大的好处,即允许仪表板简单地路由到['../sibling']

在我的孩子专用于/home/home.routes的路由中,您会看到一条注释行,如果需要的话,这将使仪表板能够自动加载。

您已经在动态菜单项中使用routerLinkActive,但是要采用最直接的方法,它应该位于绑定到routerLink的项目上,如here所述。

您可能遇到的最大问题是,嵌套的routerLink元素在this question中讨论的DOM中不能很好地发挥作用,因此您需要停止传播。

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>

我将添加一个答案,尽管它没有回答您的问题的标题,但最容易解决的问题是使用父路径,然后按如下所示设置menu-item.html:

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

,然后在scss中使用同级选择器显示或隐藏菜单。

答案 2 :(得分:0)

我设法解决了这个问题。 StackBlitz

主要是通过使用setTimeOutnavigateEnd事件中没有时间来实现的,因此我可以检查isActive的状态并获取值(如果没有setTimeout,则获取值“从过去”表示使用时不会更新)。它的工作方式有些棘手,但是有效

答案 3 :(得分:0)

这是通过向子代分配模板变量并像这样在父代上对其进行检查来实现的简单方法-

<li [ngClass]="{ 'is-expanded': rla.isActive }">
  <!-- is-expanded or active or whatever class in your template makes it active. -->
  <a>Parent Item</a>
  <ul class="treeview-menu">
    <li>
      <a routerLink="/child" routerLinkActive="active" #rla="routerLinkActive"
        >Child</a
      >
    </li>
  </ul>
</li>