916 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			916 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| - var _example = 'toh-5';
 | ||
| 
 | ||
| block includes
 | ||
|   include ../_util-fns
 | ||
|   - var _appRoutingTsVsAppComp = 'app.routing.ts'
 | ||
|   - var _declsVsDirectives = 'declarations'
 | ||
|   - var _RoutesVsAtRouteConfig = 'Routes'
 | ||
|   - var _RouterModuleVsRouterDirectives = 'RouterModule'
 | ||
|   - var _redirectTo = 'redirectTo'
 | ||
| 
 | ||
| :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.
 | ||
| 
 | ||
|   When we’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
 | ||
|   We'll add Angular’s *Component Router* to our app to satisfy these requirements.
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   :marked
 | ||
|     The [Routing and Navigation](../guide/router.html) chapter covers the router
 | ||
|     in more detail than we will in this tutorial.
 | ||
| 
 | ||
| :marked
 | ||
|   Run the <live-example></live-example> for this part.
 | ||
| 
 | ||
| +ifDocsFor('ts|js')
 | ||
|   .l-sub-section
 | ||
|     img(src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" align="right" style="margin-right:-20px")
 | ||
| 
 | ||
|     :marked
 | ||
|       To see the URL changes in the browser address bar,
 | ||
|       pop out the preview window by clicking the blue 'X' button in the upper right corner:
 | ||
| 
 | ||
| .l-main-section
 | ||
| :marked
 | ||
|   ## Where We Left Off
 | ||
| 
 | ||
|   Before we continue with our Tour of Heroes, let’s verify that
 | ||
|   we have the following structure after adding our hero service
 | ||
|   and hero detail component. If not, we’ll need to go back and follow the previous chapters.
 | ||
| 
 | ||
| block intro-file-tree
 | ||
|   .filetree
 | ||
|     .file angular2-tour-of-heroes
 | ||
|     .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 main.ts
 | ||
|         .file mock-heroes.ts
 | ||
|       .file node_modules ...
 | ||
|       .file typings ...
 | ||
|       .file index.html
 | ||
|       .file package.json
 | ||
|       .file styles.css
 | ||
|       .file systemjs.config.js
 | ||
|       .file tsconfig.json
 | ||
|       .file typings.json
 | ||
| 
 | ||
| block keep-app-running
 | ||
|   :marked
 | ||
|     ### Keep the app transpiling and running
 | ||
| 
 | ||
|     Open a terminal/console window and enter the following command to
 | ||
|     start the TypeScript compiler, start the server, and watch for changes:
 | ||
| 
 | ||
|   code-example(language="bash").
 | ||
|     npm start
 | ||
| 
 | ||
| :marked
 | ||
|   The application runs and updates automatically as we continue to build the Tour of Heroes.
 | ||
| 
 | ||
|   ## 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`
 | ||
|   * 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*
 | ||
| 
 | ||
|   Our current app loads `AppComponent` and immediately displays the list of heroes.
 | ||
| 
 | ||
|   Our 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.
 | ||
|   Let's move the display of *Heroes* out of `AppComponent` and into its own `HeroesComponent`.
 | ||
| 
 | ||
|   ### *HeroesComponent*
 | ||
| 
 | ||
|   `AppComponent` is already dedicated to *Heroes*.
 | ||
|   Instead of moving anything out of `AppComponent`, we'll just rename it `HeroesComponent`
 | ||
|   and create a new `AppComponent` shell separately.
 | ||
| 
 | ||
|   The steps are to rename:
 | ||
|   * <span ngio-ex>app.component.ts</span> file to <span ngio-ex>heroes.component.ts</span>
 | ||
|   * `AppComponent` class to `HeroesComponent`
 | ||
|   * Selector `my-app` to `my-heroes`
 | ||
| 
 | ||
| +makeExcerpt('app/heroes.component.ts (showing renamings only)', 'renaming')
 | ||
| 
 | ||
| :marked
 | ||
|   ## Create *AppComponent*
 | ||
| 
 | ||
|   The new `AppComponent` will be the application shell.
 | ||
|   It will have some navigation links at the top and a display area below for the pages we navigate to.
 | ||
| 
 | ||
|   The initial steps are:
 | ||
| 
 | ||
|   * Create the file <span ngio-ex>app/app.component.ts</span>.
 | ||
|   * Define an <span if-docs="ts">exported</span> `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 `<h1>` element, which contains a binding to  `title`
 | ||
|   * Add a `<my-heroes>` element to the app template just below the heading so we still see the heroes.
 | ||
|   * Add `HeroesComponent` to the `!{_declsVsDirectives}` !{_array} of `!{_AppModuleVsAppComp}` so Angular recognizes the `<my-heroes>` tags.
 | ||
|   * Add `HeroService` to the  `providers` !{_array} of `!{_AppModuleVsAppComp}` because we'll need it in every other view.
 | ||
|   * Remove `HeroService` from the `HeroesComponent` `providers` !{_array} since it has been promoted.
 | ||
|   * Add the supporting `import` statements for `AppComponent`.
 | ||
| 
 | ||
|   Our first draft looks like this:
 | ||
| 
 | ||
| block app-comp-v1
 | ||
|   +makeTabs(
 | ||
|     `toh-5/ts/app/app.component.1.ts,
 | ||
|     toh-5/ts/app/app.module.1.ts`,
 | ||
|     ',',
 | ||
|     `app/app.component.ts (v1),
 | ||
|     app/app.module.ts (v1)`)
 | ||
| 
 | ||
| :marked
 | ||
|   The app still runs and still displays heroes.
 | ||
|   Our refactoring of `AppComponent` into a new `AppComponent` and a `HeroesComponent` worked!
 | ||
|   We have done no harm.
 | ||
| 
 | ||
| :marked
 | ||
|   ## Add Routing
 | ||
| 
 | ||
|   We're ready to take the next step.
 | ||
|   Instead of displaying heroes automatically, we'd like to show them *after* the user clicks a button.
 | ||
|   In other words, we'd like to navigate to the list of heroes.
 | ||
| 
 | ||
|   We'll need the Angular *Component Router*.
 | ||
| 
 | ||
| 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`). We'll configure our routes first.
 | ||
| 
 | ||
| :marked
 | ||
|   ### Add the base tag
 | ||
| 
 | ||
|   Open `index.html` and add `<base href="/">` at the top of the `<head>` section.
 | ||
| 
 | ||
| +makeExcerpt('index.html', 'base-href')
 | ||
| 
 | ||
| .callout.is-important
 | ||
|   header base href is essential
 | ||
|   :marked
 | ||
|     See the *base href* section of the [Router](../guide/router.html#!#base-href)
 | ||
|     chapter to learn why this matters.
 | ||
| 
 | ||
| a#configure-routes
 | ||
| block router-config-intro
 | ||
|   :marked
 | ||
|     ### Configure routes
 | ||
| 
 | ||
|     Our application doesn't have any routes yet.
 | ||
|     We'll start by creating a configuration file for the application 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.
 | ||
| 
 | ||
|   Let's define our first route as a route to the heroes component:
 | ||
| 
 | ||
| - var _file = _docsFor == 'dart' ? 'app.component.ts' : 'app.routing.ts'
 | ||
| +makeExcerpt('app/' + _file + ' (heroes route)', 'heroes')
 | ||
| 
 | ||
| - var _are = _docsFor == 'dart' ? 'takes' : 'are'
 | ||
| - var _routePathPrefix = _docsFor == 'dart' ? '/' : ''
 | ||
| :marked
 | ||
|   The `!{_RoutesVsAtRouteConfig}` !{_are} !{_an} !{_array} of *route definitions*.
 | ||
|   We have only one route definition at the moment but rest assured, we'll add more.
 | ||
| 
 | ||
|   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`).
 | ||
|   <li if-docs="dart"> **name**: the official name of the route;
 | ||
|    it *must* begin with a capital letter to avoid confusion with the *path* (`Heroes`).</li>
 | ||
|   - **component**: the component that the router should create when navigating to this route (`HeroesComponent`).
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   :marked
 | ||
|     Learn more about defining routes with `!{_RoutesVsAtRouteConfig}` in the [Routing](../guide/router.html) chapter.
 | ||
| 
 | ||
| +ifDocsFor('ts|js')
 | ||
|   :marked
 | ||
|     We'll export a `routing` constant initialized using the `RouterModule.forRoot` method applied to our !{_array} of routes.
 | ||
|     This method returns a **configured router module** that we'll add to our root NgModule, `AppModule`.
 | ||
| 
 | ||
|   +makeExcerpt('app/app.routing.1.ts (excerpt)', 'routing-export')
 | ||
| 
 | ||
|   .l-sub-section
 | ||
|     :marked
 | ||
|       We call the `forRoot` method because we're providing a configured router at the _root_ of the application.
 | ||
|       The `forRoot` method gives us the Router service providers and directives needed for routing.
 | ||
| 
 | ||
|   :marked
 | ||
|     ### Make the router available
 | ||
| 
 | ||
|     We've setup initial routes in the `app.routing.ts` file. Now we'll add it to our root NgModule.
 | ||
| 
 | ||
|     Import the `routing` constant from `app.routing.ts` and add it the `imports` !{_array} of `AppModule`.
 | ||
| 
 | ||
|   +makeExcerpt('app/app.module.ts', 'routing')
 | ||
| 
 | ||
| - var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes'
 | ||
| :marked
 | ||
|   ### Router Outlet
 | ||
| 
 | ||
|   If we paste the path, `/heroes`, into the browser address bar,
 | ||
|   the router should match it to the `!{_heroesRoute}` route and display the `HeroesComponent`.
 | ||
|   But where?
 | ||
| 
 | ||
|   We have to ***tell it where*** by adding a `<router-outlet>` element to the bottom of the template.
 | ||
|   `RouterOutlet` is one of the <span if-docs="ts">directives provided by</span> the `!{_RouterModuleVsRouterDirectives}`.
 | ||
|   The router displays each component immediately below the `<router-outlet>` as we navigate through the application.
 | ||
| 
 | ||
|   ### Router Links
 | ||
| 
 | ||
|   We don't really expect users to paste a route URL into the address bar.
 | ||
|   We add an anchor tag to the template which, when clicked, triggers navigation to the `HeroesComponent`.
 | ||
| 
 | ||
|   The revised template looks like this:
 | ||
| 
 | ||
| +makeExcerpt('app/app.component.1.ts', 'template-v2')
 | ||
| 
 | ||
| block routerLink
 | ||
|   :marked
 | ||
|     Notice the `routerLink` binding in the anchor tag.
 | ||
|     We bind the `RouterLink` directive (another of the `RouterModule` directives) to a string
 | ||
|     that tells the router where to navigate when the user clicks the link.
 | ||
| 
 | ||
|     Since our link is not dynamic, we define a *routing instruction* with a **one-time binding** to our route **path**.
 | ||
|     Looking back at the route configuration, we confirm that `'/heroes'` is the path of the route to the `HeroesComponent`.
 | ||
|   .l-sub-section
 | ||
|     :marked
 | ||
|       Learn more about dynamic router links and the *link parameters array*
 | ||
|       in the [Routing](../guide/router.html#link-parameters-array) chapter.
 | ||
| 
 | ||
| :marked
 | ||
|   Refresh the browser.  We see only the app title and heroes link. We don't see the heroes list.
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   :marked
 | ||
|     The browser's address bar shows `/`.
 | ||
|     The route path to `HeroesComponent` is `/heroes`, not `/`.
 | ||
|     We don't have a route that matches the path `/`, so there is nothing to show.
 | ||
|     That's something we'll want to fix.
 | ||
| 
 | ||
| :marked
 | ||
|   We click the *Heroes* navigation link, the browser bar updates to `/heroes`,
 | ||
|   and now we see the list of heroes. We are navigating at last!
 | ||
| 
 | ||
|   At this stage, our `AppComponent` looks like this.
 | ||
| 
 | ||
| +makeExample('app/app.component.1.ts', 'v2', 'app/app.component.ts (v2)')
 | ||
| 
 | ||
| :marked
 | ||
|   The  *AppComponent* is now attached to a router and displaying routed views.
 | ||
|   For this reason and to distinguish it from other kinds of components,
 | ||
|   we call this type of component a *Router Component*.
 | ||
| 
 | ||
| :marked
 | ||
|   ## Add a *Dashboard*
 | ||
| 
 | ||
|   Routing only makes sense when we have multiple views. We need another view.
 | ||
| 
 | ||
|   Create a placeholder `DashboardComponent` that gives us something to navigate to and from.
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.1.ts (v1)', '')
 | ||
| 
 | ||
| :marked
 | ||
|   We’ll come back and make it more useful later.
 | ||
| 
 | ||
|   ### Configure the dashboard route
 | ||
| 
 | ||
|   Go back to `!{_appRoutingTsVsAppComp}` and teach it 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' : 'app/app.routing.ts'
 | ||
| +makeExcerpt(_file + ' (Dashboard route)', 'dashboard')
 | ||
| 
 | ||
| +ifDocsFor('ts|js')
 | ||
|   :marked
 | ||
|     Also import and add `DashboardComponent` to our root NgModule's `declarations`.
 | ||
| 
 | ||
|   +makeExcerpt('app/app.module.ts', 'dashboard')
 | ||
| 
 | ||
| :marked
 | ||
|   #### !{_redirectTo}
 | ||
| 
 | ||
|   We want the app to show the dashboard when it starts and
 | ||
|   we want to see a nice URL in the browser address bar that says `/dashboard`.
 | ||
|   Remember that the browser launches with `/` in the address bar.
 | ||
| 
 | ||
| block redirect-vs-use-as-default
 | ||
|   :marked
 | ||
|     We can use a redirect route to make this happen. Add the following
 | ||
|     to our array of route definitions:
 | ||
| 
 | ||
|   +makeExcerpt('app/app.routing.ts','redirect')
 | ||
| 
 | ||
|   .l-sub-section
 | ||
|     :marked
 | ||
|       Learn about the *redirects* in the [Routing](../guide/router.html#!#redirect) chapter.
 | ||
| 
 | ||
| :marked
 | ||
|   #### Add navigation to the template
 | ||
| 
 | ||
|   Finally, add a dashboard navigation link to the template, just above the *Heroes* link.
 | ||
| 
 | ||
| - var _vers = _docsFor == 'dart' ? '' : '.1'
 | ||
| +makeExcerpt('app/app.component' + _vers + '.ts', 'template-v3')
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   :marked
 | ||
|     We nestled the two links within `<nav>` tags.
 | ||
|     They don't do anything yet but they'll be convenient when we style the links a little later in the chapter.
 | ||
| 
 | ||
| :marked
 | ||
|   To see these changes in your browser, go to the application root (`/`) and reload.
 | ||
|   The app displays the dashboard and we can navigate between the dashboard and the heroes.
 | ||
| 
 | ||
|   ## Dashboard Top Heroes
 | ||
| 
 | ||
|   Let’s spice up the dashboard by displaying the top four heroes at a glance.
 | ||
| 
 | ||
|   Replace the `template` metadata with a `templateUrl` property that points to a new
 | ||
|   template file.
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.ts', 'templateUrl')
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   block templateUrl-path-resolution
 | ||
|     :marked
 | ||
|       We specify the path _all the way back to the application root_ —
 | ||
|       <span if-docs="ts">`app/` in this case —</span>
 | ||
|       because Angular doesn't support relative paths _by default_.
 | ||
|       We _can_ switch to [component-relative paths](../cookbook/component-relative-paths.html) if we prefer.
 | ||
| 
 | ||
| :marked
 | ||
|   Create that file with this content:
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.html')
 | ||
| 
 | ||
| :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.
 | ||
| 
 | ||
|   There's a `(click)` binding to a `gotoDetail` method we haven't written yet and
 | ||
|   we're displaying a list of heroes that we don't have.
 | ||
|   We have work to do, starting with those heroes.
 | ||
| 
 | ||
|   ### Share the *HeroService*
 | ||
| 
 | ||
|   We'd like to re-use the `HeroService` to populate the component's `heroes` !{_array}.
 | ||
| 
 | ||
|   Recall earlier in the chapter that we removed the `HeroService` from the `providers` !{_array} of `HeroesComponent`
 | ||
|   and added it to the `providers` !{_array} of `!{_AppModuleVsAppComp}`.
 | ||
| 
 | ||
|   That move created a singleton `HeroService` instance, available to *all* components of the application.
 | ||
|   Angular will inject `HeroService` and we'll use it here in the `DashboardComponent`.
 | ||
| 
 | ||
|   ### Get heroes
 | ||
| 
 | ||
|   Open <span ngio-ex>dashboard.component.ts</span> and add the requisite `import` statements.
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.2.ts','imports')
 | ||
| 
 | ||
| :marked
 | ||
|   Now implement the `DashboardComponent` class like this:
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.2.ts (class)', 'component')
 | ||
| 
 | ||
| :marked
 | ||
|   We've seen this kind of logic before in the `HeroesComponent`:
 | ||
| 
 | ||
|   * Define a `heroes` !{_array} property.
 | ||
|   * Inject the `HeroService` in the constructor and hold it in a private `!{_priv}heroService` field.
 | ||
|   * Call the service to get heroes inside the Angular `ngOnInit` lifecycle hook.
 | ||
| 
 | ||
|   The noteworthy differences: we cherry-pick four heroes (2nd, 3rd, 4th, and 5th)
 | ||
|   and stub the `gotoDetail` method until we're ready to implement it.
 | ||
| 
 | ||
|   Refresh the browser and see four heroes in the new dashboard.
 | ||
| 
 | ||
| .l-main-section
 | ||
| :marked
 | ||
|   ## Navigate to Hero Details
 | ||
| 
 | ||
|   Although we display the details of a selected hero at the bottom of the `HeroesComponent`,
 | ||
|   we don't yet *navigate* to the `HeroDetailComponent` in the three ways specified in our requirements:
 | ||
| 
 | ||
|   1. from the *Dashboard* to a selected hero.
 | ||
|   1. from the *Heroes* list to a selected hero.
 | ||
|   1. from a "deep link" URL pasted into the browser address bar.
 | ||
| 
 | ||
|   Adding a hero-detail route seems like an obvious place to start.
 | ||
| 
 | ||
|   ### Routing to a hero detail
 | ||
| 
 | ||
|   We'll add a route to the `HeroDetailComponent` in `!{_appRoutingTsVsAppComp}` where our other routes are configured.
 | ||
| 
 | ||
|   The new route is a bit unusual in that we must tell the `HeroDetailComponent` *which hero to show*.
 | ||
|   We didn't have to tell the `HeroesComponent` or the `DashboardComponent` anything.
 | ||
| 
 | ||
|   At the moment the parent `HeroesComponent` sets the component's `hero` property to a
 | ||
|   hero object with a binding like this.
 | ||
| 
 | ||
| code-example(language="html").
 | ||
|   <my-hero-detail [hero]="selectedHero"></my-hero-detail>
 | ||
| 
 | ||
| :marked
 | ||
|   That clearly won't work in any of our routing scenarios.
 | ||
|   Certainly not the last one; we can't embed an entire hero object in the URL! Nor would we want to.
 | ||
| 
 | ||
|   ### Parameterized route
 | ||
| 
 | ||
|   We *can* add the hero's `id` to the URL. When routing to the hero whose `id` is 11,
 | ||
|   we could expect to see an URL such as this:
 | ||
| 
 | ||
| code-example(format='').
 | ||
|   /detail/11
 | ||
| 
 | ||
| :marked
 | ||
|   The `/detail/` part of that URL is constant. The trailing numeric `id` part changes from hero to hero.
 | ||
|   We need to represent that variable part of the route with a *parameter* (or *token*) that stands for the hero's `id`.
 | ||
| 
 | ||
|   ### Configure a Route with a Parameter
 | ||
| 
 | ||
|   Here's the *route definition* we'll use.
 | ||
| 
 | ||
| - var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app.routing.ts'
 | ||
| +makeExcerpt(_file + ' (hero detail)','hero-detail')
 | ||
| 
 | ||
| :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`.
 | ||
| 
 | ||
| +ifDocsFor('dart')
 | ||
|   .l-sub-section
 | ||
|     :marked
 | ||
|       Remember to import the hero detail component before creating this route.
 | ||
| 
 | ||
| :marked
 | ||
|   We're finished with the application routes.
 | ||
| 
 | ||
|   We won't add a `'Hero Detail'` link to the template because users
 | ||
|   don't click a navigation *link* to view a particular hero.
 | ||
|   They click a *hero* whether that hero is displayed on the dashboard or in the heroes list.
 | ||
| 
 | ||
|   We'll get to those *hero* clicks later in the chapter.
 | ||
|   There's no point in working on them until the `HeroDetailComponent`
 | ||
|   is ready to be navigated *to*.
 | ||
| 
 | ||
|   That will require an `HeroDetailComponent` overhaul.
 | ||
| 
 | ||
| .l-main-section
 | ||
| :marked
 | ||
|   ## Revise the *HeroDetailComponent*
 | ||
| 
 | ||
|   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.
 | ||
| 
 | ||
| block route-params
 | ||
|   :marked
 | ||
|     We will no longer receive the hero in a parent component property binding.
 | ||
|     The new `HeroDetailComponent` should take the `id` parameter from the `params` observable
 | ||
|     in the `ActivatedRoute` service and use the `HeroService` to fetch the hero with that `id`.
 | ||
| 
 | ||
| :marked
 | ||
|   First, add the requisite imports:
 | ||
| 
 | ||
| - var _vers = _docsFor == 'dart' ? '' : '.1'
 | ||
| +makeExcerpt('app/hero-detail.component' + _vers + '.ts', 'added-imports', '')
 | ||
| 
 | ||
| - var _ActivatedRoute = _docsFor == 'dart' ? 'RouteParams' : 'ActivatedRoute'
 | ||
| :marked
 | ||
|   Let's have the `!{_ActivatedRoute}` service and the `HeroService` injected
 | ||
|   into the constructor, saving their values in private fields:
 | ||
| 
 | ||
| +makeExcerpt('app/hero-detail.component.ts (constructor)', 'ctor')
 | ||
| 
 | ||
| :marked
 | ||
|   We tell the class that we want to implement the `OnInit` interface.
 | ||
| 
 | ||
| +makeExcerpt('app/hero-detail.component.ts', 'implement', '')(format=".")
 | ||
| 
 | ||
| block ngOnInit
 | ||
|   :marked
 | ||
|     Inside the `ngOnInit` lifecycle hook, we use the `params` observable to
 | ||
|     extract the `id` parameter value from the `ActivateRoute` service
 | ||
|     and use the `HeroService` to fetch the hero with that `id`.
 | ||
| 
 | ||
| +makeExcerpt('app/hero-detail.component.ts', 'ngOnInit')
 | ||
| 
 | ||
| block extract-id
 | ||
|   :marked
 | ||
|     Notice how we extract the `id` by calling the `forEach` method
 | ||
|     which will deliver our !{_array} of route parameters.
 | ||
| 
 | ||
| - var _str2int = _docsFor == 'dart' ? '<code>int.parse</code> static method' : 'JavaScript (+) operator'
 | ||
| :marked
 | ||
|   The hero `id` is a number. Route parameters are *always strings*.
 | ||
|   So we convert the route parameter value to a number with the !{_str2int}.
 | ||
| 
 | ||
|   ### Add *HeroService.getHero*
 | ||
| 
 | ||
|   The problem with this bit of code is that `HeroService` doesn't have a `getHero` method!
 | ||
|   We better fix that quickly before someone notices that we broke the app.
 | ||
| 
 | ||
|   Open `HeroService` and add a `getHero` method that filters the heroes list from `getHeroes` by `id`:
 | ||
| 
 | ||
| +makeExcerpt('app/hero.service.ts', 'getHero')
 | ||
| 
 | ||
| :marked
 | ||
|   Let's return to the `HeroDetailComponent` to clean up loose ends.
 | ||
| 
 | ||
|   ### Find our way back
 | ||
| 
 | ||
|   We can navigate *to* the `HeroDetailComponent` in several ways.
 | ||
|   How do we navigate somewhere else when we're done?
 | ||
| 
 | ||
|   The user could click one of the two links in the `AppComponent`. Or click the browser's back button.
 | ||
|   We'll add a third option, a `goBack` method that navigates backward one step in the browser's history stack.
 | ||
| 
 | ||
| +makeExcerpt('app/hero-detail.component.ts', 'goBack')
 | ||
| 
 | ||
| - var _CanDeactivateGuard = _docsFor == 'dart' ? '<em>routerCanDeactivate</em> hook' : '<em>CanDeactivate</em> guard'
 | ||
| - var _CanDeactivateGuardUri = _docsFor == 'dart' ? 'angular2.router/CanDeactivate-class' : 'router/index/CanDeactivate-interface'
 | ||
| .l-sub-section
 | ||
|   :marked
 | ||
|     Going back too far could take us out of the application.
 | ||
|     That's acceptable in a demo. We'd guard against it in a real application,
 | ||
|     perhaps with the [!{_CanDeactivateGuard}](../api/!{_CanDeactivateGuardUri}.html).
 | ||
| 
 | ||
| :marked
 | ||
|   Then we wire this method with an event binding to a *Back* button that we
 | ||
|   add to the bottom of the component template.
 | ||
| 
 | ||
| +makeExcerpt('app/hero-detail.component.html', 'back-button', '')
 | ||
| 
 | ||
| :marked
 | ||
|   Modifing the template to add this button spurs us to take one more
 | ||
|   incremental improvement and migrate the template to its own file,
 | ||
|   called <span ngio-ex>hero-detail.component.html</span>:
 | ||
| 
 | ||
| +makeExample('app/hero-detail.component.html')
 | ||
| 
 | ||
| :marked
 | ||
|   We update the component metadata with a `templateUrl` pointing to the template file that we just created.
 | ||
| 
 | ||
| +makeExcerpt('app/hero-detail.component.ts', 'templateUrl')
 | ||
| 
 | ||
| :marked
 | ||
|   Refresh the browser and see the results.
 | ||
| 
 | ||
| .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.
 | ||
| 
 | ||
|   In the dashboard template we bound each hero's click event to the `gotoDetail` method, passing along the selected `hero` entity.
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.html', 'click')
 | ||
| 
 | ||
| :marked
 | ||
|   We stubbed the `gotoDetail` method when we rewrote the `DashboardComponent`.
 | ||
|   Now we give it a real implementation.
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.ts','gotoDetail')
 | ||
| 
 | ||
| - var _pathVsName = _docsFor == 'dart' ? 'name' : 'path'
 | ||
| :marked
 | ||
|   The `gotoDetail` method navigates in two steps:
 | ||
| 
 | ||
|   1. Set a route *link parameters !{_array}*
 | ||
|   1. Pass the !{_array} to the router's navigate method
 | ||
| 
 | ||
|   For navigation, we wrote router links <span if-docs="dart">as *link
 | ||
|   parameters !{_array}s*</span> in the [`AppComponent`
 | ||
|   template](#router-links).  Those link<span if-docs="dart"> parameters
 | ||
|   !{_array}</span>s had only one element, the !{_pathVsName} of the
 | ||
|   destination route.
 | ||
| 
 | ||
|   This link parameters !{_array} has two elements, the ***!{_pathVsName}*** of
 | ||
|   the destination route and a ***route parameter*** <span if-docs="dart">with
 | ||
|   an `id` field</span> set to the value of the selected hero's `id`.
 | ||
| 
 | ||
|   The two !{_array} items align with the ***!{_pathVsName}*** and ***:id***
 | ||
|   token in the parameterized hero detail route definition we added to
 | ||
|   `!{_appRoutingTsVsAppComp}` earlier in the chapter:
 | ||
| 
 | ||
| - var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app.routing.ts'
 | ||
| +makeExcerpt(_file + ' (hero detail)', 'hero-detail')
 | ||
| 
 | ||
| :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`):
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.ts ()','import-router', '')
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.ts', 'ctor', '')
 | ||
| 
 | ||
| :marked
 | ||
|   Refresh the browser and select a hero from the dashboard; the app should navigate directly to that hero’s details.
 | ||
| 
 | ||
| .l-main-section
 | ||
| :marked
 | ||
|   ## Select a Hero in the *HeroesComponent*
 | ||
| 
 | ||
|   We'll do something similar in the `HeroesComponent`.
 | ||
| 
 | ||
|   That component's current template exhibits a "master/detail" style with the list of heroes
 | ||
|   at the top and details of the selected hero below.
 | ||
| 
 | ||
| +makeExample('toh-4/ts/app/app.component.ts','template', 'app/heroes.component.ts (current template)')(format=".")
 | ||
| 
 | ||
| :marked
 | ||
|   Delete the last line of the template with the `<my-hero-detail>` tags.
 | ||
| 
 | ||
|   We'll no longer show the full `HeroDetailComponent` here.
 | ||
|   We're going to display the hero detail on its own page and route to it as we did in the dashboard.
 | ||
| 
 | ||
|   But we'll throw in a small twist for variety.
 | ||
|   When the user selects a hero from the list, we *won't* go to the detail page.
 | ||
|   We'll show a *mini-detail* on *this* page instead and make the user click a button to navigate to the *full detail *page.
 | ||
| 
 | ||
|   ### Add the *mini-detail*
 | ||
| 
 | ||
|   Add the following HTML fragment at the bottom of the template where the `<my-hero-detail>` used to be:
 | ||
| 
 | ||
| +makeExcerpt('app/heroes.component.html', 'mini-detail', '')
 | ||
| 
 | ||
| :marked
 | ||
|   After clicking a hero, the user should see something like this below the hero list:
 | ||
| 
 | ||
| figure.image-display
 | ||
|   img(src='/resources/images/devguide/toh/mini-hero-detail.png' alt="Mini Hero Detail" height="70")
 | ||
| 
 | ||
| :marked
 | ||
|   ### Format with the *uppercase* pipe
 | ||
| 
 | ||
|   Notice that the hero's name is displayed in CAPITAL LETTERS. That's the effect of the `uppercase` pipe
 | ||
|   that we slipped into the interpolation binding. Look for it right after the pipe operator ( | ).
 | ||
| 
 | ||
| +makeExcerpt('app/heroes.component.html', 'pipe', '')
 | ||
| 
 | ||
| :marked
 | ||
|   Pipes are a good way to format strings, currency amounts, dates and other display data.
 | ||
|   Angular ships with several pipes and we can write our own.
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   :marked
 | ||
|     Learn about pipes in the [Pipes](../guide/pipes.html) chapter.
 | ||
| 
 | ||
| :marked
 | ||
|   ### Move content out of the component file
 | ||
| 
 | ||
|   We are not done. We still have to update the component class to support navigation to the
 | ||
|   `HeroDetailComponent` when the user clicks the *View Details* button.
 | ||
| 
 | ||
|   This component file is really big. Most of it is either template or CSS styles.
 | ||
|   It's difficult to find the component logic amidst the noise of HTML and CSS.
 | ||
| 
 | ||
|   Let's migrate the template and the styles to their own files before we make any more changes:
 | ||
| 
 | ||
|   1. *Cut-and-paste* the template contents into a new <span ngio-ex>heroes.component.html</span> file.
 | ||
|   1. *Cut-and-paste* the styles contents into a new <span ngio-ex>heroes.component.css</span> file.
 | ||
|   1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files.
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   :marked
 | ||
|     The `styleUrls` property is !{_an} !{_array} of style file names (with paths).
 | ||
|     We could list multiple style files from different locations if we needed them.
 | ||
|     <span if-docs="ts">As with `templateUrl`, we must specify the path _all the way
 | ||
|     back to the application root_.</span>
 | ||
| 
 | ||
| block heroes-component-cleanup
 | ||
|   //- Only relevant for Dart.
 | ||
| 
 | ||
| +makeExcerpt('app/heroes.component.ts (revised metadata)', 'metadata')
 | ||
| 
 | ||
| :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 hero-detail link parameters !{_array}.
 | ||
| 
 | ||
|   Here's the revised component class:
 | ||
| 
 | ||
| +makeExcerpt('app/heroes.component.ts', 'class')
 | ||
| 
 | ||
| :marked
 | ||
|   Refresh the browser and start clicking.
 | ||
|   We can navigate around the app, from the dashboard to hero details and back,
 | ||
|   for heroes list to the mini-detail to the hero details and back to the heroes again.
 | ||
|   We can jump back and forth between the dashboard and the heroes.
 | ||
| 
 | ||
|   We've met all of the navigational requirements that propelled this chapter.
 | ||
| 
 | ||
| .l-main-section
 | ||
| :marked
 | ||
|   ## Styling the App
 | ||
| 
 | ||
|   The app is functional but pretty ugly.
 | ||
|   Our creative designer team provided some CSS files to make it look better.
 | ||
| 
 | ||
|   ### A Dashboard with Style
 | ||
| 
 | ||
|   The designers think we should display the dashboard heroes in a row of rectangles.
 | ||
|   They've given us ~60 lines of CSS for this purpose including some simple media queries for responsive design.
 | ||
| 
 | ||
|   If we paste these ~60 lines into the component `styles` metadata,
 | ||
|   they'll completely obscure the component logic.
 | ||
|   Let's not do that. It's easier to edit CSS in a separate `*.css` file anyway.
 | ||
| 
 | ||
|   Add a <span ngio-ex>dashboard.component.css</span> file to the `!{_appDir}` folder and reference
 | ||
|   that file in the component metadata's `styleUrls` !{_array} property like this:
 | ||
| 
 | ||
| +makeExcerpt('app/dashboard.component.ts (styleUrls)', 'css')
 | ||
| 
 | ||
| :marked
 | ||
|   ### Stylish Hero Details
 | ||
| 
 | ||
|   The designers also gave us CSS styles specifically for the `HeroDetailComponent`.
 | ||
| 
 | ||
|   Add a <span ngio-ex>hero-detail.component.css</span> to the `!{_appDir}`
 | ||
|   folder and refer to that file inside
 | ||
|   the `styleUrls` !{_array} as we did for `DashboardComponent`.
 | ||
|   Let's also remove the `hero` property `@Input` !{_decorator}
 | ||
|   <span if-docs="ts">and its import</span>
 | ||
|   while we are at it.
 | ||
| 
 | ||
|   Here's the content for the aforementioned component CSS files.
 | ||
| 
 | ||
| block css-files
 | ||
|   +makeTabs(
 | ||
|     `toh-5/ts/app/hero-detail.component.css,
 | ||
|     toh-5/ts/app/dashboard.component.css`,
 | ||
|     null,
 | ||
|     `app/hero-detail.component.css,
 | ||
|     app/dashboard.component.css`)
 | ||
| 
 | ||
| :marked
 | ||
|   ### Style the Navigation Links
 | ||
| 
 | ||
|   The designers gave us CSS to make the navigation links in our `AppComponent` look more like selectable buttons.
 | ||
|   We cooperated by surrounding those links in `<nav>` tags.
 | ||
| 
 | ||
|   Add a <span ngio-ex>app.component.css</span> file to the `!{_appDir}` folder with the following content.
 | ||
| 
 | ||
| +makeExcerpt('app/app.component.css (navigation styles)', '')
 | ||
| 
 | ||
| .l-sub-section
 | ||
|   block router-link-active
 | ||
|     :marked
 | ||
|       **The *routerLinkActive* directive**
 | ||
| 
 | ||
|       The Angular Router provides a `routerLinkActive` directive we can use to
 | ||
|       add a class to the HTML navigation element whose route matches the active route.
 | ||
|       All we have to do is define the style for it. Sweet!
 | ||
| 
 | ||
|     +makeExcerpt('app/app.component.ts (active router links)', 'template')
 | ||
| 
 | ||
| :marked
 | ||
|   Set the `AppComponent`’s `styleUrls` property to this CSS file.
 | ||
| 
 | ||
| +makeExcerpt('app/app.component.ts','styleUrls')
 | ||
| 
 | ||
| :marked
 | ||
|   ### Global application styles
 | ||
| 
 | ||
|   When we add styles to a component, we're keeping everything a component needs
 | ||
|   — HTML, the CSS, the code — together in one convenient place.
 | ||
|   It's pretty easy to package it all up and re-use the component somewhere else.
 | ||
| 
 | ||
|   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.
 | ||
|   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:
 | ||
| 
 | ||
| +makeExcerpt('styles.css (excerpt)', 'toh')
 | ||
| 
 | ||
| - var styles_css = 'https://raw.githubusercontent.com/angular/angular.io/master/public/docs/_examples/styles.css'
 | ||
| 
 | ||
| :marked
 | ||
|   Create the file <span ngio-ex>styles.css</span>, if it doesn't exist already.
 | ||
|   Ensure that it contains the [master styles given here](!{styles_css}).
 | ||
| 
 | ||
|   If necessary, also edit <span ngio-ex>index.html</span> to refer to this stylesheet.
 | ||
| 
 | ||
| +makeExcerpt('index.html (link ref)', 'css')
 | ||
| 
 | ||
| :marked
 | ||
|   Look at the app now. Our dashboard, heroes, and navigation links are styling!
 | ||
| 
 | ||
| figure.image-display
 | ||
|   img(src='/resources/images/devguide/toh/dashboard-top-heroes.png' alt="View navigations")
 | ||
| 
 | ||
| .l-main-section
 | ||
| :marked
 | ||
|   ## Application structure and code
 | ||
| 
 | ||
|   Review the sample source code in the <live-example></live-example> for this chapter.
 | ||
|   Verify that we have the following structure:
 | ||
| 
 | ||
| block file-tree-end
 | ||
|   .filetree
 | ||
|     .file angular2-tour-of-heroes
 | ||
|     .children
 | ||
|       .file app
 | ||
|       .children
 | ||
|         .file app.component.css
 | ||
|         .file app.component.ts
 | ||
|         .file app.module.ts
 | ||
|         .file app.routing.ts
 | ||
|         .file dashboard.component.css
 | ||
|         .file dashboard.component.html
 | ||
|         .file dashboard.component.ts
 | ||
|         .file hero.service.ts
 | ||
|         .file hero.ts
 | ||
|         .file hero-detail.component.css
 | ||
|         .file hero-detail.component.html
 | ||
|         .file hero-detail.component.ts
 | ||
|         .file heroes.component.css
 | ||
|         .file heroes.component.html
 | ||
|         .file heroes.component.ts
 | ||
|         .file main.ts
 | ||
|         .file mock-heroes.ts
 | ||
|       .file node_modules ...
 | ||
|       .file typings ...
 | ||
|       .file index.html
 | ||
|       .file package.json
 | ||
|       .file styles.css
 | ||
|       .file systemjs.config.js
 | ||
|       .file tsconfig.json
 | ||
|       .file typings.json
 | ||
| 
 | ||
| .l-main-section
 | ||
| :marked
 | ||
|   ## Recap
 | ||
| 
 | ||
|   ### 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 link 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.
 | ||
| 
 | ||
|   ### 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 the next chapter,
 | ||
|   we’ll replace our mock data with data retrieved from a server using http.
 |