I'm trying to learn Angular, and I have a separation-of-concerns problem with routing. In all guides I found, when happens is that you specify the route and parameters in the routing module, but parse them in the component file. For example, in the classic Tour of Heroes example:
// in app-routing.module.ts
{ path: 'hero/:id': component: HeroDetailComponent }
// in hero-detail.component.ts
constructor(
private route: ActivatedRoute,
private heroService: HeroService,
private location: Location
) {}
ngOnInit() {
const id = +this.route.snapshot.paramMap.get('id');
this.heroService.getHero(id)
.subscribe(hero => this.hero = hero);
}
But earlier in the tutorial, what you had was that a Hero
object was used as Input()
:
@Input() hero: Hero;
And you would bind to it:
<hero-detail [hero]='hero'></hero-detail>
What I would want is to be able to keep that logic, and instead parse the URL and pass the Input()
s in the same file as the routing module, and keep the component file as-is. Something like:
// app-routing.module.ts
{
path: 'hero/:id', component: HeroDetailComponent, inputs: path => ({ hero: getHeroObject(+path.params["id"]) })
}
Is there any option in Angular routing to do something like this? If not, can you think of a roundabout-way to separate the logic parts? One thing of which I thought is to create separate components in the routing module for each path, each of them renders the "pure-logic" component, something like:
// app-routing/hero-detail-route.component.ts
constructor(
private route: ActivatedRoute,
private heroService: HeroService,
private location: Location
) {}
ngOnInit() {
const id = +this.route.snapshot.paramMap.get('id');
this.heroService.getHero(id)
.subscribe(hero => this.hero = hero);
}
<!--- app-routing/hero-detail-route.component.html -->
<hero-detail [hero]='hero'></hero-detail>
But that's just kind of "kicking the problem down the road", isn't it?
Any ideas?
Thanks in advance!
You can do it in 2 ways.
1. Using resolvers
You can attach a resolver in the route like
{
path: 'hero/:id',
component: HeroDetailComponent,
resolve: {
hero: HeroDetailResolver
}
}
HeroDetailResolver
will contain all the logic to get the hero detail from server. In component
you catch resolver data.
this.route.data.subscribe(data => {
this.hero = data.hero;
});
If you are not familiar with angular resolvers
they are a handy tool to pre-fetch some server data before component
loads.
2. Using smart parent
and dumb child
components
Use a smart parent component
to fetch all server data and provide them to dumb child components
. In this case you can make HeroDetailLayoutComponent
to fetch the hero detail. Then pass it as @Input()
to HeroDetailComponent
.
// in app-routing.module.ts
{ path: 'hero/:id': component: HeroDetailLayoutComponent }
// in hero-detail-layout.component.ts
constructor(private route: ActivatedRoute, private heroService: HeroService) {}
ngOnInit() {
const id = +this.route.snapshot.paramMap.get('id');
this.heroService.getHero(id).subscribe(hero => this.hero = hero);
}
// hero-detail-layout.component.html
<hero-detail [hero]='hero'></hero-detail>
// in hero-detail.component.ts
@Input() hero: any[];