| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | - var _example = 'toh-5'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | block includes | 
					
						
							|  |  |  |  |   include ../_util-fns | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   - var _appRoutingTsVsAppComp = 'app.module.ts' | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   - var _declsVsDirectives = 'declarations' | 
					
						
							|  |  |  |  |   - var _RoutesVsAtRouteConfig = 'Routes' | 
					
						
							|  |  |  |  |   - var _RouterModuleVsRouterDirectives = 'RouterModule' | 
					
						
							|  |  |  |  |   - var _redirectTo = 'redirectTo' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   We received new requirements for our Tour of Heroes application: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   * 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. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   * Clicking a *deep link* in an email opens the detail view for a particular hero. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   When we’re done, users will be able to navigate the app like this: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | figure.image-display | 
					
						
							|  |  |  |  |   img(src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations") | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   We'll add Angular’s *Router* to our app to satisfy these requirements. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |     The [Routing and Navigation](../guide/router.html) chapter covers the router | 
					
						
							|  |  |  |  |     in more detail than we will in this tutorial. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   Run the <live-example></live-example> for this part. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +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: | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | .l-main-section | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Where We Left Off | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   Before we continue with our Tour of Heroes, let’s verify that | 
					
						
							|  |  |  |  |   we have the following structure after adding our hero service | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   and hero detail component. If not, we’ll need to go back and follow the previous chapters. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | block intro-file-tree | 
					
						
							|  |  |  |  |   .filetree | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     .file angular-tour-of-heroes | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |     .children | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |       .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 index.html | 
					
						
							|  |  |  |  |       .file package.json | 
					
						
							|  |  |  |  |       .file styles.css | 
					
						
							|  |  |  |  |       .file systemjs.config.js | 
					
						
							|  |  |  |  |       .file tsconfig.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 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   The application runs and updates automatically as we continue to build the Tour of Heroes. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ## Action plan | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Our revised app should present a shell with a choice of views (*Dashboard* and *Heroes*) | 
					
						
							|  |  |  |  |   and then default to one of them. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   The `AppComponent` should only handle navigation. | 
					
						
							|  |  |  |  |   Let's move the display of *Heroes* out of `AppComponent` and into its own `HeroesComponent`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### *HeroesComponent* | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   `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: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   * <span ngio-ex>app.component.ts</span> file to <span ngio-ex>heroes.component.ts</span> | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   * `AppComponent` class to `HeroesComponent` | 
					
						
							|  |  |  |  |   * Selector `my-app` to `my-heroes` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +makeExcerpt('app/heroes.component.ts (showing renamings only)', 'renaming') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Create *AppComponent* | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   * 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`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   Our first draft looks like this: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 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)`) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   We'll need the Angular *Router*. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 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') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | .callout.is-important | 
					
						
							|  |  |  |  |   header base href is essential | 
					
						
							|  |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-11-15 15:36:34 -08:00
										 |  |  |  |     See the *base href* section of the [Router](../guide/router.html#base-href) | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |     chapter to learn why this matters. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | a#configure-routes | 
					
						
							|  |  |  |  | block router-config-intro | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     ### Configure routes | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |     Our application doesn't have any routes yet. | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     We'll start by creating a configuration for the application routes. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   *Routes* tell the router which views to display when a user clicks a link or | 
					
						
							|  |  |  |  |   pastes a URL into the browser address bar. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Let's define our first route as a route to the heroes component: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | - var _file = _docsFor == 'dart' ? 'app.component.ts' : 'app.module.2.ts' | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +makeExcerpt('app/' + _file + ' (heroes route)', 'heroes') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | - var _are = _docsFor == 'dart' ? 'takes' : 'are' | 
					
						
							|  |  |  |  | - var _routePathPrefix = _docsFor == 'dart' ? '/' : '' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   The `!{_RoutesVsAtRouteConfig}` !{_are} !{_an} !{_array} of *route definitions*. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   We have only one route definition at the moment but rest assured, we'll add more. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   This *route definition* has the following parts: | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   - **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`). | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |     Learn more about defining routes with `!{_RoutesVsAtRouteConfig}` in the [Routing](../guide/router.html) chapter. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +ifDocsFor('ts|js') | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     ### Make the router available | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     We've setup the initial route configuration. Now we'll add it to our `AppModule`. | 
					
						
							|  |  |  |  |     We'll add our configured `RouterModule` to the `AppModule` imports !{_array}. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   +makeExcerpt('app/app.module.2.ts (app routing)', '') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   .l-sub-section | 
					
						
							|  |  |  |  |     :marked | 
					
						
							|  |  |  |  |       We use 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, and | 
					
						
							|  |  |  |  |       performs the initial navigation based on the current browser URL. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | - var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   ### Router Outlet | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   If we paste the path, `/heroes`, into the browser address bar, | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   the router should match it to the `!{_heroesRoute}` route and display the `HeroesComponent`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   But where? | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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}`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   The router displays each component immediately below the `<router-outlet>` as we navigate through the application. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### Router Links | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +makeExcerpt('app/app.component.1.ts', 'template-v2') | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | block routerLink | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |     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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Refresh the browser.  We see only the app title and heroes link. We don't see the heroes list. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | .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. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   We click the *Heroes* navigation link, the browser bar updates to `/heroes`, | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   and now we see the list of heroes. We are navigating at last! | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   At this stage, our `AppComponent` looks like this. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExample('app/app.component.1.ts', 'v2', 'app/app.component.ts (v2)') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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* | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/dashboard.component.1.ts (v1)', '') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   We’ll come back and make it more useful later. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### Configure the dashboard route | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Go back to `!{_appRoutingTsVsAppComp}` and teach it to navigate to the dashboard. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Import the dashboard component and | 
					
						
							|  |  |  |  |   add the following route definition to the `!{_RoutesVsAtRouteConfig}` !{_array} of definitions. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | - var _file = _docsFor == 'dart' ? 'lib/app_component.dart' : 'app/app.module.3.ts' | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +makeExcerpt(_file + ' (Dashboard route)', 'dashboard') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +ifDocsFor('ts|js') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     Also import and add `DashboardComponent` to our `AppModule`'s `declarations`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   +makeExcerpt('app/app.module.ts', 'dashboard') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   #### !{_redirectTo} | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |     We can use a redirect route to make this happen. Add the following | 
					
						
							|  |  |  |  |     to our array of route definitions: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   +makeExcerpt('app/app.module.3.ts','redirect') | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   .l-sub-section | 
					
						
							|  |  |  |  |     :marked | 
					
						
							| 
									
										
										
										
											2016-11-15 15:36:34 -08:00
										 |  |  |  |       Learn about the *redirects* in the [Routing](../guide/router.html#redirect) chapter. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   #### Add navigation to the template | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   Finally, add a dashboard navigation link to the template, just above the *Heroes* link. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | - var _vers = _docsFor == 'dart' ? '' : '.1' | 
					
						
							|  |  |  |  | +makeExcerpt('app/app.component' + _vers + '.ts', 'template-v3') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     We nested the two links within `<nav>` tags. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |     They don't do anything yet but they'll be convenient when we style the links a little later in the chapter. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ## Dashboard Top Heroes | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +ifDocsFor('ts|js') | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Set the `moduleId` property to `module.id` for module-relative loading of the `templateUrl`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +makeExcerpt('app/dashboard.component.ts', 'metadata') | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | block templateUrl-path-resolution | 
					
						
							|  |  |  |  |   //- N/A for TS | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Create that file with this content: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +makeExample('app/dashboard.component.1.html', '', 'app/dashboard.component.html') | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### Share the *HeroService* | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   We'd like to re-use the `HeroService` to populate the component's `heroes` !{_array}. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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}`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   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 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Open <span ngio-ex>dashboard.component.ts</span> and add the requisite `import` statements. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +makeExcerpt('app/dashboard.component.ts','imports') | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   Now implement the `DashboardComponent` class like this: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +makeExcerpt('app/dashboard.component.ts (class)', 'class') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   In this dashboard we cherry-pick four heroes (2nd, 3rd, 4th, and 5th)<span if-docs="ts"> with the `Array.slice` method</span>. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   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: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Adding a hero-detail route seems like an obvious place to start. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### Routing to a hero detail | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   We'll add a route to the `HeroDetailComponent` in `!{_appRoutingTsVsAppComp}` where our other routes are configured. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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"). | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   <my-hero-detail [hero]="selectedHero"></my-hero-detail> | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   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: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | code-example(format=''). | 
					
						
							|  |  |  |  |   /detail/11 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | - var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app.module.3.ts' | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +makeExcerpt(_file + ' (hero detail)','hero-detail') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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`. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-26 15:48:47 -07:00
										 |  |  |  | +ifDocsFor('dart') | 
					
						
							|  |  |  |  |   .l-sub-section | 
					
						
							|  |  |  |  |     :marked | 
					
						
							|  |  |  |  |       Remember to import the hero detail component before creating this route. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :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: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | +makeExample('toh-4/ts/app/hero-detail.component.ts', null, 'app/hero-detail.component.ts (current)') | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   The template won't change. We'll display a hero the same way. | 
					
						
							|  |  |  |  |   The big changes are driven by how we get the hero. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 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`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Let's have the `!{_ActivatedRoute}` service, the `HeroService` and the `Location` service injected | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   into the constructor, saving their values in private fields: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/hero-detail.component.ts (constructor)', 'ctor') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   We tell the class that we want to implement the `OnInit` interface. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/hero-detail.component.ts', 'implement', '')(format=".") | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | block ngOnInit | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Inside the `ngOnInit` lifecycle hook, we use the `params` observable to | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     extract the `id` parameter value from the `ActivatedRoute` service | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |     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' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   The hero `id` is a number. Route parameters are *always strings*. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   So we convert the route parameter value to a number with the !{_str2int}. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### Add *HeroService.getHero* | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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`: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/hero.service.ts', 'getHero') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Let's return to the `HeroDetailComponent` to clean up loose ends. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### 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. | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   We'll add a third option, a `goBack` method that navigates backward one step in the browser's history stack | 
					
						
							|  |  |  |  |   using the `Location` service we injected previously. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +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' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   :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). | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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', '') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Modifying the template to add this button spurs us to take one more | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   We update the component metadata with a <span if-docs="ts">`moduleId` and a </span>`templateUrl` pointing to the template file that we just created. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +makeExcerpt('app/hero-detail.component.ts', 'metadata') | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Refresh the browser and see the results. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | .l-main-section | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Select a *Dashboard* Hero | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   When a user selects a hero in the dashboard, the app should navigate to the `HeroDetailComponent` to view and edit the selected hero. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Although the dashboard heroes are presented as button-like blocks, they should behave like anchor tags. | 
					
						
							|  |  |  |  |   When hovering over a hero block, the target URL should display in the browser status bar  | 
					
						
							|  |  |  |  |   and the user should be able to copy the link or open the hero detail view in a new tab. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   To achieve this effect, reopen the `dashboard.component.html` and replace the repeated `<div *ngFor...>` tags | 
					
						
							|  |  |  |  |   with `<a>` tags. The opening `<a>` tag looks like this: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +makeExample('app/dashboard.component.html', 'click', 'app/dashboard.component.html (repeated <a> tag)') | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +ifDocsFor('dart') | 
					
						
							|  |  |  |  |   .alert.is-important | 
					
						
							|  |  |  |  |     :marked | 
					
						
							|  |  |  |  |       Router links in the dashboard are currently not operational, as reported in issue | 
					
						
							|  |  |  |  |       [dart-lang/angular2/issues/186](https://github.com/dart-lang/angular2/issues/186). | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | - var _pathVsName = _docsFor == 'dart' ? 'name' : 'path' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Notice the `[routerLink]` binding. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Top level navigation in the [`AppComponent` | 
					
						
							|  |  |  |  |   template](#router-links) has router links set to fixed !{_pathVsName}s of the | 
					
						
							|  |  |  |  |   destination routes, "/dashboard" and "/heroes". | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   This time, we're binding to an expression containing a **link parameters !{_array}**. | 
					
						
							|  |  |  |  |   The !{_array} has two elements, the ***!{_pathVsName}*** of | 
					
						
							|  |  |  |  |   the destination route and a ***route parameter*** set to the value of the current hero's `id`. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | - var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app.module.3.ts' | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +makeExcerpt(_file + ' (hero detail)', 'hero-detail') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Refresh the browser and select a hero from the dashboard; the app should navigate directly to that hero’s details. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +ifDocsFor('ts') | 
					
						
							|  |  |  |  |   .l-main-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     ## Refactor routes to a _Routing Module_ | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     Almost 20 lines of `AppModule` are devoted to configuring four routes.  | 
					
						
							|  |  |  |  |     Most applications have many more routes and they [add guard services](../guide/router.html#guards)  | 
					
						
							|  |  |  |  |     to protect against unwanted or unauthorized navigations.  | 
					
						
							|  |  |  |  |     Routing considerations could quickly dominate this module and obscure its primary purpose which is to  | 
					
						
							|  |  |  |  |     establish key facts about the entire app for the Angular compiler. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     We should refactor the routing configuration into its own class. | 
					
						
							|  |  |  |  |     What kind of class?  | 
					
						
							|  |  |  |  |     The current `RouterModule.forRoot()` produces an Angular `ModuleWithProviders` which suggests that a | 
					
						
							|  |  |  |  |     class dedicated to routing should be some kind of module. | 
					
						
							|  |  |  |  |     It should be a [_Routing Module_](../guide/router.html#routing-module). | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     By convention the name of a _Routing Module_ contains the word "Routing" and  | 
					
						
							|  |  |  |  |     aligns with the name of the module that declares the components navigated to. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Create an `app-routing.module.ts` file as a sibling to `app.module.ts`. Give it the following contents extracted from the `AppModule` class: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   +makeExample('app/app-routing.module.ts') | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Noteworthy points, typical of _Routing Modules_: | 
					
						
							|  |  |  |  |     * Pull the routes into a variable. You might export it in future and it clarifies the _Routing Module_ pattern. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     * Add `RouterModule.forRoot(routes)` to `imports`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     * Add `RouterModule` to `exports` so that the components in the companion module have access to Router declarables  | 
					
						
							|  |  |  |  |     such as `RouterLink` and `RouterOutlet`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     * No `declarations`!  Declarations are the responsibility of the companion module. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     * Add module `providers` for guard services if you have them; there are none in this example. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     ### Update _AppModule_ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Now delete the routing configuration from `AppModule` and import the `AppRoutingModule`  | 
					
						
							|  |  |  |  |     (_both_ with an ES `import` statement _and_ by adding it to the `NgModule.imports` list). | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Here is the revised `AppModule`, compared to its pre-refactor state: | 
					
						
							|  |  |  |  |   +makeTabs( | 
					
						
							|  |  |  |  |     `toh-5/ts/app/app.module.ts, toh-5/ts/app/app.module.3.ts`, | 
					
						
							|  |  |  |  |      null, | 
					
						
							|  |  |  |  |     `app/app.module.ts (after), app/app.module.ts (before)`) | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     It's simpler and focused on indentifying the key pieces of the application. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | .l-main-section | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Select a Hero in the *HeroesComponent* | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Earlier we added the ability to select a hero from the dashboard. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   We'll do something similar in the `HeroesComponent`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   The `HeroesComponent` template exhibits a "master/detail" style with the list of heroes | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   at the top and details of the selected hero below. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | +makeExample('toh-4/ts/app/app.component.ts','template', 'app/heroes.component.ts (current template)')(format=".") | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   Our goal is to move the detail to its own view and navigate to it when the user decides to edit a selected hero. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   Delete the `<h1>` at the top (we forgot about it during the `AppComponent`-to-`HeroesComponent` conversion). | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   We'll throw in a small twist for variety. | 
					
						
							|  |  |  |  |   We are keeping the "master/detail" style but shrinking the detail to a "mini", read-only version. | 
					
						
							|  |  |  |  |   When the user selects a hero from the list, we *don't* go to the detail page. | 
					
						
							|  |  |  |  |   We show a *mini-detail* on *this* page instead and make the user click a button to navigate to the *full detail *page. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### Add the *mini-detail* | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   Add the following HTML fragment at the bottom of the template where the `<my-hero-detail>` used to be: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/heroes.component.html', 'mini-detail', '') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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") | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   ### Format with the *uppercase* pipe | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Notice that the hero's name is displayed in CAPITAL LETTERS. That's the effect of the `uppercase` pipe | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   that we slipped into the interpolation binding. Look for it right after the pipe operator ( | ). | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/heroes.component.html', 'pipe', '') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							|  |  |  |  |   :marked | 
					
						
							|  |  |  |  |     Learn about pipes in the [Pipes](../guide/pipes.html) chapter. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   ### Move content out of the component file | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   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. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files. | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   <li if-docs="ts">. *Set* the `moduleId` property to `module.id` so that `templateUrl` and `styleUrls` are relative to the component.</li> | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | .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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | block heroes-component-cleanup | 
					
						
							|  |  |  |  |   //- Only relevant for Dart. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/heroes.component.ts (revised metadata)', 'metadata') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   ### Update the _HeroesComponent_ class. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   The `HeroesComponent` navigates to the `HeroesDetailComponent` in response to a button click.  | 
					
						
							|  |  |  |  |   The button's _click_ event is bound to a `gotoDetail` method that navigates _imperatively_ | 
					
						
							|  |  |  |  |   by telling the router where to go. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   This approach requires some changes to the component class: | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   1. Import the `router` from the Angular router library | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   1. Inject the `router` in the constructor (along with the `HeroService`) | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   1. Implement `gotoDetail` by calling the `router.navigate` method | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | +makeExcerpt('app/heroes.component.ts', 'gotoDetail') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   Note that we're passing a two-element **link parameters !{_array}** | 
					
						
							|  |  |  |  |   — a path and the route parameter — to | 
					
						
							|  |  |  |  |   the `router.navigate` method just as we did in the `[routerLink]` binding | 
					
						
							|  |  |  |  |   back in the `DashboardComponent`. | 
					
						
							|  |  |  |  |   Here's the fully revised `HeroesComponent` class: | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/heroes.component.ts', 'class') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :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 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   The app is functional but pretty ugly. | 
					
						
							|  |  |  |  |   Our creative designer team provided some CSS files to make it look better. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### A Dashboard with Style | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   ### Stylish Hero Details | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   The designers also gave us CSS styles specifically for the `HeroDetailComponent`. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   Here's the content for the aforementioned component CSS files. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 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`) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   ### Style the Navigation Links | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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)', '') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | .l-sub-section | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   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') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   Set the `AppComponent`’s `styleUrls` property to this CSS file. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('app/app.component.ts','styleUrls') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							|  |  |  |  |   ### Global application styles | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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 | 
					
						
							| 
									
										
										
										
											2016-11-15 15:36:34 -08:00
										 |  |  |  |   [QuickStart, "Add some style"](../quickstart.html#add-some-style)). | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Here is an excerpt: | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | +makeExcerpt('styles.css (excerpt)', 'toh') | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  | - var styles_css = 'https://raw.githubusercontent.com/angular/angular.io/master/public/docs/_examples/_boilerplate/styles.css' | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Create the file <span ngio-ex>styles.css</span>, if it doesn't exist already. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   Ensure that it contains the [master styles given here](!{styles_css}). | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   If necessary, also edit <span ngio-ex>index.html</span> to refer to this stylesheet. | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | +makeExcerpt('index.html (link ref)', 'css') | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | :marked | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   Look at the app now. Our dashboard, heroes, and navigation links are styling! | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 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: | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | block file-tree-end | 
					
						
							|  |  |  |  |   .filetree | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |     .file angular-tour-of-heroes | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |     .children | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |       .file app | 
					
						
							|  |  |  |  |       .children | 
					
						
							|  |  |  |  |         .file app.component.css | 
					
						
							|  |  |  |  |         .file app.component.ts | 
					
						
							|  |  |  |  |         .file app.module.ts | 
					
						
							| 
									
										
										
										
											2016-10-05 16:59:09 -05:00
										 |  |  |  |         .file app-routing.module.ts | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |         .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 index.html | 
					
						
							|  |  |  |  |       .file package.json | 
					
						
							|  |  |  |  |       .file styles.css | 
					
						
							|  |  |  |  |       .file systemjs.config.js | 
					
						
							|  |  |  |  |       .file tsconfig.json | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | .l-main-section | 
					
						
							|  |  |  |  | :marked | 
					
						
							|  |  |  |  |   ## Recap | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### The Road Behind | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   We travelled a great distance in this chapter | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   - We added the Angular *Router* to navigate among different components. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   - We learned how to create router links to represent navigation menu items. | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  |   - We used router link parameters to navigate to the details of user selected hero. | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   - 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. | 
					
						
							| 
									
										
										
										
											2016-11-08 14:48:03 -08:00
										 |  |  |  |   <li if-docs="ts"> We refactored routes into a `Routing Module` that we import.</li> | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   ### The Road Ahead | 
					
						
							| 
									
										
										
										
											2016-08-17 13:31:40 -07:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 13:39:17 -07:00
										 |  |  |  |   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. |