J'essaie de faire un menu imbriqué avec des itinéraires angulaires.
Ce dont j'ai besoin est d'appliquer la classe à une route imbriquée si elle est active et d'appliquer la classe à un composant parent si son enfant est actif.
Comment est-ce que je réalise ceci? Pour le moment, je suis en train de créer des menus récursifs pour faciliter leur utilisation lorsque j'ai besoin d'imbrication à plusieurs niveaux.
composant.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>
composant.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;
}
}
}
Existe-t-il un moyen de savoir à partir d'un composant si son itinéraire est actif? Le truc, c’est que j’utilise [routerLink]
et non une routerLink
pour pouvoir transmettre .
en tant que lien vers la page racine.
Le meilleur que je pourrais recréer stackblitz Essayez de visiter "Dasboard". Il convient d’ajouter une classe "active" au parent li
. Si vous l'ajoutez vous-même, vous verrez la classe appliquée sur son élément et une barre de couleur à droite.
J'ai réussi à contourner le problème. StackBlitz
Cela est principalement réalisé en utilisant setTimeOut
sans heure dans l'événement navigateEnd
afin que je puisse vérifier l'état de isActive
et obtenir la valeur (s'il n'y a pas de setTimeout, la valeur est "du passé", ce qui signifie qu'il n'est pas mis à jour lorsqu'il est pris). Cela fonctionne de manière un peu délicate mais cela fonctionne
Toutes ces fonctionnalités sont construites de manière native dans le module de routeur d’Angular. C’est toute l’idée qui se cache derrière les itinéraires pour enfants. Si une route est vraiment un composant parent, il doit s'agir d'un parent dans le routage. En faisant cela, vous pouvez simplement vérifier si la route parente est active au lieu des routes attribuées arbitrairement dans le composant d'application.
De plus, cela présente l’énorme avantage de permettre au tableau de bord d’acheminer simplement vers ['../sibling']
.
Dans mes itinéraires enfants spécifiquement /home/home.routes, vous verrez une ligne commentée qui permettrait au tableau de bord de se charger automatiquement si tel était le comportement souhaité.
Vous utilisez déjà routerLinkActive
, dans votre élément de menu dynamique, mais pour que l'approche soit la plus simple possible, il convient de placer l'élément lié au routerLink comme indiqué ici .
Le plus gros problème que vous rencontrerez probablement est que les éléments routerLink imbriqués ne fonctionnent pas correctement dans le DOM, comme indiqué dans cette question . Vous devez donc arrêter la propagation.
app-composant.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>
J'ajouterai, bien que cela ne corresponde pas le titre de votre question, que ce problème est résolu plus facilement en utilisant les itinéraires parents, puis en configurant menu-item.html comme suit:
<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>
puis utilisez un sélecteur de frères et sœurs dans votre scss pour afficher ou masquer le menu.
Peut-être que vous pouvez essayer d'accéder à ActivatedRoute dans n'importe quel composant
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)
}
J'espère que ça va aider!