2016-08-17 13:31:40 -07:00
|
|
|
|
- var _example = 'toh-5';
|
|
|
|
|
|
|
|
|
|
block includes
|
|
|
|
|
include ../_util-fns
|
2016-10-05 16:59:09 -05:00
|
|
|
|
- var _appRoutingTsVsAppComp = 'app.module.ts'
|
2016-08-17 13:31:40 -07:00
|
|
|
|
- var _RoutesVsAtRouteConfig = 'Routes'
|
|
|
|
|
- var _RouterModuleVsRouterDirectives = 'RouterModule'
|
|
|
|
|
- var _redirectTo = 'redirectTo'
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
There are new requirements for the Tour of Heroes app:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2016-06-10 09:37:33 -07:00
|
|
|
|
* Add a *Dashboard* view.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
* Add the ability to navigate between the *Heroes* and *Dashboard* views.
|
|
|
|
|
* When users click a hero name in either view, they should navigate to a detail view of the selected hero.
|
|
|
|
|
* When users click a *deep link* in an email, the detail view for a particular hero should open.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
When you’re done, users will be able to navigate the app like this:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
figure.image-display
|
|
|
|
|
img(src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations")
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
To satisfy these requirements, you'll add Angular’s router to the app.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
For more information about the router, read the [Routing and Navigation](../guide/router.html) page.
|
2016-06-07 15:51:25 -07:00
|
|
|
|
|
2016-07-03 17:11:17 -07:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
When you're done with this page, the app should look like this <live-example></live-example>.
|
2016-06-07 15:51:25 -07:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
+ifDocsFor('ts|js')
|
2016-11-28 21:23:28 -08:00
|
|
|
|
include ../../../_includes/_see-addr-bar
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Where you left off
|
|
|
|
|
Before continuing with the Tour of Heroes, verify that you have the following structure.
|
|
|
|
|
If not, go back to the previous pages.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
block intro-file-tree
|
|
|
|
|
.filetree
|
2016-09-20 05:24:40 +02:00
|
|
|
|
.file angular-tour-of-heroes
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.children
|
2017-02-02 18:38:17 +00:00
|
|
|
|
.file src
|
2016-08-17 13:31:40 -07:00
|
|
|
|
.children
|
2017-02-02 18:38:17 +00: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 mock-heroes.ts
|
2016-08-17 13:31:40 -07:00
|
|
|
|
.file main.ts
|
2017-02-02 18:38:17 +00:00
|
|
|
|
.file index.html
|
|
|
|
|
.file styles.css
|
|
|
|
|
.file systemjs.config.js
|
|
|
|
|
.file tsconfig.json
|
2016-08-17 13:31:40 -07:00
|
|
|
|
.file node_modules ...
|
|
|
|
|
.file package.json
|
|
|
|
|
|
|
|
|
|
block keep-app-running
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Keep the app transpiling and running
|
|
|
|
|
Enter the following command in the terminal window:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
code-example(language="sh" class="code-shell").
|
|
|
|
|
npm start
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
This command runs the TypeScript compiler in "watch mode", recompiling automatically when the code changes.
|
|
|
|
|
The command simultaneously launches the app in a browser and refreshes the browser when the code changes.
|
|
|
|
|
|
|
|
|
|
You can keep building the Tour of Heroes without pausing to recompile or refresh the browser.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
## Action plan
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Here's the plan:
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
* 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.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
*Routing* is another name for *navigation*. The router is the mechanism for navigating from view to view.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
|
|
|
|
.l-main-section
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
|
|
|
|
## Splitting the *AppComponent*
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The current app loads `AppComponent` and immediately displays the list of heroes.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The revised app should present a shell with a choice of views (*Dashboard* and *Heroes*)
|
2016-08-17 13:31:40 -07:00
|
|
|
|
and then default to one of them.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The `AppComponent` should only handle navigation, so you'll
|
|
|
|
|
move the display of *Heroes* out of `AppComponent` and into its own `HeroesComponent`.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
### *HeroesComponent*
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
`AppComponent` is already dedicated to *Heroes*.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Instead of moving the code out of `AppComponent`, rename it `HeroesComponent`
|
|
|
|
|
and create a separate `AppComponent` shell.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Do the following:
|
|
|
|
|
* Rename the <span ngio-ex>app.component.ts</span> file as <span ngio-ex>heroes.component.ts</span>.
|
|
|
|
|
* Rename the `AppComponent` class as `HeroesComponent` (rename locally, _only_ in this file).
|
|
|
|
|
* Rename the selector `my-app` as `my-heroes`.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/heroes.component.ts (showing renamings only)', 'renaming')
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Create *AppComponent*
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The new `AppComponent` is the application shell.
|
|
|
|
|
It will have some navigation links at the top and a display area below for the pages users navigate to.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Perform these steps:
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
* Create the file <span ngio-ex>src/app/app.component.ts</span>.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
* 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`:
|
2017-03-21 11:08:09 -07:00
|
|
|
|
* `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 you still see the heroes.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
* Add `HeroesComponent` to the `!{_declsVsDirectives}` !{_array} of `!{_AppModuleVsAppComp}` so Angular recognizes the `<my-heroes>` tags.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
* Add `HeroService` to the `providers` !{_array} of `!{_AppModuleVsAppComp}` because you'll need it in every other view.
|
|
|
|
|
* Remove `HeroService` from the `HeroesComponent` `providers` !{_array} since it was promoted.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
* Add the supporting `import` statements for `AppComponent`.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The first draft looks like this:
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
block app-comp-v1
|
|
|
|
|
+makeTabs(
|
2017-02-02 18:38:17 +00:00
|
|
|
|
`toh-5/ts/src/app/app.component.1.ts,
|
|
|
|
|
toh-5/ts/src/app/app.module.1.ts`,
|
2016-08-17 13:31:40 -07:00
|
|
|
|
',',
|
2017-02-02 18:38:17 +00:00
|
|
|
|
`src/app/app.component.ts (v1),
|
|
|
|
|
src/app/app.module.ts (v1)`)
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The app still runs and displays heroes.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Adding routing
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Instead of displaying automatically, heroes should display after users click a button.
|
|
|
|
|
In other words, users should be able to navigate to the list of heroes.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Use the Angular router to enable navigation.
|
2016-06-19 00:20:38 -04: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`),
|
2017-03-21 11:08:09 -07:00
|
|
|
|
and a configuration (`Routes`). You'll configure the routes first.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-02-21 08:13:09 -08:00
|
|
|
|
### *<base href>*
|
|
|
|
|
|
|
|
|
|
Open `index.html` and ensure there is a `<base href="...">` element
|
|
|
|
|
(or a script that dynamically sets this element)
|
|
|
|
|
at the top of the `<head>` section.
|
|
|
|
|
|
|
|
|
|
+makeExcerpt('src/index.html', 'base-href')
|
|
|
|
|
|
|
|
|
|
.callout.is-important
|
|
|
|
|
header base href is essential
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
For more information, see the [Set the base href](../guide/router.html#!#base-href)
|
|
|
|
|
section of the [Routing and Navigation](../guide/router.html) page.
|
|
|
|
|
|
2017-02-21 08:13:09 -08:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
a#configure-routes
|
|
|
|
|
block router-config-intro
|
|
|
|
|
:marked
|
|
|
|
|
### Configure routes
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Create a configuration file for the app routes.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
:marked
|
2016-06-19 00:20:38 -04:00
|
|
|
|
*Routes* tell the router which views to display when a user clicks a link or
|
2015-12-23 09:42:57 -08:00
|
|
|
|
pastes a URL into the browser address bar.
|
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Define the first route as a route to the heroes component.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2016-10-05 16:59:09 -05:00
|
|
|
|
- var _file = _docsFor == 'dart' ? 'app.component.ts' : 'app.module.2.ts'
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/' + _file + ' (heroes route)', 'heroes')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
- var _are = _docsFor == 'dart' ? 'takes' : 'are'
|
|
|
|
|
- var _routePathPrefix = _docsFor == 'dart' ? '/' : ''
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2016-08-17 13:31:40 -07:00
|
|
|
|
The `!{_RoutesVsAtRouteConfig}` !{_are} !{_an} !{_array} of *route definitions*.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
This route definition has the following parts:
|
|
|
|
|
|
|
|
|
|
- *Path*: The router matches this route's path to the URL in the browser address bar (`!{_routePathPrefix}heroes`).
|
|
|
|
|
<li if-docs="dart"> *Name*: The official name of the route;
|
|
|
|
|
it must begin with a capital letter to avoid confusion with the path (`Heroes`).</li>
|
|
|
|
|
- *Component*: The component that the router should create when navigating to this route (`HeroesComponent`).
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Read more about defining routes with `!{_RoutesVsAtRouteConfig}` in the [Routing & Navigation](../guide/router.html) page.
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
+ifDocsFor('ts|js')
|
|
|
|
|
:marked
|
|
|
|
|
### Make the router available
|
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Add the initial route configuration, `RouterModule`, to the `AppModule` imports !{_array}.
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.module.2.ts (app routing)', '')
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2016-10-05 16:59:09 -05:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The `forRoot` method is called because a configured router is provided at the app's root.
|
|
|
|
|
The `forRoot` method supplies the Router service providers and directives needed for routing, and
|
2016-10-05 16:59:09 -05:00
|
|
|
|
performs the initial navigation based on the current browser URL.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
- var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes'
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Router outlet
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
If you paste the path, `/heroes`, into the browser address bar at the end of the URL,
|
2016-08-17 13:31:40 -07:00
|
|
|
|
the router should match it to the `!{_heroesRoute}` route and display the `HeroesComponent`.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
However, you have to tell the router where to display the component.
|
|
|
|
|
To do this, you can add a `<router-outlet>` element at the end of the template.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
`RouterOutlet` is one of the <span if-docs="ts">directives provided by</span> the `!{_RouterModuleVsRouterDirectives}`.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The router displays each component immediately below the `<router-outlet>` as users navigate through the app.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Router links
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Users shouldn't have to paste a route URL into the address bar.
|
|
|
|
|
Instead, add an anchor tag to the template that, when clicked, triggers navigation to the `HeroesComponent`.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
The revised template looks like this:
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.component.1.ts', 'template-v2')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
block routerLink
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Note the `routerLink` binding in the anchor tag.
|
|
|
|
|
The `RouterLink` directive (another of the `RouterModule` directives) is bound to a string
|
2016-08-17 13:31:40 -07:00
|
|
|
|
that tells the router where to navigate when the user clicks the link.
|
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Since the link is not dynamic, a routing instruction is defined with a one-time binding to the route path.
|
|
|
|
|
Looking back at the route configuration, you can confirm that `'/heroes'` is the path of the route to the `HeroesComponent`.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Read more about dynamic router links and the link parameters array
|
|
|
|
|
in the [Appendix: Link Parameters Array](../guide/router.html#link-parameters-array) section of the
|
|
|
|
|
[Routing & Navigation](../guide/router.html#) page.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Refresh the browser. The app title and heroes link, but not the heroes list, display.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2016-06-19 00:20:38 -04:00
|
|
|
|
The browser's address bar shows `/`.
|
|
|
|
|
The route path to `HeroesComponent` is `/heroes`, not `/`.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Soon you'll add a route that matches the path `/`.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Click the *Heroes* navigation link. The browser bar updates to `/heroes`
|
|
|
|
|
and the list of heroes displays.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
`AppComponent` now looks like this:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExample('src/app/app.component.1.ts', 'v2', 'src/app/app.component.ts (v2)')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The *AppComponent* is now attached to a router and displays routed views.
|
|
|
|
|
For this reason, and to distinguish it from other kinds of components,
|
|
|
|
|
this component type is called a *router component*.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Add a dashboard
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Routing only makes sense when multiple views exist.
|
|
|
|
|
To add another view, create a placeholder `DashboardComponent`, which users can navigate to and from.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/dashboard.component.1.ts (v1)', '')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You'll make this component more useful later.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
### Configure the dashboard route
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
To teach `!{_appRoutingTsVsAppComp}` to navigate to the dashboard,
|
|
|
|
|
import the dashboard component and
|
2016-08-17 13:31:40 -07:00
|
|
|
|
add the following route definition to the `!{_RoutesVsAtRouteConfig}` !{_array} of definitions.
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
- var _file = _docsFor == 'dart' ? 'lib/app_component.dart' : 'src/app/app.module.3.ts'
|
2016-08-17 13:31:40 -07:00
|
|
|
|
+makeExcerpt(_file + ' (Dashboard route)', 'dashboard')
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
+ifDocsFor('ts|js')
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Also import and add `DashboardComponent` to the `AppModule`'s `declarations`.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.module.ts', 'dashboard')
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Add a redirect route
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
When the app starts, it should show the dashboard and
|
|
|
|
|
display a `/dashboard` URL in the browser address bar.
|
|
|
|
|
Currently, the browser launches with `/` in the address bar.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
block redirect-vs-use-as-default
|
2016-06-19 00:20:38 -04:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
To make this happen, use a redirect route. Add the following
|
|
|
|
|
to the array of route definitions:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.module.3.ts','redirect')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Read more about *redirects* in the [Redirecting routes](../guide/router.html#!#redirect) section
|
|
|
|
|
of the [Routing & Navigation](../guide/router.html#) page.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Add navigation to the template
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Add a dashboard navigation link to the template, just above the *Heroes* link.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
- var _vers = _docsFor == 'dart' ? '' : '.1'
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.component' + _vers + '.ts', 'template-v3')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The `<nav>` tags don't do anything yet, but they'll be useful later when you style the links.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
In your browser, go to the application root (`/`) and reload.
|
|
|
|
|
The app displays the dashboard and you can navigate between the dashboard and the heroes.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Add heroes to the dashboard
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
To make the dashboard more interesting, you'll display the top four heroes at a glance.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
Replace the `template` metadata with a `templateUrl` property that points to a new
|
2016-03-08 13:45:29 +01:00
|
|
|
|
template file.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/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-03-08 13:45:29 +01:00
|
|
|
|
:marked
|
2016-08-17 13:31:40 -07:00
|
|
|
|
Create that file with this content:
|
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExample('src/app/dashboard.component.1.html', '', 'src/app/dashboard.component.html')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
`*ngFor` is used again to iterate over a list of heroes and display their names.
|
|
|
|
|
The extra `<div>` elements will help with styling later in this page.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Sharing the *HeroService*
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
To populate the component's `heroes` !{_array}, you can re-use the `HeroService`.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Earlier in the page, you removed the `HeroService` from the `providers` !{_array} of `HeroesComponent`
|
2016-08-17 13:31:40 -07:00
|
|
|
|
and added it to the `providers` !{_array} of `!{_AppModuleVsAppComp}`.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
That move created a singleton `HeroService` instance, available to all components of the app.
|
|
|
|
|
Angular injects `HeroService` and you can use it in the `DashboardComponent`.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
### Get heroes
|
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
In <span ngio-ex>dashboard.component.ts</span>, add the following `import` statements.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/dashboard.component.ts','imports')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
:marked
|
2015-12-23 09:42:57 -08:00
|
|
|
|
Now implement the `DashboardComponent` class like this:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/dashboard.component.ts (class)', 'class')
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
This kind of logic is also used in the `HeroesComponent`:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
* 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-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
In this dashboard you specify four heroes (2nd, 3rd, 4th, and 5th)<span if-docs="ts"> with the `Array.slice` method</span>.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Refresh the browser to see four hero names in the new dashboard.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2016-06-19 00:20:38 -04:00
|
|
|
|
.l-main-section
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Navigating to hero details
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
While the details of a selected hero displays at the bottom of the `HeroesComponent`,
|
|
|
|
|
users should be able to navigate to the `HeroDetailComponent` in the following additional ways:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
* From the dashboard to a selected hero.
|
|
|
|
|
* From the heroes list to a selected hero.
|
|
|
|
|
* From a "deep link" URL pasted into the browser address bar.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
### Routing to a hero detail
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You can add a route to the `HeroDetailComponent` in `!{_appRoutingTsVsAppComp}`, where the other routes are configured.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The new route is unusual in that you must tell the `HeroDetailComponent` which hero to show.
|
|
|
|
|
You didn't have to tell the `HeroesComponent` or the `DashboardComponent` anything.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Currently, the parent `HeroesComponent` sets the component's `hero` property to a
|
|
|
|
|
hero object with a binding like this:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
code-example(language="html").
|
2017-03-21 11:08:09 -07:00
|
|
|
|
<hero-detail [hero]="selectedHero"></hero-detail>
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
But this binding won't work in any of the routing scenarios.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
### Parameterized route
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You can add the hero's `id` to the URL. When routing to the hero whose `id` is 11,
|
|
|
|
|
you could expect to see a URL such as this:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2016-12-01 08:15:04 -08:00
|
|
|
|
code-example(format="nocode").
|
2015-12-23 09:42:57 -08:00
|
|
|
|
/detail/11
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The `/detail/` part of the URL is constant. The trailing numeric `id` changes from hero to hero.
|
|
|
|
|
You need to represent the variable part of the route with a *parameter* (or *token*) that stands for the hero's `id`.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Configure a route with a parameter
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Use the following *route definition*.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
- var _file = _docsFor == 'dart' ? 'src/app/app.component.ts' : 'src/app/app.module.3.ts'
|
2016-08-17 13:31:40 -07:00
|
|
|
|
+makeExcerpt(_file + ' (hero detail)','hero-detail')
|
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The colon (:) in the path indicates that `:id` is a placeholder for a specific hero `id`
|
2015-12-23 09:42:57 -08:00
|
|
|
|
when navigating to the `HeroDetailComponent`.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2016-08-17 20:28:22 -05:00
|
|
|
|
+ifDocsFor('dart')
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Be sure to import the hero detail component before creating this route.
|
2016-08-09 17:38:25 +01:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You're finished with the app routes.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You didn'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 name*, whether the name displays on the dashboard or in the heroes list.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You don't need to add the hero clicks until the `HeroDetailComponent`
|
|
|
|
|
is revised and ready to be navigated to.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2016-06-19 00:20:38 -04:00
|
|
|
|
.l-main-section
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
|
|
|
|
## Revise the *HeroDetailComponent*
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Here's what the `HeroDetailComponent` looks like now:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExample('toh-4/ts/src/app/hero-detail.component.ts', null, 'src/app/hero-detail.component.ts (current)')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The template won't change. Hero names will display the same way.
|
|
|
|
|
The major changes are driven by how you get hero names.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
block route-params
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You'll no longer receive the hero in a parent component property binding.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
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`.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Add the following imports:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
- var _vers = _docsFor == 'dart' ? '' : '.1'
|
2017-03-21 11:08:09 -07:00
|
|
|
|
|
|
|
|
|
+makeExcerpt('src/app/hero-detail.component' + _vers + '.ts', 'added-imports', 'src/app/hero-detail.component')
|
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
- var _ActivatedRoute = _docsFor == 'dart' ? 'RouteParams' : 'ActivatedRoute'
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Inject the `!{_ActivatedRoute}`, `HeroService`, and `Location` services
|
2016-08-17 13:31:40 -07:00
|
|
|
|
into the constructor, saving their values in private fields:
|
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/hero-detail.component.ts (constructor)', 'ctor')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2016-11-23 15:42:49 -08:00
|
|
|
|
+ifDocsFor('ts')
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Import the `switchMap` operator to use later with the route parameters `Observable`.
|
2016-10-29 16:08:54 -05:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/hero-detail.component.ts (switchMap import)', 'rxjs-import')
|
2016-10-29 16:08:54 -05:00
|
|
|
|
|
2016-03-15 13:24:50 +01:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Tell the class to implement the `OnInit` interface.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
+makeExcerpt('src/app/hero-detail.component.ts', 'implement', 'src/app/hero-detail.component.ts')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
block ngOnInit
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Inside the `ngOnInit` lifecycle hook, use the `params` observable to
|
2016-09-18 12:34:32 -04: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`.
|
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
+makeExcerpt('src/app/hero-detail.component.ts', 'ngOnInit', 'src/app/hero-detail.component.ts')
|
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
block extract-id
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The `switchMap` operator maps the id in the observable route parameters
|
2016-10-29 16:08:54 -05:00
|
|
|
|
to a new `Observable`, the result of the `HeroService.getHero` method.
|
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
If a user re-navigates to this component while a getHero request is still processing,
|
|
|
|
|
`switchMap` cancels the old request and then calls `HeroService.getHero` again.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
- var _str2int = _docsFor == 'dart' ? '<code>int.parse</code> static method' : 'JavaScript (+) operator'
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The hero `id` is a number. Route parameters are always strings.
|
|
|
|
|
So the route parameter value is converted to a number with the !{_str2int}.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2016-11-23 15:42:49 -08:00
|
|
|
|
+ifDocsFor('ts')
|
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Do you need to unsubscribe?
|
2016-10-29 16:08:54 -05:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
As described in the [ActivatedRoute: the one-stop-shop for route information](../guide/router.html#activated-route)
|
|
|
|
|
section of the [Routing & Navigation](../guide/router.html) page,
|
|
|
|
|
the `Router` manages the observables it provides and localizes
|
2016-11-23 15:42:49 -08:00
|
|
|
|
the subscriptions. The subscriptions are cleaned up when the component is destroyed, protecting against
|
2017-03-21 11:08:09 -07:00
|
|
|
|
memory leaks, so you don't need to unsubscribe from the route params `Observable`.
|
2016-10-29 16:08:54 -05:00
|
|
|
|
|
|
|
|
|
:marked
|
2015-12-23 09:42:57 -08:00
|
|
|
|
### Add *HeroService.getHero*
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
In the previous code snippet, `HeroService` doesn't have a `getHero` method. To fix this issue,
|
|
|
|
|
open `HeroService` and add a `getHero` method that filters the heroes list from `getHeroes` by `id`.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/hero.service.ts', 'getHero')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Find the way back
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Users have several ways to navigate *to* the `HeroDetailComponent`.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
To navigate somewhere else, users can click one of the two links in the `AppComponent` or click the browser's back button.
|
|
|
|
|
Now add a third option, a `goBack` method that navigates backward one step in the browser's history stack
|
|
|
|
|
using the `Location` service you injected previously.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/hero-detail.component.ts', 'goBack')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
- var _CanDeactivateGuard = _docsFor == 'dart' ? '<em>routerCanDeactivate</em> hook' : '<em>CanDeactivate</em> guard'
|
|
|
|
|
- var _CanDeactivateGuardUri = _docsFor == 'dart' ? 'angular2.router/CanDeactivate-class' : 'router/index/CanDeactivate-interface'
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.l-sub-section
|
2016-08-17 13:31:40 -07:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Going back too far could take users out of the app.
|
|
|
|
|
In a real app, you can prevent this issue with the !{_CanDeactivateGuard}.
|
|
|
|
|
Read more on the [CanDeactivate](../api/!{_CanDeactivateGuardUri}.html) page.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You'll wire this method with an event binding to a *Back* button that you'll add to the component template.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/hero-detail.component.html', 'back-button', '')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Migrate the template to its own file
|
2016-08-17 13:31:40 -07:00
|
|
|
|
called <span ngio-ex>hero-detail.component.html</span>:
|
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExample('src/app/hero-detail.component.html')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Update the component metadata with a `templateUrl` pointing to the template file that you just created.
|
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/hero-detail.component.ts', 'metadata')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2016-08-17 13:31:40 -07:00
|
|
|
|
Refresh the browser and see the results.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Select a dashboard hero
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2016-06-10 09:37:33 -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-06-19 00:20:38 -04:00
|
|
|
|
|
2016-11-03 10:25:01 -07:00
|
|
|
|
Although the dashboard heroes are presented as button-like blocks, they should behave like anchor tags.
|
2017-03-15 03:54:03 -07:00
|
|
|
|
When hovering over a hero block, the target URL should display in the browser status bar
|
2016-11-03 10:25:01 -07:00
|
|
|
|
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
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
To achieve this effect, reopen `dashboard.component.html` and replace the repeated `<div *ngFor...>` tags
|
|
|
|
|
with `<a>` tags. Change the opening `<a>` tag to the following:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExample('src/app/dashboard.component.html', 'click', 'src/app/dashboard.component.html (repeated <a> tag)')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
- var _pathVsName = _docsFor == 'dart' ? 'name' : 'path'
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2016-11-03 10:25:01 -07:00
|
|
|
|
Notice the `[routerLink]` binding.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
As described in the [Router links](#router-links) section of this page,
|
|
|
|
|
top-level navigation in the `AppComponent` template has router links set to fixed !{_pathVsName}s of the
|
2016-11-03 10:25:01 -07:00
|
|
|
|
destination routes, "/dashboard" and "/heroes".
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
This time, you'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-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The two !{_array} items align with the *!{_pathVsName}* and *:id*
|
|
|
|
|
token in the parameterized hero detail route definition that you added to
|
|
|
|
|
`!{_appRoutingTsVsAppComp}` earlier:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
- var _file = _docsFor == 'dart' ? 'src/app/app.component.ts' : 'src/app/app.module.3.ts'
|
2016-08-17 13:31:40 -07:00
|
|
|
|
+makeExcerpt(_file + ' (hero detail)', 'hero-detail')
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Refresh the browser and select a hero from the dashboard; the app navigates to that hero’s details.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2016-11-08 14:48:03 -08:00
|
|
|
|
+ifDocsFor('ts')
|
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
|
|
|
|
## Refactor routes to a _Routing Module_
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
2017-03-15 03:54:03 -07:00
|
|
|
|
Almost 20 lines of `AppModule` are devoted to configuring four routes.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Most applications have many more routes and they add guard services
|
2017-03-15 03:54:03 -07:00
|
|
|
|
to protect against unwanted or unauthorized navigations.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
(Read more about guard services in the [Route Guards](../guide/router.html#guards)
|
|
|
|
|
section of the [Routing & Navigation](../guide/router.html) page.)
|
|
|
|
|
Routing considerations could quickly dominate this module and obscure its primary purpose, which is to
|
2016-11-08 14:48:03 -08:00
|
|
|
|
establish key facts about the entire app for the Angular compiler.
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
It's a good idea to refactor the routing configuration into its own class.
|
|
|
|
|
The current `RouterModule.forRoot()` produces an Angular `ModuleWithProviders`.
|
|
|
|
|
A class dedicated to routing should be a *routing module*.
|
|
|
|
|
For more information, see the [Milestone #2: The Routing Module](../guide/router.html#routing-module)
|
|
|
|
|
section of the [Routing & Navigation](../guide/router.html) page.
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
By convention, a routing module name contains the word "Routing" and
|
2016-11-08 14:48:03 -08:00
|
|
|
|
aligns with the name of the module that declares the components navigated to.
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Create an `app-routing.module.ts` file as a sibling to `app.module.ts`.
|
|
|
|
|
Give it the following contents, extracted from the `AppModule` class:
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExample('src/app/app-routing.module.ts')
|
2016-11-08 14:48:03 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The following points are typical of routing modules:
|
|
|
|
|
* The Routing Module pulls the routes into a variable. The variable clarifies the
|
|
|
|
|
routing module pattern in case you export the module in the future.
|
|
|
|
|
* The Routing Module adds `RouterModule.forRoot(routes)` to `imports`.
|
|
|
|
|
* The Routing Module adds `RouterModule` to `exports` so that the
|
|
|
|
|
components in the companion module have access to Router declarables,
|
2016-11-08 14:48:03 -08:00
|
|
|
|
such as `RouterLink` and `RouterOutlet`.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
* There are no `declarations`. Declarations are the responsibility of the companion module.
|
|
|
|
|
* If you have guard services, the Routing Module adds module `providers`. (There are none in this example.)
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Update *AppModule*
|
2016-11-08 14:48:03 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
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).
|
2016-11-08 14:48:03 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
:marked
|
2016-11-08 14:48:03 -08:00
|
|
|
|
Here is the revised `AppModule`, compared to its pre-refactor state:
|
2017-03-21 11:08:09 -07:00
|
|
|
|
|
|
|
|
|
+makeTabs(
|
2017-02-02 18:38:17 +00:00
|
|
|
|
`toh-5/ts/src/app/app.module.ts, toh-5/ts/src/app/app.module.3.ts`,
|
2016-11-08 14:48:03 -08:00
|
|
|
|
null,
|
2017-02-02 18:38:17 +00:00
|
|
|
|
`src/app/app.module.ts (after), src/app/app.module.ts (before)`)
|
2017-03-21 11:08:09 -07:00
|
|
|
|
:marked
|
|
|
|
|
The revised and simplified `AppModule` is focused on identifying the key pieces of the app.
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
|
|
|
|
.l-main-section
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Select a hero in the *HeroesComponent*
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
In the `HeroesComponent`,
|
|
|
|
|
the current template exhibits a "master/detail" style with the list of heroes
|
2015-12-23 09:42:57 -08:00
|
|
|
|
at the top and details of the selected hero below.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExample('toh-4/ts/src/app/app.component.ts','template', 'src/app/heroes.component.ts (current template)')(format=".")
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Delete the `<h1>` at the top.
|
|
|
|
|
|
|
|
|
|
Delete the last line of the template with the `<hero-detail>` tags.
|
2016-10-11 16:30:30 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You'll no longer show the full `HeroDetailComponent` here.
|
|
|
|
|
Instead, you'll display the hero detail on its own page and route to it as you did in the dashboard.
|
2016-10-05 16:59:09 -05:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
However, when users select a hero from the list, they won't go to the detail page.
|
|
|
|
|
Instead, they'll see a mini detail on *this* page and have to click a button to navigate to the *full detail* page.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Add the *mini detail*
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Add the following HTML fragment at the bottom of the template where the `<hero-detail>` used to be:
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08: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
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
+makeExcerpt('src/app/heroes.component.html', 'mini-detail', 'src/app/heroes.component.ts')
|
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
After clicking a hero, users should see something like this below the hero list:
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
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
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Format with the uppercase pipe
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The hero's name is displayed in capital letters because of the `uppercase` pipe
|
|
|
|
|
that's included in the interpolation binding, right after the pipe operator ( | ).
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/heroes.component.html', 'pipe', '')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
|
|
|
|
Pipes are a good way to format strings, currency amounts, dates and other display data.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Angular ships with several pipes and you can write your own.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.l-sub-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Read more about pipes on the [Pipes](../guide/pipes.html) page.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
|
|
|
|
### Move content out of the component file
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You still have to update the component class to support navigation to the
|
|
|
|
|
`HeroDetailComponent` when users click the *View Details* button.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The component file is big.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
It's difficult to find the component logic amidst the noise of HTML and CSS.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Before making any more changes, migrate the template and styles to their own files.
|
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.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files.
|
2016-06-19 00:20:38 -04: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).
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You could list multiple style files from different locations if you needed them.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
block heroes-component-cleanup
|
|
|
|
|
//- Only relevant for Dart.
|
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/heroes.component.ts (revised metadata)', 'metadata')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Update the _HeroesComponent_ class
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
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_
|
2016-11-03 10:25:01 -07:00
|
|
|
|
by telling the router where to go.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
This approach requires the following changes to the component class:
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
1. Import the `router` from the Angular router library.
|
|
|
|
|
1. Inject the `router` in the constructor (along with the `HeroService`).
|
|
|
|
|
1. Implement `gotoDetail` by calling the `router.navigate` method.
|
2016-11-03 10:25:01 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/heroes.component.ts', 'gotoDetail')
|
2016-11-08 14:48:03 -08:00
|
|
|
|
|
2016-11-03 10:25:01 -07:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Note that you're passing a two-element *link parameters !{_array}*—a
|
|
|
|
|
path and the route parameter—to
|
|
|
|
|
the `router.navigate` method, just as you did in the `[routerLink]` binding
|
2016-11-08 14:48:03 -08:00
|
|
|
|
back in the `DashboardComponent`.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Here's the revised `HeroesComponent` class:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/heroes.component.ts', 'class')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2016-06-19 00:20:38 -04:00
|
|
|
|
Refresh the browser and start clicking.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Users can navigate around the app, from the dashboard to hero details and back,
|
|
|
|
|
from heroes list to the mini detail to the hero details and back to the heroes again.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You've met all of the navigational requirements that propelled this page.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## Style the app
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The app is functional but it needs styling.
|
|
|
|
|
The dashboard heroes should display in a row of rectangles.
|
|
|
|
|
You've received around 60 lines of CSS for this purpose, including some simple media queries for responsive design.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Adding the CSS to the component `styles` metadata
|
|
|
|
|
would obscure the component logic.
|
|
|
|
|
Instead, edit the CSS in a separate `*.css` file.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
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:
|
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/dashboard.component.ts (styleUrls)', 'css')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Add stylish hero details
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You've also been provided with CSS styles specifically for the `HeroDetailComponent`.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
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
|
2017-03-21 11:08:09 -07:00
|
|
|
|
the `styleUrls` !{_array} as you did for `DashboardComponent`.
|
|
|
|
|
Also, in hero-detail.component.ts, remove the `hero` property `@Input` !{_decorator}
|
|
|
|
|
<span if-docs="ts">and its import</span>.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Here's the content for the component CSS files.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
|
|
|
|
block css-files
|
|
|
|
|
+makeTabs(
|
2017-02-02 18:38:17 +00:00
|
|
|
|
`toh-5/ts/src/app/hero-detail.component.css,
|
|
|
|
|
toh-5/ts/src/app/dashboard.component.css`,
|
2016-08-17 13:31:40 -07:00
|
|
|
|
null,
|
2017-02-02 18:38:17 +00:00
|
|
|
|
`src/app/hero-detail.component.css,
|
|
|
|
|
src/app/dashboard.component.css`)
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### Style the navigation links
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The provided CSS makes the navigation links in the `AppComponent` look more like selectable buttons.
|
|
|
|
|
You'll surround those links in `<nav>` tags.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Add an <span ngio-ex>app.component.css</span> file to the `!{_appDir}` folder with the following content.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.component.css (navigation styles)', '')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.l-sub-section
|
2016-08-17 13:31:40 -07:00
|
|
|
|
block router-link-active
|
|
|
|
|
:marked
|
|
|
|
|
**The *routerLinkActive* directive**
|
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The Angular router provides a `routerLinkActive` directive you can use to
|
2016-08-17 13:31:40 -07:00
|
|
|
|
add a class to the HTML navigation element whose route matches the active route.
|
2017-03-21 11:08:09 -07:00
|
|
|
|
All you have to do is define the style for it.
|
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.component.ts (active router links)', 'template')
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-15 03:54:03 -07:00
|
|
|
|
:marked
|
|
|
|
|
Add a `styleUrls` property that refers to this CSS file as follows:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/app/app.component.ts','styleUrls')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
|
|
|
|
### Global application styles
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
When you add styles to a component, you keep everything a component needs—HTML,
|
|
|
|
|
the CSS, the code—together in one convenient place.
|
|
|
|
|
It's easy to package it all up and re-use the component somewhere else.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You can also create styles at the *application level* outside of any component.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
The designers provided some basic styles to apply to elements across the entire app.
|
|
|
|
|
These correspond to the full set of master styles that you installed earlier during [setup](../guide/setup.html).
|
|
|
|
|
Here's an excerpt:
|
2016-06-10 09:37:33 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/styles.css (excerpt)', 'toh')
|
2016-06-10 09:37:33 -07:00
|
|
|
|
|
2017-02-06 18:27:52 +00:00
|
|
|
|
- var styles_css = 'https://raw.githubusercontent.com/angular/angular.io/master/public/docs/_examples/_boilerplate/src/styles.css'
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Create the file <span ngio-ex>styles.css</span>.
|
|
|
|
|
Ensure that the file contains the [master styles provided here](!{styles_css}).
|
|
|
|
|
Also edit <span ngio-ex>index.html</span> to refer to this stylesheet.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-02-02 18:38:17 +00:00
|
|
|
|
+makeExcerpt('src/index.html (link ref)', 'css')
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2015-12-23 09:42:57 -08:00
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Look at the app now. The dashboard, heroes, and navigation links are styled.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2015-12-23 09:42:57 -08: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
|
2016-07-03 17:11:17 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Review the sample source code in the <live-example></live-example> for this page.
|
|
|
|
|
Verify that you have the following structure:
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2016-08-17 13:31:40 -07:00
|
|
|
|
block file-tree-end
|
|
|
|
|
.filetree
|
2016-09-20 05:24:40 +02:00
|
|
|
|
.file angular-tour-of-heroes
|
2015-12-23 09:42:57 -08:00
|
|
|
|
.children
|
2017-02-02 18:38:17 +00:00
|
|
|
|
.file src
|
2016-08-17 13:31:40 -07:00
|
|
|
|
.children
|
2017-02-02 18:38:17 +00:00
|
|
|
|
.file app
|
|
|
|
|
.children
|
|
|
|
|
.file app.component.css
|
|
|
|
|
.file app.component.ts
|
|
|
|
|
.file app.module.ts
|
|
|
|
|
.file app-routing.module.ts
|
|
|
|
|
.file dashboard.component.css
|
|
|
|
|
.file dashboard.component.html
|
|
|
|
|
.file dashboard.component.ts
|
|
|
|
|
.file hero.service.ts
|
|
|
|
|
.file hero.ts
|
|
|
|
|
.file hero-detail.component.css
|
|
|
|
|
.file hero-detail.component.html
|
|
|
|
|
.file hero-detail.component.ts
|
|
|
|
|
.file heroes.component.css
|
|
|
|
|
.file heroes.component.html
|
|
|
|
|
.file heroes.component.ts
|
|
|
|
|
.file mock-heroes.ts
|
2016-08-17 13:31:40 -07:00
|
|
|
|
.file main.ts
|
2017-02-02 18:38:17 +00:00
|
|
|
|
.file index.html
|
|
|
|
|
.file styles.css
|
|
|
|
|
.file systemjs.config.js
|
2017-03-15 03:54:03 -07:00
|
|
|
|
.file tsconfig.json
|
2016-08-17 13:31:40 -07:00
|
|
|
|
.file node_modules ...
|
|
|
|
|
.file package.json
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
|
|
|
|
.l-main-section
|
|
|
|
|
:marked
|
2017-03-21 11:08:09 -07:00
|
|
|
|
## The road you’ve travelled
|
|
|
|
|
Here's what you achieved in this page:
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
- You added the Angular router to navigate among different components.
|
|
|
|
|
- You learned how to create router links to represent navigation menu items.
|
|
|
|
|
- You used router link parameters to navigate to the details of the user-selected hero.
|
|
|
|
|
- You shared the `HeroService` among multiple components.
|
|
|
|
|
- You moved HTML and CSS out of the component file and into their own files.
|
|
|
|
|
- You added the `uppercase` pipe to format data.
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
Your app should look like this <live-example></live-example>.
|
2015-12-23 09:42:57 -08:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
### The road ahead
|
2016-08-17 13:31:40 -07:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
You have much of the foundation you need to build an app.
|
|
|
|
|
You're still missing a key piece: remote data access.
|
2016-06-19 00:20:38 -04:00
|
|
|
|
|
2017-03-21 11:08:09 -07:00
|
|
|
|
In the next page,
|
|
|
|
|
you’ll replace the mock data with data retrieved from a server using http.
|