- var _example = 'toh-5';
block includes
include ../_util-fns
- var _appRoutingTsVsAppComp = 'app.module.ts'
- var _RoutesVsAtRouteConfig = 'Routes'
- var _RouterModuleVsRouterDirectives = 'RouterModule'
- var _redirect = 'redirect'
:marked
There are new requirements for the Tour of Heroes app:
* Add a *Dashboard* view.
* Add the ability to navigate between the *Heroes* and *Dashboard* views.
* When users click a hero name in either view, navigate to a detail view of the selected hero.
* When users click a *deep link* in an email, open the detail view for a particular hero.
When you’re done, users will be able to navigate the app like this:
figure.image-display
img(src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations")
:marked
To satisfy these requirements, you'll add Angular’s router to the app.
.l-sub-section
:marked
For more information about the router, read the [Routing and Navigation](../guide/router.html) page.
:marked
When you're done with this page, the app should look like this .
+ifDocsFor('ts|js')
include ../../../_includes/_see-addr-bar
.l-main-section
:marked
## Where you left off
Before continuing with the Tour of Heroes, verify that you have the following structure.
block intro-file-tree
.filetree
.file angular-tour-of-heroes
.children
.file src
.children
.file app
.children
.file app.component.ts
.file app.module.ts
.file hero.service.ts
.file hero.ts
.file hero-detail.component.ts
.file mock-heroes.ts
.file main.ts
.file index.html
.file styles.css
.file systemjs.config.js
.file tsconfig.json
.file node_modules ...
.file package.json
block keep-app-running
:marked
## Keep the app transpiling and running
Enter the following command in the terminal window:
code-example(language="sh" class="code-shell").
npm start
:marked
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
:marked
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
## Action plan
Here's the 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`.
* Tie the *Dashboard* into the navigation structure.
.l-sub-section
:marked
*Routing* is another name for *navigation*. The router is the mechanism for navigating from view to view.
.l-main-section
:marked
## Splitting the *AppComponent*
The current app loads `AppComponent` and immediately displays the list of heroes.
The revised app should present a shell with a choice of views (*Dashboard* and *Heroes*)
and then default to one of them.
The `AppComponent` should only handle navigation, so you'll
move the display of *Heroes* out of `AppComponent` and into its own `HeroesComponent`.
### *HeroesComponent*
`AppComponent` is already dedicated to *Heroes*.
Instead of moving the code out of `AppComponent`, rename it to `HeroesComponent`
and create a separate `AppComponent` shell.
Do the following:
* Rename the app.component.ts file to heroes.component.ts.
* Rename the `AppComponent` class to `HeroesComponent` (rename locally, _only_ in this file).
* Rename the selector `my-app` to `my-heroes`.
+makeExcerpt('src/app/heroes.component.ts (showing renamings only)', 'renaming')
:marked
### Create *AppComponent*
The new `AppComponent` is the application shell.
It will have some navigation links at the top and a display area below.
Perform these steps:
* Create the file src/app/app.component.ts.
* Define an exported `AppComponent` class.
* Add an `@Component` !{_decorator} above the class with a `my-app` selector.
* Move the following from `HeroesComponent` to `AppComponent`:
* `title` class property.
* `@Component` template `
` element, which contains a binding to `title`.
* Add a `` element to the app template just below the heading so you still see the heroes.
* Add `HeroesComponent` to the `!{_declsVsDirectives}` !{_array} of `!{_AppModuleVsAppComp}` so Angular recognizes the `` tags.
* Add `HeroService` to the `providers` !{_array} of `!{_AppModuleVsAppComp}` because you'll need it in every other view.
* Remove `HeroService` from the `HeroesComponent` `providers` !{_array} since it was promoted.
* Add the supporting `import` statements for `AppComponent`.
The first draft looks like this:
block app-comp-v1
+makeTabs(
`toh-5/ts/src/app/app.component.1.ts,
toh-5/ts/src/app/app.module.1.ts`,
',',
`src/app/app.component.ts (v1),
src/app/app.module.ts (v1)`)
:marked
The app still runs and displays heroes.
:marked
## Add routing
Instead of displaying automatically, heroes should display after users click a button.
In other words, users should be able to navigate to the list of heroes.
Use the Angular router to enable navigation.
block angular-router
:marked
The Angular router is an external, optional Angular NgModule called `RouterModule`.
The router is a combination of multiple provided services (`RouterModule`),
multiple directives (`RouterOutlet, RouterLink, RouterLinkActive`),
and a configuration (`Routes`). You'll configure the routes first.
:marked
### *<base href>*
Open `index.html` and ensure there is a `` element
(or a script that dynamically sets this element)
at the top of the `` section.
+makeExcerpt('src/index.html', 'base-href')
.callout.is-important
header base href is essential
:marked
For more information, see the [Set the base href](../guide/router.html#!#base-href)
section of the [Routing and Navigation](../guide/router.html) page.
a#configure-routes
block router-config-intro
:marked
### Configure routes
Create a configuration file for the app routes.
:marked
*Routes* tell the router which views to display when a user clicks a link or
pastes a URL into the browser address bar.
Define the first route as a route to the heroes component.
- var _file = _docsFor == 'dart' ? 'app.component.ts' : 'app.module.2.ts'
+makeExcerpt('src/app/' + _file + ' (heroes route)', 'heroes')
- var _are = _docsFor == 'dart' ? 'takes' : 'are'
- var _routePathPrefix = _docsFor == 'dart' ? '/' : ''
:marked
The `!{_RoutesVsAtRouteConfig}` !{_are} !{_an} !{_array} of *route definitions*.
This route definition has the following parts:
- *Path*: The router matches this route's path to the URL in the browser address bar (`!{_routePathPrefix}heroes`).
*Name*: The official name of the route;
it must begin with a capital letter to avoid confusion with the path (`Heroes`).
- *Component*: The component that the router should create when navigating to this route (`HeroesComponent`).
.l-sub-section
:marked
Read more about defining routes with `!{_RoutesVsAtRouteConfig}` in the [Routing & Navigation](../guide/router.html) page.
+ifDocsFor('ts|js')
:marked
### Make the router available
Import the `RouterModule` and add it to the `AppModule` imports !{_array}.
+makeExcerpt('src/app/app.module.2.ts (app routing)', '')
.l-sub-section
:marked
The `forRoot()` method is called because a configured router is provided at the app's root.
The `forRoot()` method supplies the Router service providers and directives needed for routing, and
performs the initial navigation based on the current browser URL.
- var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes'
:marked
### Router outlet
If you paste the path, `/heroes`, into the browser address bar at the end of the URL,
the router should match it to the `!{_heroesRoute}` route and display the `HeroesComponent`.
However, you have to tell the router where to display the component.
To do this, you can add a `` element at the end of the template.
`RouterOutlet` is one of the directives provided by the `!{_RouterModuleVsRouterDirectives}`.
The router displays each component immediately below the `` as users navigate through the app.
### Router links
Users shouldn't have to paste a route URL into the address bar.
Instead, add an anchor tag to the template that, when clicked, triggers navigation to the `HeroesComponent`.
The revised template looks like this:
+makeExcerpt('src/app/app.component.1.ts', 'template-v2')
block routerLink
:marked
Note the `routerLink` binding in the anchor tag.
The `RouterLink` directive (another of the `RouterModule` directives) is bound to a string
that tells the router where to navigate when the user clicks the link.
Since the link is not dynamic, a routing instruction is defined with a one-time binding to the route path.
Looking back at the route configuration, you can confirm that `'/heroes'` is the path of the route to the `HeroesComponent`.
.l-sub-section
:marked
Read more about dynamic router links and the link parameters array
in the [Appendix: Link Parameters Array](../guide/router.html#link-parameters-array) section of the
[Routing & Navigation](../guide/router.html#) page.
:marked
Refresh the browser. The browser displays the app title and heroes link, but not the heroes list.
.l-sub-section
:marked
The browser's address bar shows `/`.
The route path to `HeroesComponent` is `/heroes`, not `/`.
Soon you'll add a route that matches the path `/`.
:marked
Click the *Heroes* navigation link. The address bar updates to `/heroes`
and the list of heroes displays.
`AppComponent` now looks like this:
+makeExample('src/app/app.component.1.ts', 'v2', 'src/app/app.component.ts (v2)')
:marked
The *AppComponent* is now attached to a router and displays routed views.
For this reason, and to distinguish it from other kinds of components,
this component type is called a *router component*.
:marked
## Add a dashboard
Routing only makes sense when multiple views exist.
To add another view, create a placeholder `DashboardComponent`, which users can navigate to and from.
+makeExcerpt('src/app/dashboard.component.1.ts (v1)', '')
:marked
You'll make this component more useful later.
### Configure the dashboard route
To teach `!{_appRoutingTsVsAppComp}` to navigate to the dashboard,
import the dashboard component and
add the following route definition to the `!{_RoutesVsAtRouteConfig}` !{_array} of definitions.
- var _file = _docsFor == 'dart' ? 'lib/app_component.dart' : 'src/app/app.module.3.ts'
+makeExcerpt(_file + ' (Dashboard route)', 'dashboard')
+ifDocsFor('ts|js')
:marked
Also import and add `DashboardComponent` to the `AppModule`'s `declarations`.
+makeExcerpt('src/app/app.module.ts', 'dashboard')
:marked
### Add a !{_redirect} route
Currently, the browser launches with `/` in the address bar.
When the app starts, it should show the dashboard and
display a `/dashboard` URL in the browser address bar.
block redirect-vs-use-as-default
:marked
To make this happen, use a redirect route. Add the following
to the array of route definitions:
+makeExcerpt('src/app/app.module.3.ts','redirect')
.l-sub-section
:marked
Read more about *redirects* in the [Redirecting routes](../guide/router.html#!#redirect) section
of the [Routing & Navigation](../guide/router.html#) page.
:marked
### Add navigation to the template
Add a dashboard navigation link to the template, just above the *Heroes* link.
- var _vers = _docsFor == 'dart' ? '' : '.1'
+makeExcerpt('src/app/app.component' + _vers + '.ts', 'template-v3')
.l-sub-section
:marked
The `