Tinkering with my work-along from the Angular "Tour of Heroes" tutorial, I'm trying to see how child routes work. I have a HeroesComponent with an embedded <router-outlet></router-outlet>
. HeroesComponent, reached via '/heroes', displays a list of links for individual heroes, each with routerLink
set to '/heroes/[id]', with that hero's ID appearing instead of '[id]'--'/heroes/7', for example.
(I know how to add a child component directly to a parent component without using a child route. My purpose here is to learn how child routes work. With a direct child component, I know to use ngOnChanges, but in this case, it isn't an @Input component property that's changing, it's a router property.)
Routing
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'heroes', component: HeroesComponent, runGuardsAndResolvers: 'always', children: [
{ path: ':id', component: HeroDetailComponent, runGuardsAndResolvers: 'always' }
] },
{ path: 'dashboard', component: DashboardComponent }
];
Child router outlet in parent HeroesComponent
<section>
<h2>All Heroes</h2>
<div><a routerLink="/detail">Create a hero</a></div>
<table class="table">
<thead>
<tr><th>Name</th><th>Location</th></tr>
</thead>
<tbody>
<tr *ngFor="let hero of heroes">
<th><a routerLink="/heroes/{{hero.id}}">{{hero.name}}</a></th>
<td>{{hero.location}}</td>
<td><a (click)="delete(hero.id)">delete</a></td>
</tr>
</tbody>
</table>
</section>
<router-outlet></router-outlet>
Key code in the child HeroDetailComponent
ngOnInit(): void {
let id: string;
// I'll explain this comment later.
// this.route.paramMap.subscribe((paramMap) => id = paramMap.get('id'));
id = this.route.snapshot.paramMap.get('id');
if (id) {
this.mode = 'update';
this.heroService.getHero(+id).subscribe((hero) => {
this.hero = hero
});
} else {
this.mode = 'add';
this.hero = { id: 0, name: '', location: '', cuisine: 0 }
}
}
Navigating to /heroes gives me the following. (I already had a restaurant API set up for a .NET CORE tutorial, so I'm repurposing that as my data source for this Angular tutorial.)
The browser's address field shows "localhost:4200/heroes". When I hover the cursor over "Masseria", the browser status bar (this is Chrome on Windows 10, if it matters) reads "http://localhost:4200/heroes/9". Clicking the Masseria link, I get:
So far, so good! But then, when I click, say, the Takumi link, though the contents of the browser address field change correctly to "http://localhost:4200/heroes/13", the display doesn't change: I'm still seeing the details for Masseria.
Now, if I click into the browser address field and press Enter, then the entire page refreshes, showing me both the list of restaurants and the details for Takumi.
I figure some necessary update notification isn't happening. I did a little research. Regarding the line I commented out in the last chunk of code above, I thought that maybe subscribing explicitly to the router parameter :id would help, using
this.route.paramMap.subscribe((paramMap) => id = paramMap.get('id'));
instead of taking the parameter from the router snapshot. This had no effect.
I also tried adding the attribute runGuardsAndResolvers: 'always'
to both the parent and child paths in my routes array (first chunk of code, above), but that also had no effect.
Ack, I got it. I was halfway there with the subscription in OnInit to the router's paramMap:
this.route.paramMap.subscribe((paramMap) => id = paramMap.get('id'));
The part I was missing is that all the state computation needs to be inside the subscription callback, not just setting the ID value. Behold:
ngOnInit(): void {
let id: string;
this.route.paramMap.subscribe((paramMap) => {
id = paramMap.get('id');
if (id) {
this.mode = 'update';
this.heroService.getHero(+id).subscribe((hero) => {
this.hero = hero
});
} else {
this.mode = 'add';
this.hero = { id: 0, name: '', location: '', cuisine: 0 }
}
}
);
Now it's working as intended.