I am trying to wrap my head around the following issue. In the root-level app.component you have the following structure:
<div class="container">
<sidebar />
<menu-bar />
<div class="h-full w-full overflow-auto">
<main class="overflow-auto">
<router-outlet></router-outlet>
</main>
</div>
</div>
The menu-bar
is only used in some of the routes and even might change. Therefore I don't want to provide a specific implementation, but more or less:
<div class="container">
<sidebar />
<ng-container *ngComponentOutlet="..." />
<div class="h-full w-full overflow-auto">
<main class="overflow-auto">
<router-outlet></router-outlet>
</main>
</div>
</div>
This outlet should be provided by a child component (indirectly). The child component depends on the current route.
ASP.NET Blazor for example has a concept called SectionOutlet
which describes exactly what I want:
Here the app.component.html:
<div class="container">
<sidebar />
<section-outlet SectionName="menu-bar" />
<div class="h-full w-full overflow-auto">
<main class="overflow-auto">
<router-outlet></router-outlet>
</main>
</div>
</div>
Now when I have a process page, this page can then easily provide the content: process-page.component.html:
<section-content SectionName="menu-bar">
<my-real-menu/>
</section>
<OtherContent/>
What is the idiomatic way of doing this in Angular?
Here are two ways to do this.
You can write a switchStatement or if statement that returns the correct menu component based on the route used.
private sub: Subscription = new Subscription();
menuComp: any;
getMenu() {
if(this.router.url.includes('/a-route') {
this.menuComp = AMenuComponent
} else if(this.router.url.includes('/b-route') {
this.menuComp = BMenuComponent
}
}
Then on router events navigation you trigger the logic to rerun.
ngOnInit() {
this.sub.add(
this.router.events.subscribe(value => {
if(value instanceof NavigationEnd)
this.getMenu();
})
);
}
ngOnDestroy() {
this.sub.unsubscribe();
}
Finally use this property to dynamically create the menu.
<div class="container">
<sidebar />
<ng-container *ngComponentOutlet="menuComp" />
<div class="h-full w-full overflow-auto">
<main class="overflow-auto">
<router-outlet></router-outlet>
</main>
</div>
</div>
Another way is to use this same approach is to specify the component on the route data property.
export const routes: Routes = [
{ path: 'a-route', component: AComponent, data: { menu: AMenuComponent } },
];
The on the component root, subscribe to the data changes on the first child, then you can access this component and render using component outlet.
private sub: Subscription = new Subscription();
menuComp: any;
ngOnInit() {
this.sub.add(
this.activatedRoute.firstChild.data.subscribe((data: any) => {
this.menuComp = data.menu;
);
}
ngOnDestroy() {
this.sub.unsubscribe();
}
Same html for this as above method.