|
|
|
@ -3,10 +3,10 @@ include ../_util-fns
|
|
|
|
|
:marked
|
|
|
|
|
# Routing Around the App
|
|
|
|
|
We received new requirements for our Tour of Heroes application:
|
|
|
|
|
* add a *Dashboard* view.
|
|
|
|
|
* navigate between the *Heroes* and *Dashboard* views.
|
|
|
|
|
* clicking on a hero in either view navigates to a detail view of the selected hero.
|
|
|
|
|
* clicking a *deep link* in an email opens the detail view for a particular hero;
|
|
|
|
|
* Add a *Dashboard* view.
|
|
|
|
|
* Navigate between the *Heroes* and *Dashboard* views.
|
|
|
|
|
* Clicking on a hero in either view navigates to a detail view of the selected hero.
|
|
|
|
|
* Clicking a *deep link* in an email opens the detail view for a particular hero;
|
|
|
|
|
|
|
|
|
|
When we’re done, users will be able to navigate the app like this:
|
|
|
|
|
figure.image-display
|
|
|
|
@ -16,7 +16,7 @@ figure.image-display
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
The [Routing and Navigation](../guide/router-deprecated.html) chapter covers the router in more detail
|
|
|
|
|
than we will in this tour.
|
|
|
|
|
than we will in this tutorial.
|
|
|
|
|
|
|
|
|
|
p Run the #[+liveExampleLink2('', 'toh-5')] for this part.
|
|
|
|
|
|
|
|
|
@ -65,10 +65,10 @@ code-example(language="bash").
|
|
|
|
|
## Action plan
|
|
|
|
|
Here's our plan
|
|
|
|
|
|
|
|
|
|
* turn `AppComponent` into an application shell that only handles navigation.
|
|
|
|
|
* relocate the *Heroes* concerns within the current `AppComponent` to a separate `HeroesComponent`
|
|
|
|
|
* add routing
|
|
|
|
|
* create a new `DashboardComponent`
|
|
|
|
|
* turn `AppComponent` into an application shell that only handles navigation,
|
|
|
|
|
* relocate the *Heroes* concerns within the current `AppComponent` to a separate `HeroesComponent`,
|
|
|
|
|
* add routing,
|
|
|
|
|
* create a new `DashboardComponent`,
|
|
|
|
|
* tie the *Dashboard* into the navigation structure.
|
|
|
|
|
|
|
|
|
|
.l-sub-section
|
|
|
|
@ -91,13 +91,13 @@ code-example(language="bash").
|
|
|
|
|
Instead of moving anything out of `AppComponent`, we'll just rename it `HeroesComponent`
|
|
|
|
|
and create a new `AppComponent` shell separately.
|
|
|
|
|
|
|
|
|
|
The steps are:
|
|
|
|
|
* rename `app.component.ts` file to `heroes.component.ts`.
|
|
|
|
|
* rename the `AppComponent` class to `HeroesComponent`.
|
|
|
|
|
* rename the selector `my-app` to `my-heroes`.
|
|
|
|
|
The steps are: rename the
|
|
|
|
|
* `app.component.ts` file to `heroes.component.ts`,
|
|
|
|
|
* `AppComponent` class to `HeroesComponent`,
|
|
|
|
|
* selector `my-app` to `my-heroes`.
|
|
|
|
|
|
|
|
|
|
:marked
|
|
|
|
|
+makeExample('toh-5/ts/app/heroes.component.ts', 'heroes-component-renaming', 'app/heroes.component.ts (renaming)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/heroes.component.ts', 'heroes-component-renaming', 'app/heroes.component.ts (showing renamings only)')(format=".")
|
|
|
|
|
|
|
|
|
|
:marked
|
|
|
|
|
## Create *AppComponent*
|
|
|
|
@ -154,10 +154,10 @@ code-example(language="bash").
|
|
|
|
|
|
|
|
|
|
The Angular router is a combination of multiple services (`ROUTER_PROVIDERS`), multiple directives (`ROUTER_DIRECTIVES`),
|
|
|
|
|
and a configuration decorator (`RouteConfig`). We'll import them all together:
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'import-router', 'app.component.ts (router imports)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'import-router', 'app/app.component.ts (router imports)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Next we update the `directives` and `providers` metadata arrays to *include* the router assets.
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'directives-and-providers', 'app.component.ts (directives and providers)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'directives-and-providers', 'app/app.component.ts (directives and providers)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Notice that we also removed the `HeroesComponent` from the `directives` array.
|
|
|
|
|
`AppComponent` no longer shows heroes; that will be the router's job.
|
|
|
|
@ -172,7 +172,7 @@ code-example(language="bash").
|
|
|
|
|
pastes a URL into the browser address bar.
|
|
|
|
|
|
|
|
|
|
Let's define our first route, a route to the `HeroesComponent`.
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'route-config', 'app.component.ts (RouteConfig for heroes)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'route-config', 'app/app.component.ts (RouteConfig for heroes)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
`@RouteConfig` takes an array of *route definitions*.
|
|
|
|
|
We have only one route definition at the moment but rest assured, we'll add more.
|
|
|
|
@ -203,7 +203,7 @@ code-example(language="bash").
|
|
|
|
|
We add an anchor tag to the template which, when clicked, triggers navigation to the `HeroesComponent`.
|
|
|
|
|
|
|
|
|
|
The revised template looks like this:
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'template', 'app.component.ts (template for Heroes)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.2.ts', 'template', 'app/app.component.ts (template v1)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Notice the `[routerLink]` binding in the anchor tag.
|
|
|
|
|
We bind the `RouterLink` directive (another of the `ROUTER_DIRECTIVES`) to an array
|
|
|
|
@ -250,7 +250,7 @@ code-example(language="bash").
|
|
|
|
|
Import the `DashboardComponent` so we can reference it in the dashboard route definition.
|
|
|
|
|
|
|
|
|
|
Add the following `'Dashboard'` route definition to the `@RouteConfig` array of definitions.
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.ts','dashboard-route', 'app.component.ts (Dashboard Route)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.ts','dashboard-route', 'app/app.component.ts (Dashboard route)')(format=".")
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
**useAsDefault**
|
|
|
|
@ -265,7 +265,7 @@ code-example(language="bash").
|
|
|
|
|
:marked
|
|
|
|
|
Finally, add a dashboard navigation link to the template, just above the *Heroes* link.
|
|
|
|
|
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.ts','template', 'app.component.ts (template)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.ts','template', 'app/app.component.ts (template)')(format=".")
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
We nestled the two links within `<nav>` tags.
|
|
|
|
@ -288,7 +288,7 @@ code-example(language="bash").
|
|
|
|
|
We _can_ switch to [component-relative paths](../cookbook/component-relative-paths.html) if we prefer.
|
|
|
|
|
:marked
|
|
|
|
|
Create that file with these contents:
|
|
|
|
|
+makeExample('toh-5/ts/app/dashboard.component.html', null, 'dashboard.component.html')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/dashboard.component.html', null, 'app/dashboard.component.html')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
We use `*ngFor` once again to iterate over a list of heroes and display their names.
|
|
|
|
|
We added extra `<div>` elements to help with styling later in this chapter.
|
|
|
|
@ -305,7 +305,7 @@ code-example(language="bash").
|
|
|
|
|
and added it to the `providers` array of the top level `AppComponent`.
|
|
|
|
|
|
|
|
|
|
That move created a singleton `HeroService` instance, available to *all* components of the application.
|
|
|
|
|
We'll inject and use it here in the `DashboardComponent` .
|
|
|
|
|
Angular will inject `HeroService` and we'll use it here in the `DashboardComponent`.
|
|
|
|
|
|
|
|
|
|
### Get heroes
|
|
|
|
|
Open the `dashboard.component.ts` and add the requisite `import` statements.
|
|
|
|
@ -364,7 +364,7 @@ code-example(format='').
|
|
|
|
|
### Configure a Route with a Parameter
|
|
|
|
|
|
|
|
|
|
Here's the *route definition* we'll use.
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-route', 'app/app.component.ts (Route to HeroDetailComponent)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-route', 'app/app.component.ts (route to HeroDetailComponent)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
The colon (:) in the path indicates that `:id` is a placeholder to be filled with a specific hero `id`
|
|
|
|
|
when navigating to the `HeroDetailComponent`.
|
|
|
|
@ -389,19 +389,19 @@ code-example(format='').
|
|
|
|
|
:marked
|
|
|
|
|
## Revise the *HeroDetailComponent*
|
|
|
|
|
|
|
|
|
|
Before we rewrite the `HeroDetailComponent`, let's remember what it looks like now:
|
|
|
|
|
Before we rewrite the `HeroDetailComponent`, let's review what it looks like now:
|
|
|
|
|
+makeExample('toh-4/ts/app/hero-detail.component.ts', null, 'app/hero-detail.component.ts (current)')
|
|
|
|
|
:marked
|
|
|
|
|
The template won't change. We'll display a hero the same way. The big changes are driven by how we get the hero.
|
|
|
|
|
|
|
|
|
|
We will no longer receive the hero in a parent component property binding.
|
|
|
|
|
The new `HeroDetailComponent` should take the `id` parameter from the router's `RouteParams` service
|
|
|
|
|
and use the `HeroService` to fetch the hero with that `id` from storage.
|
|
|
|
|
and use the `HeroService` to fetch the hero with that `id`.
|
|
|
|
|
|
|
|
|
|
We need an import statement to reference the `RouteParams`.
|
|
|
|
|
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-route-params')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
We import the `HeroService`so we can fetch a hero`.
|
|
|
|
|
We import the `HeroService`so we can fetch a hero.
|
|
|
|
|
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'import-hero-service')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
We import the `OnInit` interface because we'll call the `HeroService` inside the `ngOnInit` component lifecycle hook.
|
|
|
|
@ -460,13 +460,11 @@ code-example(format='').
|
|
|
|
|
:marked
|
|
|
|
|
Here's the (nearly) finished `HeroDetailComponent`:
|
|
|
|
|
+makeExample('toh-5/ts/app/hero-detail.component.ts', 'v2', 'app/hero-detail.component.ts (latest)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
|
|
|
|
## Select a *Dashboard* Hero
|
|
|
|
|
When a user selects a hero in the dashboard, the app should navigate to the `HeroDetailComponent` to view and edit the selected hero..
|
|
|
|
|
When a user selects a hero in the dashboard, the app should navigate to the `HeroDetailComponent` to view and edit the selected hero.
|
|
|
|
|
|
|
|
|
|
In the dashboard template we bound each hero's click event to the `gotoDetail` method, passing along the selected `hero` entity.
|
|
|
|
|
+makeExample('toh-5/ts/app/dashboard.component.html','click', 'app/dashboard.component.html (click binding)')(format=".")
|
|
|
|
@ -489,7 +487,7 @@ code-example(format='').
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.ts','hero-detail-route', 'app/app.component.ts (hero detail route)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
The `DashboardComponent` doesn't have the router yet. We obtain it in the usual way:
|
|
|
|
|
`import` the `router` reference and inject it in the constructor (along with the `HeroService`):
|
|
|
|
|
import the `router` reference and inject it in the constructor (along with the `HeroService`):
|
|
|
|
|
|
|
|
|
|
+makeExample('toh-5/ts/app/dashboard.component.ts','import-router', 'app/dashboard.component.ts (excerpts)')(format=".")
|
|
|
|
|
+makeExample('toh-5/ts/app/dashboard.component.ts','ctor')(format=".")
|
|
|
|
@ -526,7 +524,7 @@ figure.image-display
|
|
|
|
|
### Format with the *UpperCasePipe*
|
|
|
|
|
|
|
|
|
|
Notice that the hero's name is displayed in CAPITAL LETTERS. That's the effect of the `UpperCasePipe`
|
|
|
|
|
that we slipped into the interpolation binding. Look for it right after the pipe operator, ( | ).
|
|
|
|
|
that we slipped into the interpolation binding. Look for it right after the pipe operator ( | ).
|
|
|
|
|
+makeExample('toh-5/ts/app/heroes.component.html','pipe')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Pipes are a good way to format strings, currency amounts, dates and other display data.
|
|
|
|
@ -551,14 +549,14 @@ figure.image-display
|
|
|
|
|
So we can remove it from the metadata `directives` array. The `directives` array is now empty so we delete it.
|
|
|
|
|
We might as well delete the `HeroDetailComponent` import statement too.
|
|
|
|
|
|
|
|
|
|
The revised component metadata looks like this:
|
|
|
|
|
The revised `@Component` looks like this:
|
|
|
|
|
+makeExample('toh-5/ts/app/heroes.component.ts', 'metadata', 'app/heroes.component.ts (revised metadata)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Now we can see what's going on as we update the component class along the same lines as the dashboard:
|
|
|
|
|
1. Import the `router`
|
|
|
|
|
1. Inject the `router` in the constructor (along with the `HeroService`)
|
|
|
|
|
1. Implement the `gotoDetail` method by calling the `router.navigate` method
|
|
|
|
|
with a two-part 'HeroDetail' *link parameters array*.
|
|
|
|
|
with a two-part `HeroDetail` *link parameters array*.
|
|
|
|
|
|
|
|
|
|
Here's the revised component class:
|
|
|
|
|
+makeExample('toh-5/ts/app/heroes.component.ts', 'class', 'app/heroes.component.ts (class)')
|
|
|
|
@ -613,7 +611,7 @@ figure.image-display
|
|
|
|
|
We cooperated by surrounding those links in `<nav>` tags.
|
|
|
|
|
|
|
|
|
|
Add a `app.component.css` file to the `app` folder with the following content.
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.css', 'css', 'app/app.component.css (Navigation Styles)')
|
|
|
|
|
+makeExample('toh-5/ts/app/app.component.css', '', 'app/app.component.css (navigation styles)')
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
|
|
|
|
**The *router-link-active* class**
|
|
|
|
@ -632,11 +630,20 @@ figure.image-display
|
|
|
|
|
We can also create styles at the *application level* outside of any component.
|
|
|
|
|
|
|
|
|
|
Our designers provided some basic styles to apply to elements across the entire app.
|
|
|
|
|
Add the following to a new file named `styles.css` in the root folder.
|
|
|
|
|
+makeExample('toh-5/ts/styles.1.css', '', 'styles.css (App Styles)')(format=".")
|
|
|
|
|
These correspond to the full set of master styles that we
|
|
|
|
|
introduced earlier (see
|
|
|
|
|
[QuickStart, "Add some style"](../quickstart.html#!#add-some-style)).
|
|
|
|
|
Here is an excerpt.
|
|
|
|
|
|
|
|
|
|
+makeExample('toh-5/ts/styles.1.css', 'toh-excerpt', 'styles.css (app styles excerpt)')(format=".")
|
|
|
|
|
|
|
|
|
|
- var styles_css = 'https://raw.githubusercontent.com/angular/angular.io/master/public/docs/_examples/styles.css'
|
|
|
|
|
|
|
|
|
|
:marked
|
|
|
|
|
Reference this stylesheet within the `index.html` in the traditional manner.
|
|
|
|
|
Add a new file named `styles.css` in the root folder, if there isn't one already.
|
|
|
|
|
Ensure that it contains the [master styles given here](!{styles_css}).
|
|
|
|
|
|
|
|
|
|
Also ensure this stylesheet is referenced in the traditional manner within `index.html`.
|
|
|
|
|
+makeExample('toh-5/ts/index.html','css', 'index.html (link ref)')(format=".")
|
|
|
|
|
:marked
|
|
|
|
|
Look at the app now. Our dashboard, heroes, and navigation links are styling!
|
|
|
|
@ -680,7 +687,6 @@ p.
|
|
|
|
|
.file systemjs.config.json
|
|
|
|
|
.file tsconfig.json
|
|
|
|
|
.file typings.json
|
|
|
|
|
:marked
|
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
|
|
|
@ -689,15 +695,15 @@ p.
|
|
|
|
|
### The Road Behind
|
|
|
|
|
We travelled a great distance in this chapter.
|
|
|
|
|
- We added the Angular *Component Router* to navigate among different components.
|
|
|
|
|
- We learned how to create router links to represent navigation menu items
|
|
|
|
|
- We used router parameters to navigate to the details of user selected hero
|
|
|
|
|
- We shared the `HeroService` among multiple components
|
|
|
|
|
- We learned how to create router links to represent navigation menu items.
|
|
|
|
|
- We used router parameters to navigate to the details of user selected hero.
|
|
|
|
|
- We shared the `HeroService` among multiple components.
|
|
|
|
|
- We moved HTML and CSS out of the component file and into their own files.
|
|
|
|
|
- We added the `uppercase` pipe to format data
|
|
|
|
|
- We added the `uppercase` pipe to format data.
|
|
|
|
|
|
|
|
|
|
### The Road Ahead
|
|
|
|
|
We have much of the foundation we need to build an application.
|
|
|
|
|
We're still missing a key piece: remote data access.
|
|
|
|
|
|
|
|
|
|
In a forthcoming tutorial chapter,
|
|
|
|
|
In the next chapter,
|
|
|
|
|
we’ll replace our mock data with data retrieved from a server using http.
|
|
|
|
|