I have an application which routes would look like this:
/:partner/login
/:partner/tools
/:partner/other
Currently using 2.0.0-rc.5
, I can quite simply build routes that look exactly as above. However, a downside of this approach is that each component has to extract the partner
parameter on its own. So the login
path declaration would look like:
{
path: ':partner/login',
component: LoginComponent
}
And the LoginComponent
would look like:
export class LoginComponent implements OnInit {
partner: string;
constructor(private _route: ActivatedRoute) { }
ngOnInit() {
this._route.params.forEach(p => {
this.partner = p[`partner`];
});
}
}
While this is manageable with 3 routes, conceptually, what I really want to say is that I have a PartnerComponent
which has child routes login
, tools
, etc.
The routing would look like this:
{ path: ':partner', component: PartnerComponent
children: [
{ path: 'login', component: LoginComponent},
{ path: 'tools', component: ToolsComponent}
]
}
And using TypeScript's inheritance, I can create a base component for each child like:
export class BaseComponent implements OnInit {
partner: string;
constructor(private _route: ActivatedRoute) { }
ngOnInit() {
this._route.parent.params.forEach(p => {
this.partner = p[`partner`];
});
}
}
and child components would look like:
export class LoginComponent extends BaseComponent {
constructor(route: ActivatedRoute) {
super(route);
}
ngOnInit() {
super.ngOnInit();
}
}
While the above works, it feels a bit dirty to look up the parent route. I know if I change the hierarchy, I only need to change it in the base, but still
Is there a cleaner way to share information between a parent and its child route components.
If it were a more traditional parent element/child element relationship, we would simply use data binding between the two to pass that information.
What you can do is to use a Resolve
guard for your parent route, so the data will be loaded for any :partner
route.
A Resolve
would look like:
import { Injectable } from '@angular/core';
import { Router, Resolve,
ActivatedRouteSnapshot } from '@angular/router';
import { Partner } from './partner.model';
import { Api } from './api.service';
@Injectable()
export class PartnerResolve implements Resolve<Partner> {
constructor(private api: Api,
private router: Router) {}
resolve(route: ActivatedRouteSnapshot): Observable<Partner> {
let id = +route.params['partner'];
return this.api.getPartner(id);
}
}
Then, your route would need to include the PartnerResolve
so it would not load until the data was requested and it's available:
{
path: ':partner', component: PartnerComponent,
resolve: {
partner: PartnerResolve
},
children: [
{ path: 'login', component: LoginComponent},
{ path: 'tools', component: ToolsComponent},
]
}
And your PartnerComponent
would just get it with:
ngOnInit() {
this.route.data.forEach((data: { partner: Partner }) => {
this.partner = data.partner;
});
}
And your child components would use:
ngOnInit() {
this.route.parent.data.forEach((data: { partner: Partner }) => {
this.partner = data.partner;
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With