In my Angular (v4) app, I'm trying to create a breadcrumb module. I found this S.O. question helpful, suggesting I store breadcrumb information in the router's data
property like so
{ path: '', component: HomeComponent, data: {breadcrumb: 'home'}}
But what I'd like to do is store a component in the data property, and then have the breadcrumb module extract and render it. Allowing me to interact with the breadcrumb module like this
{ path: '', component: HomeComponent, data: {breadcrumb: CustomViewComponent}}
Following the angular Dynamic Component Loader guide and a helpful blog post, I've attempted to make my breadcrumb rendering component do this:
import { Component, Input, OnInit, AfterViewInit,
ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import 'rxjs/add/operator/filter';
import { MainLogoComponent } from '../my-material/main-logo/main-logo.component';
@Component({
selector: 'app-breadcrumbs',
template: `<div #parent></div>`,
styleUrls: ['./breadcrumbs.component.scss']
})
export class BreadcrumbsComponent implements OnInit, AfterViewInit {
breadcrumbs: Array<any>;
@ViewChild('parent', {read: ViewContainerRef}) parent: ViewContainerRef;
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private router:Router, private route:ActivatedRoute) { }
ngOnInit() {
this.router.events
.filter(event => event instanceof NavigationEnd)
.subscribe(event => { // note, we don't use event
this.breadcrumbs = [];
let currentRoute = this.route.root,
url = '';
do {
let childrenRoutes = currentRoute.children;
currentRoute = null;
childrenRoutes.forEach(route => {
if(route.outlet === 'primary') {
let routeSnapshot = route.snapshot;
url += '/' + routeSnapshot.url.map(segment => segment.path).join('/');
this.breadcrumbs.push({
label: route.snapshot.data.breadcrumb,
url: url });
currentRoute = route;
}
})
} while(currentRoute);
})
}
ngAfterViewInit() {
const logoComponent = this.componentFactoryResolver
.resolveComponentFactory(this.breadcrumbs[0]); // at the moment just using breadcrumbs[0] for simplicity
this.parent.createComponent(logoComponent);
}
}
Unfortunately, it appears that the route data property cannot / will not store a component. Delving into the debug console, the data property gets saved as {data: {breadcrumb: }}
If anyone knows of a way to make this code work, or has a suggestion for a better way to achieve my goal of building a breadcrumbs component that consumes other view components, I would greatly appreciate it!!
PS: If I skip the data
property, and just manually ask the breadcrumb component to add a MainLogoComponent
to the page like so:
ngAfterViewInit() {
const logoComponent = this.componentFactoryResolver.resolveComponentFactory(MainLogoComponent);
this.parent.createComponent(logoComponent);
}
It works
And, for completeness, the code for the associated route.
const routes: Routes = [
{
path: 'calendar',
data: {
breadcrumb: MainLogoComponent
},
canActivate: [ AuthGuard ],
children: [
{
path: '',
component: CalendarComponent
}
]
}
];
UPDATE
Here is the relevant NgModule:
@NgModule({
imports: [
CommonModule,
MyMaterialModule
],
exports: [
BreadcrumbsComponent
],
entryComponents: [BreadcrumbsComponent, MainLogoComponent],
declarations: [BreadcrumbsComponent] // MainLogoComponent is declared in MyMaterialModule
})
export class BreadcrumbsModule { }