I'm tying to get the child route data('layout') from parent component using ActivatedRoute. I have tried the following example and I'm able to get the data I'm looking for but only for one time. During child route changes, I don't see the updated 'layout' value.
Routes
const routes: Routes = [
{
path: '',
component: Layout1Component,
children: [
{
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{
path: 'home',
loadChildren: './pages/analytics/analytics.module#AnalyticsModule',
data: {
layout: 'boxed'
}
},
{
path: 'files',
loadChildren: './pages/files/files.module#FilesModule',
data: {
layout: 'full'
}
}
]
}
];
Layout1Component
export class Layout1Component implements OnInit {
constructor(private service: AppService, route: ActivatedRoute) {
this.layout = route.snapshot.firstChild.data['layout'];
route.url.subscribe(() => {
console.log(route.snapshot.firstChild.data['layout']);
});
}
}
I want the console.log to be printed with updated 'layout' value every time the route is changed.
Please help me fix this.
From my understanding, the reason why you're getting the data only once is because ActivatedRoute gets the url as a BehaviorSubject when it is instantiated.
function createActivatedRoute(c: ActivatedRouteSnapshot) {
return new ActivatedRoute(
new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams),
new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c);
}
Here's the link for the above snippet
This means that when you subscribe, you'll only get that first stored value.
One way to solve it is to make use of the router events.
Assuming you have something like this:
const routes: Route[] = [
{
path: '',
component: HelloComponent,
children: [
{
path: 'foo',
loadChildren: () => import('path/to/foo').then(m => m.FooModule),
data: {
name: 'andrei'
}
},
{
path: 'bar',
loadChildren: () => import('path/to/bar').then(m => m.BarModule),
data: {
name: 'john'
}
},
]
},
]
Here is a possible solution:
constructor (private router: Router, private route: ActivatedRoute) { }
ngOnInit () {
const routeEndEvent$ = this.router.events
.pipe(
filter(e => e instanceof NavigationEnd),
tap(() => console.warn("END")),
);
this.router.events
.pipe(
filter(e => e instanceof ChildActivationEnd && e.snapshot.component === this.route.component),
buffer(routeEndEvent$),
map(([ev]) => (ev as ChildActivationEnd).snapshot.firstChild.data),
takeUntil(this.ngUnsubscribe$)
)
.subscribe(childRoute => {
console.log('childRoute', childRoute);
})
}
routeEndEvent$ is an observable that will emit only when the route is ready(NavigationEnd)
filter(e => e instanceof ChildActivationEnd && e.snapshot.component === this.route.component): after examining a while the events emitted by the router, I reached this solution to determine whether the component(HelloComponent) is the parent(this.route.component) of the route which changed(e.snapshot.component).
buffer(routeEndEvent$): we only want to act when the router reached its final state(NavigationEnd). here you can read more about the buffer operator