Skip to content

Commit 81c9dd7

Browse files
atscottdylhunn
authored andcommitted
docs(router): Update common tasks guide to use input binding instead of ActivatedRoute (#49633)
The easiest way to access route data is now to use direct bindings to component inputs rather than going through `ActivatedRoute`. The tour of heroes guide still uses `ActivatedRoute` for now. I was hesitant to remove all of the content in one swoop and the tour of heroes is quite a bit more involved. PR Close #49633
1 parent f982a3f commit 81c9dd7

File tree

5 files changed

+83
-68
lines changed

5 files changed

+83
-68
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {NgModule} from '@angular/core';
2+
import {provideRouter, Routes, withComponentInputBinding} from '@angular/router';
3+
4+
import {authGuard} from './auth/auth.guard';
5+
import {ComposeMessageComponent} from './compose-message/compose-message.component';
6+
import {PageNotFoundComponent} from './page-not-found/page-not-found.component';
7+
8+
const appRoutes: Routes = [
9+
{path: 'compose', component: ComposeMessageComponent, outlet: 'popup'}, {
10+
path: 'admin',
11+
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
12+
canMatch: [authGuard]
13+
},
14+
{
15+
path: 'crisis-center',
16+
loadChildren: () =>
17+
import('./crisis-center/crisis-center.module').then(m => m.CrisisCenterModule),
18+
data: {preload: true}
19+
},
20+
{path: '', redirectTo: '/superheroes', pathMatch: 'full'},
21+
{path: '**', component: PageNotFoundComponent}
22+
];
23+
24+
@NgModule({
25+
// #docregion withComponentInputBinding
26+
providers: [
27+
provideRouter(appRoutes, withComponentInputBinding()),
28+
]
29+
// #enddocregion withComponentInputBinding
30+
})
31+
export class AppRoutingModule {
32+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// #docplaster
2+
// #docregion
3+
import {Component, Input, OnInit} from '@angular/core';
4+
import {Router} from '@angular/router';
5+
import {Observable} from 'rxjs';
6+
7+
import {Hero} from '../hero';
8+
import {HeroService} from '../hero.service';
9+
10+
@Component({
11+
selector: 'app-hero-detail',
12+
templateUrl: './hero-detail.component.html',
13+
styleUrls: ['./hero-detail.component.css']
14+
})
15+
export class HeroDetailComponent {
16+
hero$!: Observable<Hero>;
17+
18+
constructor(private router: Router, private service: HeroService) {}
19+
20+
// #docregion id-input
21+
@Input()
22+
set id(heroId: string) {
23+
this.hero$ = this.service.getHero(heroId);
24+
}
25+
// #enddocregion id-input
26+
27+
gotoHeroes(hero: Hero) {
28+
const heroId = hero ? hero.id : null;
29+
// Pass along the hero id if available
30+
// so that the HeroList component can select that hero.
31+
// Include a junk 'foo' property for fun.
32+
this.router.navigate(['/superheroes', {id: heroId, foo: 'foo'}]);
33+
}
34+
}
Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
// #docplaster
22
// #docregion
3-
import { switchMap } from 'rxjs/operators';
4-
import { Component, OnInit } from '@angular/core';
5-
// #docregion imports-route-info
6-
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
7-
// #enddocregion imports-route-info
8-
import { Observable } from 'rxjs';
3+
import {Component, OnInit} from '@angular/core';
4+
import {ActivatedRoute, ParamMap, Router} from '@angular/router';
5+
import {Observable} from 'rxjs';
6+
import {switchMap} from 'rxjs/operators';
97

10-
import { HeroService } from '../hero.service';
11-
import { Hero } from '../hero';
8+
import {Hero} from '../hero';
9+
import {HeroService} from '../hero.service';
1210

1311
@Component({
1412
selector: 'app-hero-detail',
@@ -18,22 +16,13 @@ import { Hero } from '../hero';
1816
export class HeroDetailComponent implements OnInit {
1917
hero$!: Observable<Hero>;
2018

21-
// #docregion activated-route
22-
constructor(
23-
private route: ActivatedRoute,
24-
// #enddocregion activated-route
25-
private router: Router,
26-
private service: HeroService
27-
// #docregion activated-route
28-
) {}
29-
// #enddocregion activated-route
19+
constructor(private route: ActivatedRoute, private router: Router, private service: HeroService) {
20+
}
3021

3122

3223
ngOnInit() {
3324
this.hero$ = this.route.paramMap.pipe(
34-
switchMap((params: ParamMap) =>
35-
this.service.getHero(params.get('id')!))
36-
);
25+
switchMap((params: ParamMap) => this.service.getHero(params.get('id')!)));
3726
}
3827

3928
// #docregion redirect
@@ -42,7 +31,7 @@ export class HeroDetailComponent implements OnInit {
4231
// Pass along the hero id if available
4332
// so that the HeroList component can select that hero.
4433
// Include a junk 'foo' property for fun.
45-
this.router.navigate(['/superheroes', { id: heroId, foo: 'foo' }]);
34+
this.router.navigate(['/superheroes', {id: heroId, foo: 'foo'}]);
4635
}
4736
// #enddocregion redirect
4837
}

aio/content/guide/router.md

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -119,41 +119,22 @@ To edit an item, users click an Edit button, which opens an `EditGroceryItem` co
119119
You want that component to retrieve the `id` for the grocery item so it can display the right information to the user.
120120

121121
Use a route to pass this type of information to your application components.
122-
To do so, you use the [ActivatedRoute](api/router/ActivatedRoute) interface.
122+
To do so, you use the [withComponentInputBinding](api/router/withComponentInputBinding) feature with `provideRouter` or the `bindToComponentInputs` option of `RouterModule.forRoot`.
123123

124124
To get information from a route:
125125

126-
1. Import `ActivatedRoute` and `ParamMap` to your component.
126+
1. Add the `withComponentInputBinding` feature to the `provideRouter` method.
127127

128-
<code-example header="In the component class (excerpt)" path="router/src/app/heroes/hero-detail/hero-detail.component.ts" region="imports-route-info"></code-example>
128+
<code-example header="provideRouter feature" path="router/src/app/app-routing.module.11.ts" region="withComponentInputBinding"></code-example>
129129

130-
These `import` statements add several important elements that your component needs.
131-
To learn more about each, see the following API pages:
130+
1. Update the component to have an `Input` matching the name of the parameter.
132131

133-
* [`Router`](api/router)
134-
* [`ActivatedRoute`](api/router/ActivatedRoute)
135-
* [`ParamMap`](api/router/ParamMap)
136-
137-
1. Inject an instance of `ActivatedRoute` by adding it to your component's constructor:
138-
139-
<code-example header="In the component class (excerpt)" path="router/src/app/heroes/hero-detail/hero-detail.component.ts" region="activated-route"></code-example>
140-
141-
1. Update the `ngOnInit()` method to access the `ActivatedRoute` and track the `name` parameter:
142-
143-
<code-example header="In the component (excerpt)">
144-
145-
ngOnInit() {
146-
this.route.queryParams.subscribe(params =&gt; {
147-
this.name = params['name'];
148-
});
149-
}
150-
151-
</code-example>
132+
<code-example header="The component input (excerpt)" path="router/src/app/heroes/hero-detail/hero-detail.component.4.ts" region="id-input"></code-example>
152133

153134
<div class="alert is-helpful">
154135

155-
**NOTE**: <br />
156-
The preceding example uses a variable, `name`, and assigns it the value based on the `name` parameter.
136+
**NOTE:** <br>
137+
You can bind all route data with key, value pairs to component inputs: static or resolved route data, path parameters, matrix parameters, and query parameters.
157138

158139
</div>
159140

packages/router/test/directives/router_outlet.spec.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -264,27 +264,6 @@ describe('component input binding', () => {
264264
const instance = await harness.navigateByUrl('/x;language=english', MyComponent);
265265
expect(instance.language).toEqual('english');
266266
});
267-
it('sets component inputs from path params', async () => {
268-
@Component({
269-
template: '',
270-
})
271-
class MyComponent {
272-
@Input() language?: string;
273-
}
274-
275-
TestBed.configureTestingModule({
276-
providers: [provideRouter(
277-
[{
278-
path: '**',
279-
component: MyComponent,
280-
}],
281-
withComponentInputBinding())]
282-
});
283-
const harness = await RouterTestingHarness.create();
284-
285-
const instance = await harness.navigateByUrl('/x;language=english', MyComponent);
286-
expect(instance.language).toEqual('english');
287-
});
288267

289268
it('when keys conflict, sets inputs based on priority: data > path params > query params',
290269
async () => {

0 commit comments

Comments
 (0)