I am working with using the Angular router to dynamically add breadcrumbs. I have followed several examples and have gotten them to successfully work.
However, if I attempt incorporate a lazy-loaded module, I get the error:
Root segment cannot have matrix parameters
I have researched that issue and have not been able to find satisfactory information/fixes.
I've created a plunk with what I am trying to accomplish to save from extensive amounts of code on this page: https://plnkr.co/edit/505x2r
How can I continuing utilizing the dynamic breadcrumb creation from the router, but use lazy-loaded routes as well.
import {NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
import { RootComponent } from 'src/root/root.component';
import { IndexComponent } from 'src/index/index.component';
import { SignupComponent } from 'src/signup/signup.component';
import { SigninComponent } from 'src/signin/signin.component';
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: RootComponent,
children: [
{
path: 'signin',
loadChildren: 'src/signin/signin.module#SigninModule'
},
{
path: 'signup',
component: SignupComponent,
data: {
breadcrumb: 'Sign Up'
}
},
{
path: '',
component: IndexComponent
}
]
}
])
],
exports: [
RouterModule
]
})
export class RootRoutingModule {}
Thank you to Robert, Alex Beugnet, and DAG for their comments and getting me on the right track to a solid solution!
The final plunk is here: https://plnkr.co/edit/iedQjH?p=preview
Here are the condensed issues I ran into:
First, I had lazy loaded routes where I had data on the route in the form of breadcrumb. When running through getBreadcrumbs, the breadcrumb label would show twice. I resolved this by adding the following lines:
if (
child.snapshot.url.map(segment => segment.path).length === 0
) {
return this.getBreadcrumbs(child, url, breadcrumbs);
}
Second, I had routes that were simply a parameter input, but I needed to have it listed on the breadcrumb trail. This was resolved by mapping the child.snapshot.url and assigning the segment to the label.
const routeArray = child.snapshot.url.map(segment => segment.path);
for ( let i = 0; i < routeArray.length; i++ ) {
label = routeArray[i];
}
Third, I needed to show the parameter as well as the label for next route whether it was loaded immediately or lazy loaded via a module. I also ran into the following paths which needed to be split and labels assigned appropriately:
{
path: ':id/products',
loadChildren: 'src/products/products.module#ProductsModule',
data: {
breadcrumb: 'Products'
}
}
and
{
path: 'orders/:id',
component: OrderComponent,
data: {
breadcrumb: 'Orders'
}
},
The solution to both was:
for ( let i = 0; i < routeArray.length; i++ ) {
if ( Object.keys(child.snapshot.params).length > 0 ) {
if ( this.isParam(child.snapshot.params, routeArray[i]) ) {
label = routeArray[i];
} else {
label = child.snapshot.data[ROUTE_DATA_BREADCRUMB];
}
} else {
label = child.snapshot.data[ROUTE_DATA_BREADCRUMB];
}
const routeURL = routeArray[i];
url += `/${routeURL}`;
const breadcrumb: BreadCrumb = {
label: label,
params: child.snapshot.params,
url: url
};
breadcrumbs.push(breadcrumb);
}
private isParam(params: Params, segment: string) {
for ( const key of Object.keys(params)) {
const value = params[key];
if ( value === segment ) {
return true;
}
}
return false;
}
With all good things, there one catch: YOU HAVE TO HAVE A ROUTE FOR EACH PARAMETER.
For example, if you have url: #/quotes/123456/products/123456/Generic/Tissue, you will need the following routes:
{
path: ':id',
component: ProductComponent,
data: 'data'
},
{
path: ':id/:make',
component: ProductComponent,
data: 'data'
},
{
path: ':id/:make/:model',
component: ProductComponent,
data: 'data'
}
And you will end up with a clickable breadcrumbs that looks like this:
Home / Quotes / 123456 / Product / 123456 / Generic / Tissue
The project I am working on will only requires me to have one parameter... ever.
In the end, I am able to handle both routes loaded immediately and lazy-loaded routes which was my end-goal. I am sure there are some instances that I have not accounted for, but I feel it's pretty solid.
Regardless, I encourage folks to take what's here and run with it. Shoot me a note with any updates so I can what else this can become! Thx!