docs(toh-5/dart): use routerLink in dashboard (#2744)
* docs(toh-5/dart): use routerLink in dashboard * minor edits to TS jade * remove dart/toh-pt5 from bad-code-excerpt-skip-patterns
This commit is contained in:
parent
2808878c36
commit
c24dd074a6
|
@ -4,21 +4,27 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:angular2/core.dart';
|
||||
// #docregion import-router
|
||||
import 'package:angular2/router.dart';
|
||||
// #enddocregion import-router
|
||||
|
||||
import 'hero.dart';
|
||||
import 'hero_service.dart';
|
||||
// #enddocregion imports
|
||||
|
||||
// #docregion metadata
|
||||
@Component(
|
||||
selector: 'my-dashboard',
|
||||
// #docregion templateUrl
|
||||
templateUrl: 'dashboard_component.html',
|
||||
// #enddocregion templateUrl
|
||||
// #docregion css
|
||||
styleUrls: const ['dashboard_component.css']
|
||||
styleUrls: const ['dashboard_component.css'],
|
||||
// #enddocregion css
|
||||
directives: const [ROUTER_DIRECTIVES],
|
||||
)
|
||||
// #docregion component
|
||||
// #enddocregion metadata
|
||||
// #docregion class, component
|
||||
class DashboardComponent implements OnInit {
|
||||
List<Hero> heroes;
|
||||
|
||||
|
@ -26,11 +32,9 @@ class DashboardComponent implements OnInit {
|
|||
final HeroService _heroService;
|
||||
|
||||
DashboardComponent(this._heroService);
|
||||
|
||||
// #enddocregion ctor
|
||||
|
||||
Future<Null> ngOnInit() async {
|
||||
heroes = (await _heroService.getHeroes()).skip(1).take(4).toList();
|
||||
}
|
||||
}
|
||||
// #enddocregion component
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<h3>Top Heroes</h3>
|
||||
<div class="grid grid-pad">
|
||||
<!-- #docregion click -->
|
||||
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">
|
||||
<a *ngFor="let hero of heroes" [routerLink]="['./HeroDetail', {id: hero.id}]" class="col-1-4">
|
||||
<!-- #enddocregion click -->
|
||||
<div class="module hero">
|
||||
<h4>{{hero.name}}</h4>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
// #docregion , v2
|
||||
// #docregion added-imports
|
||||
import 'dart:async';
|
||||
import 'dart:html' show window;
|
||||
|
||||
// #enddocregion added-imports
|
||||
import 'package:angular2/core.dart';
|
||||
// #docregion added-imports
|
||||
import 'package:angular2/router.dart';
|
||||
import 'package:angular2/platform/common.dart';
|
||||
|
||||
// #enddocregion added-imports
|
||||
import 'hero.dart';
|
||||
|
@ -17,9 +17,9 @@ import 'hero_service.dart';
|
|||
|
||||
@Component(
|
||||
selector: 'my-hero-detail',
|
||||
// #docregion templateUrl
|
||||
// #docregion metadata, templateUrl
|
||||
templateUrl: 'hero_detail_component.html',
|
||||
// #enddocregion templateUrl, v2
|
||||
// #enddocregion metadata, templateUrl, v2
|
||||
styleUrls: const ['hero_detail_component.css']
|
||||
// #docregion v2
|
||||
)
|
||||
|
@ -30,8 +30,9 @@ class HeroDetailComponent implements OnInit {
|
|||
// #docregion ctor
|
||||
final HeroService _heroService;
|
||||
final RouteParams _routeParams;
|
||||
final Location _location;
|
||||
|
||||
HeroDetailComponent(this._heroService, this._routeParams);
|
||||
HeroDetailComponent(this._heroService, this._routeParams, this._location);
|
||||
// #enddocregion ctor
|
||||
|
||||
// #docregion ngOnInit
|
||||
|
@ -44,7 +45,7 @@ class HeroDetailComponent implements OnInit {
|
|||
|
||||
// #docregion goBack
|
||||
void goBack() {
|
||||
window.history.back();
|
||||
_location.back();
|
||||
}
|
||||
// #enddocregion goBack
|
||||
}
|
||||
|
|
|
@ -41,9 +41,11 @@ class HeroesComponent implements OnInit {
|
|||
selectedHero = hero;
|
||||
}
|
||||
|
||||
// #docregion gotoDetail
|
||||
Future<Null> gotoDetail() => _router.navigate([
|
||||
'HeroDetail',
|
||||
{'id': selectedHero.id.toString()}
|
||||
]);
|
||||
// #enddocregion gotoDetail
|
||||
// #docregion renaming
|
||||
}
|
||||
|
|
|
@ -33,4 +33,3 @@ export class DashboardComponent implements OnInit {
|
|||
.then(heroes => this.heroes = heroes.slice(1, 5));
|
||||
}
|
||||
}
|
||||
// #enddocregion class
|
||||
|
|
|
@ -96,12 +96,13 @@ block redirect-vs-use-as-default
|
|||
router will display the dashboard when the browser URL doesn't match an existing route.
|
||||
|
||||
block templateUrl-path-resolution
|
||||
:marked
|
||||
The value of `templateUrl` can be an [asset][] in this package or another
|
||||
package. To use an asset in another package, use a full package reference,
|
||||
such as `'package:some_other_package/dashboard_component.html'`.
|
||||
.l-sub-section
|
||||
:marked
|
||||
The value of `templateUrl` can be an [asset][] in this package or another
|
||||
package. To use an asset in another package, use a full package reference,
|
||||
such as `'package:some_other_package/dashboard_component.html'`.
|
||||
|
||||
[asset]: https://www.dartlang.org/tools/pub/glossary#asset
|
||||
[asset]: https://www.dartlang.org/tools/pub/glossary#asset
|
||||
|
||||
block route-params
|
||||
:marked
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
block includes
|
||||
include ../_util-fns
|
||||
- var _appRoutingTsVsAppComp = 'app-routing.module.ts'
|
||||
- var _appRoutingTsVsAppComp = 'app.module.ts'
|
||||
- var _declsVsDirectives = 'declarations'
|
||||
- var _RoutesVsAtRouteConfig = 'Routes'
|
||||
- var _RouterModuleVsRouterDirectives = 'RouterModule'
|
||||
|
@ -24,7 +24,7 @@ figure.image-display
|
|||
img(src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations")
|
||||
|
||||
:marked
|
||||
We'll add Angular’s *Component Router* to our app to satisfy these requirements.
|
||||
We'll add Angular’s *Router* to our app to satisfy these requirements.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -52,7 +52,7 @@ figure.image-display
|
|||
|
||||
block intro-file-tree
|
||||
.filetree
|
||||
.file angular2-tour-of-heroes
|
||||
.file angular-tour-of-heroes
|
||||
.children
|
||||
.file app
|
||||
.children
|
||||
|
@ -164,7 +164,7 @@ block app-comp-v1
|
|||
Instead of displaying heroes automatically, we'd like to show them *after* the user clicks a button.
|
||||
In other words, we'd like to navigate to the list of heroes.
|
||||
|
||||
We'll need the Angular *Component Router*.
|
||||
We'll need the Angular *Router*.
|
||||
|
||||
block angular-router
|
||||
:marked
|
||||
|
@ -192,7 +192,7 @@ block router-config-intro
|
|||
### Configure routes
|
||||
|
||||
Our application doesn't have any routes yet.
|
||||
We'll start by creating a configuration file for the application routes.
|
||||
We'll start by creating a configuration for the application routes.
|
||||
|
||||
:marked
|
||||
*Routes* tell the router which views to display when a user clicks a link or
|
||||
|
@ -200,7 +200,7 @@ block router-config-intro
|
|||
|
||||
Let's define our first route as a route to the heroes component:
|
||||
|
||||
- var _file = _docsFor == 'dart' ? 'app.component.ts' : 'app-routing.module.ts'
|
||||
- var _file = _docsFor == 'dart' ? 'app.component.ts' : 'app.module.2.ts'
|
||||
+makeExcerpt('app/' + _file + ' (heroes route)', 'heroes')
|
||||
|
||||
- var _are = _docsFor == 'dart' ? 'takes' : 'are'
|
||||
|
@ -222,24 +222,18 @@ block router-config-intro
|
|||
|
||||
+ifDocsFor('ts|js')
|
||||
:marked
|
||||
We'll export a `routing` constant initialized using the `RouterModule.forRoot` method applied to our !{_array} of routes.
|
||||
This method returns a **configured router module** that we'll add to our root NgModule, `AppModule`.
|
||||
### Make the router available
|
||||
|
||||
+makeExcerpt('app/app-routing.module.1.ts (excerpt)', 'routing-export')
|
||||
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}.
|
||||
|
||||
+makeExcerpt('app/app.module.2.ts (app routing)', '')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We call the `forRoot` method because we're providing a configured router at the _root_ of the application.
|
||||
The `forRoot` method gives us the Router service providers and directives needed for routing.
|
||||
|
||||
:marked
|
||||
### Make the router available
|
||||
|
||||
We've setup initial routes in the `app-routing.module.ts` file. Now we'll add it to our root NgModule.
|
||||
|
||||
Import the `routing` constant from `app-routing.module.ts` and add it the `imports` !{_array} of `AppModule`.
|
||||
|
||||
+makeExcerpt('app/app.module.ts', 'routing')
|
||||
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.
|
||||
|
||||
- var _heroesRoute = _docsFor == 'dart' ? "'Heroes'" : 'heroes'
|
||||
:marked
|
||||
|
@ -317,12 +311,12 @@ block routerLink
|
|||
Import the dashboard component and
|
||||
add the following route definition to the `!{_RoutesVsAtRouteConfig}` !{_array} of definitions.
|
||||
|
||||
- var _file = _docsFor == 'dart' ? 'lib/app_component.dart' : 'app/app-routing.module.ts'
|
||||
- var _file = _docsFor == 'dart' ? 'lib/app_component.dart' : 'app/app.module.3.ts'
|
||||
+makeExcerpt(_file + ' (Dashboard route)', 'dashboard')
|
||||
|
||||
+ifDocsFor('ts|js')
|
||||
:marked
|
||||
Also import and add `DashboardComponent` to our root NgModule's `declarations`.
|
||||
Also import and add `DashboardComponent` to our `AppModule`'s `declarations`.
|
||||
|
||||
+makeExcerpt('app/app.module.ts', 'dashboard')
|
||||
|
||||
|
@ -338,7 +332,7 @@ block redirect-vs-use-as-default
|
|||
We can use a redirect route to make this happen. Add the following
|
||||
to our array of route definitions:
|
||||
|
||||
+makeExcerpt('app/app-routing.module.ts','redirect')
|
||||
+makeExcerpt('app/app.module.3.ts','redirect')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -354,7 +348,7 @@ block redirect-vs-use-as-default
|
|||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We nestled the two links within `<nav>` tags.
|
||||
We nested the two links within `<nav>` tags.
|
||||
They don't do anything yet but they'll be convenient when we style the links a little later in the chapter.
|
||||
|
||||
:marked
|
||||
|
@ -367,30 +361,24 @@ block redirect-vs-use-as-default
|
|||
|
||||
Replace the `template` metadata with a `templateUrl` property that points to a new
|
||||
template file.
|
||||
+ifDocsFor('ts|js')
|
||||
:marked
|
||||
Set the `moduleId` property to `module.id` for module-relative loading of the `templateUrl`.
|
||||
|
||||
+makeExcerpt('app/dashboard.component.ts', 'templateUrl')
|
||||
+makeExcerpt('app/dashboard.component.ts', 'metadata')
|
||||
|
||||
.l-sub-section
|
||||
block templateUrl-path-resolution
|
||||
:marked
|
||||
We specify the path _all the way back to the application root_ —
|
||||
<span if-docs="ts">`app/` in this case —</span>
|
||||
because Angular doesn't support relative paths _by default_.
|
||||
We _can_ switch to [component-relative paths](../cookbook/component-relative-paths.html) if we prefer.
|
||||
block templateUrl-path-resolution
|
||||
//- N/A for TS
|
||||
|
||||
:marked
|
||||
Create that file with this content:
|
||||
|
||||
+makeExcerpt('app/dashboard.component.html')
|
||||
+makeExample('app/dashboard.component.1.html', '', 'app/dashboard.component.html')
|
||||
|
||||
:marked
|
||||
We use `*ngFor` once again to iterate over a list of heroes and display their names.
|
||||
We added extra `<div>` elements to help with styling later in this chapter.
|
||||
|
||||
There's a `(click)` binding to a `gotoDetail` method we haven't written yet and
|
||||
we're displaying a list of heroes that we don't have.
|
||||
We have work to do, starting with those heroes.
|
||||
|
||||
### Share the *HeroService*
|
||||
|
||||
We'd like to re-use the `HeroService` to populate the component's `heroes` !{_array}.
|
||||
|
@ -405,12 +393,12 @@ block redirect-vs-use-as-default
|
|||
|
||||
Open <span ngio-ex>dashboard.component.ts</span> and add the requisite `import` statements.
|
||||
|
||||
+makeExcerpt('app/dashboard.component.2.ts','imports')
|
||||
+makeExcerpt('app/dashboard.component.ts','imports')
|
||||
|
||||
:marked
|
||||
Now implement the `DashboardComponent` class like this:
|
||||
|
||||
+makeExcerpt('app/dashboard.component.2.ts (class)', 'component')
|
||||
+makeExcerpt('app/dashboard.component.ts (class)', 'class')
|
||||
|
||||
:marked
|
||||
We've seen this kind of logic before in the `HeroesComponent`:
|
||||
|
@ -419,8 +407,7 @@ block redirect-vs-use-as-default
|
|||
* Inject the `HeroService` in the constructor and hold it in a private `!{_priv}heroService` field.
|
||||
* Call the service to get heroes inside the Angular `ngOnInit` lifecycle hook.
|
||||
|
||||
The noteworthy differences: we cherry-pick four heroes (2nd, 3rd, 4th, and 5th)
|
||||
and stub the `gotoDetail` method until we're ready to implement it.
|
||||
In this dashboard we cherry-pick four heroes (2nd, 3rd, 4th, and 5th)<span if-docs="ts"> with the `Array.slice` method</span>.
|
||||
|
||||
Refresh the browser and see four heroes in the new dashboard.
|
||||
|
||||
|
@ -470,7 +457,7 @@ code-example(format='').
|
|||
|
||||
Here's the *route definition* we'll use.
|
||||
|
||||
- var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app-routing.module.ts'
|
||||
- var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app.module.3.ts'
|
||||
+makeExcerpt(_file + ' (hero detail)','hero-detail')
|
||||
|
||||
:marked
|
||||
|
@ -521,7 +508,7 @@ block route-params
|
|||
|
||||
- var _ActivatedRoute = _docsFor == 'dart' ? 'RouteParams' : 'ActivatedRoute'
|
||||
:marked
|
||||
Let's have the `!{_ActivatedRoute}` service and the `HeroService` injected
|
||||
Let's have the `!{_ActivatedRoute}` service, the `HeroService` and the `Location` service injected
|
||||
into the constructor, saving their values in private fields:
|
||||
|
||||
+makeExcerpt('app/hero-detail.component.ts (constructor)', 'ctor')
|
||||
|
@ -534,7 +521,7 @@ block route-params
|
|||
block ngOnInit
|
||||
:marked
|
||||
Inside the `ngOnInit` lifecycle hook, we use the `params` observable to
|
||||
extract the `id` parameter value from the `ActivateRoute` service
|
||||
extract the `id` parameter value from the `ActivatedRoute` service
|
||||
and use the `HeroService` to fetch the hero with that `id`.
|
||||
|
||||
+makeExcerpt('app/hero-detail.component.ts', 'ngOnInit')
|
||||
|
@ -567,7 +554,8 @@ block extract-id
|
|||
How do we navigate somewhere else when we're done?
|
||||
|
||||
The user could click one of the two links in the `AppComponent`. Or click the browser's back button.
|
||||
We'll add a third option, a `goBack` method that navigates backward one step in the browser's history stack.
|
||||
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.
|
||||
|
||||
+makeExcerpt('app/hero-detail.component.ts', 'goBack')
|
||||
|
||||
|
@ -586,16 +574,16 @@ block extract-id
|
|||
+makeExcerpt('app/hero-detail.component.html', 'back-button', '')
|
||||
|
||||
:marked
|
||||
Modifing the template to add this button spurs us to take one more
|
||||
Modifying the template to add this button spurs us to take one more
|
||||
incremental improvement and migrate the template to its own file,
|
||||
called <span ngio-ex>hero-detail.component.html</span>:
|
||||
|
||||
+makeExample('app/hero-detail.component.html')
|
||||
|
||||
:marked
|
||||
We update the component metadata with a `templateUrl` pointing to the template file that we just created.
|
||||
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.
|
||||
|
||||
+makeExcerpt('app/hero-detail.component.ts', 'templateUrl')
|
||||
+makeExcerpt('app/hero-detail.component.ts', 'metadata')
|
||||
|
||||
:marked
|
||||
Refresh the browser and see the results.
|
||||
|
@ -606,71 +594,118 @@ block extract-id
|
|||
|
||||
When a user selects a hero in the dashboard, the app should navigate to the `HeroDetailComponent` to view and edit the selected hero.
|
||||
|
||||
In the dashboard template we bound each hero's click event to the `gotoDetail` method, passing along the selected `hero` entity.
|
||||
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.
|
||||
|
||||
+makeExcerpt('app/dashboard.component.html', 'click')
|
||||
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:
|
||||
|
||||
:marked
|
||||
We stubbed the `gotoDetail` method when we rewrote the `DashboardComponent`.
|
||||
Now we give it a real implementation.
|
||||
+makeExample('app/dashboard.component.html', 'click', 'app/dashboard.component.html (repeated <a> tag)')
|
||||
|
||||
+makeExcerpt('app/dashboard.component.ts','gotoDetail')
|
||||
+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).
|
||||
|
||||
- var _pathVsName = _docsFor == 'dart' ? 'name' : 'path'
|
||||
:marked
|
||||
The `gotoDetail` method navigates in two steps:
|
||||
Notice the `[routerLink]` binding.
|
||||
|
||||
1. Set a route *link parameters !{_array}*
|
||||
1. Pass the !{_array} to the router's navigate method
|
||||
Top level navigation in the [`AppComponent`
|
||||
template](#router-links) has router links set to fixed !{_pathVsName}s of the
|
||||
destination routes, "/dashboard" and "/heroes".
|
||||
|
||||
For navigation, we wrote router links <span if-docs="dart">as *link
|
||||
parameters !{_array}s*</span> in the [`AppComponent`
|
||||
template](#router-links). Those link<span if-docs="dart"> parameters
|
||||
!{_array}</span>s had only one element, the !{_pathVsName} of the
|
||||
destination route.
|
||||
|
||||
This link parameters !{_array} has two elements, the ***!{_pathVsName}*** of
|
||||
the destination route and a ***route parameter*** <span if-docs="dart">with
|
||||
an `id` field</span> set to the value of the selected hero's `id`.
|
||||
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`.
|
||||
|
||||
The two !{_array} items align with the ***!{_pathVsName}*** and ***:id***
|
||||
token in the parameterized hero detail route definition we added to
|
||||
`!{_appRoutingTsVsAppComp}` earlier in the chapter:
|
||||
|
||||
- var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app-routing.module.ts'
|
||||
- var _file = _docsFor == 'dart' ? 'app/app.component.ts' : 'app/app.module.3.ts'
|
||||
+makeExcerpt(_file + ' (hero detail)', 'hero-detail')
|
||||
|
||||
:marked
|
||||
The `DashboardComponent` doesn't have the router yet. We obtain it in the usual way:
|
||||
import the `router` reference and inject it in the constructor (along with the `HeroService`):
|
||||
|
||||
+makeExcerpt('app/dashboard.component.ts ()','import-router', '')
|
||||
|
||||
+makeExcerpt('app/dashboard.component.ts', 'ctor', '')
|
||||
|
||||
:marked
|
||||
Refresh the browser and select a hero from the dashboard; the app should navigate directly to that hero’s details.
|
||||
|
||||
+ifDocsFor('ts')
|
||||
.l-main-section
|
||||
:marked
|
||||
## Refactor routes to a _Routing Module_
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Select a Hero in the *HeroesComponent*
|
||||
|
||||
Earlier we added the ability to select a hero from the dashboard.
|
||||
We'll do something similar in the `HeroesComponent`.
|
||||
|
||||
That component's current template exhibits a "master/detail" style with the list of heroes
|
||||
The `HeroesComponent` template exhibits a "master/detail" style with the list of heroes
|
||||
at the top and details of the selected hero below.
|
||||
|
||||
+makeExample('toh-4/ts/app/app.component.ts','template', 'app/heroes.component.ts (current template)')(format=".")
|
||||
|
||||
:marked
|
||||
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).
|
||||
|
||||
Delete the last line of the template with the `<my-hero-detail>` tags.
|
||||
|
||||
We'll no longer show the full `HeroDetailComponent` here.
|
||||
We're going to display the hero detail on its own page and route to it as we did in the dashboard.
|
||||
|
||||
But we'll throw in a small twist for variety.
|
||||
When the user selects a hero from the list, we *won't* go to the detail page.
|
||||
We'll show a *mini-detail* on *this* page instead and make the user click a button to navigate to the *full detail *page.
|
||||
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.
|
||||
|
||||
### Add the *mini-detail*
|
||||
|
||||
|
@ -714,13 +749,12 @@ figure.image-display
|
|||
1. *Cut-and-paste* the template contents into a new <span ngio-ex>heroes.component.html</span> file.
|
||||
1. *Cut-and-paste* the styles contents into a new <span ngio-ex>heroes.component.css</span> file.
|
||||
1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files.
|
||||
<li if-docs="ts">. *Set* the `moduleId` property to `module.id` so that `templateUrl` and `styleUrls` are relative to the component.</li>
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
The `styleUrls` property is !{_an} !{_array} of style file names (with paths).
|
||||
We could list multiple style files from different locations if we needed them.
|
||||
<span if-docs="ts">As with `templateUrl`, we must specify the path _all the way
|
||||
back to the application root_.</span>
|
||||
|
||||
block heroes-component-cleanup
|
||||
//- Only relevant for Dart.
|
||||
|
@ -728,15 +762,26 @@ block heroes-component-cleanup
|
|||
+makeExcerpt('app/heroes.component.ts (revised metadata)', 'metadata')
|
||||
|
||||
:marked
|
||||
Now we can see what's going on as we update the component class along the same lines as the dashboard:
|
||||
### Update the _HeroesComponent_ class.
|
||||
|
||||
1. Import the `router`
|
||||
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.
|
||||
|
||||
This approach requires some changes to the component class:
|
||||
|
||||
1. Import the `router` from the Angular router library
|
||||
1. Inject the `router` in the constructor (along with the `HeroService`)
|
||||
1. Implement the `gotoDetail` method by calling the `router.navigate` method
|
||||
1. Implement `gotoDetail` by calling the `router.navigate` method
|
||||
|
||||
with a two-part hero-detail link parameters !{_array}.
|
||||
+makeExcerpt('app/heroes.component.ts', 'gotoDetail')
|
||||
|
||||
Here's the revised component class:
|
||||
: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:
|
||||
|
||||
+makeExcerpt('app/heroes.component.ts', 'class')
|
||||
|
||||
|
@ -834,7 +879,7 @@ block css-files
|
|||
|
||||
+makeExcerpt('styles.css (excerpt)', 'toh')
|
||||
|
||||
- var styles_css = 'https://raw.githubusercontent.com/angular/angular.io/master/public/docs/_examples/styles.css'
|
||||
- var styles_css = 'https://raw.githubusercontent.com/angular/angular.io/master/public/docs/_examples/_boilerplate/styles.css'
|
||||
|
||||
:marked
|
||||
Create the file <span ngio-ex>styles.css</span>, if it doesn't exist already.
|
||||
|
@ -859,7 +904,7 @@ figure.image-display
|
|||
|
||||
block file-tree-end
|
||||
.filetree
|
||||
.file angular2-tour-of-heroes
|
||||
.file angular-tour-of-heroes
|
||||
.children
|
||||
.file app
|
||||
.children
|
||||
|
@ -895,12 +940,13 @@ block file-tree-end
|
|||
|
||||
We travelled a great distance in this chapter
|
||||
|
||||
- We added the Angular *Component Router* to navigate among different components.
|
||||
- We added the Angular *Router* to navigate among different components.
|
||||
- We learned how to create router links to represent navigation menu items.
|
||||
- We used router link parameters to navigate to the details of user selected hero.
|
||||
- We shared the `HeroService` among multiple components.
|
||||
- We moved HTML and CSS out of the component file and into their own files.
|
||||
- We added the `uppercase` pipe to format data.
|
||||
<li if-docs="ts"> We refactored routes into a `Routing Module` that we import.</li>
|
||||
|
||||
### The Road Ahead
|
||||
|
||||
|
|
|
@ -205,7 +205,6 @@ block router-config-intro
|
|||
|
||||
- var _are = _docsFor == 'dart' ? 'takes' : 'are'
|
||||
- var _routePathPrefix = _docsFor == 'dart' ? '/' : ''
|
||||
|
||||
:marked
|
||||
The `!{_RoutesVsAtRouteConfig}` !{_are} !{_an} !{_array} of *route definitions*.
|
||||
We have only one route definition at the moment but rest assured, we'll add more.
|
||||
|
@ -362,11 +361,15 @@ block redirect-vs-use-as-default
|
|||
|
||||
Replace the `template` metadata with a `templateUrl` property that points to a new
|
||||
template file.
|
||||
|
||||
Set the `moduleId` property to `module.id` for module-relative loading of the `templateUrl`.
|
||||
+ifDocsFor('ts|js')
|
||||
:marked
|
||||
Set the `moduleId` property to `module.id` for module-relative loading of the `templateUrl`.
|
||||
|
||||
+makeExcerpt('app/dashboard.component.ts', 'metadata')
|
||||
|
||||
block templateUrl-path-resolution
|
||||
//- N/A for TS
|
||||
|
||||
:marked
|
||||
Create that file with this content:
|
||||
|
||||
|
@ -404,7 +407,7 @@ block redirect-vs-use-as-default
|
|||
* 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.
|
||||
|
||||
In this dashboard we cherry-pick four heroes (2nd, 3rd, 4th, and 5th) with the `Array.slice` method.
|
||||
In this dashboard we cherry-pick four heroes (2nd, 3rd, 4th, and 5th)<span if-docs="ts"> with the `Array.slice` method</span>.
|
||||
|
||||
Refresh the browser and see four heroes in the new dashboard.
|
||||
|
||||
|
@ -575,10 +578,10 @@ block extract-id
|
|||
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')(format='.')
|
||||
+makeExample('app/hero-detail.component.html')
|
||||
|
||||
:marked
|
||||
We update the component metadata with a `moduleId` and a `templateUrl` pointing to the template file that we just created.
|
||||
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.
|
||||
|
||||
+makeExcerpt('app/hero-detail.component.ts', 'metadata')
|
||||
|
||||
|
@ -600,11 +603,17 @@ block extract-id
|
|||
|
||||
+makeExample('app/dashboard.component.html', 'click', 'app/dashboard.component.html (repeated <a> tag)')
|
||||
|
||||
+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).
|
||||
|
||||
- var _pathVsName = _docsFor == 'dart' ? 'name' : 'path'
|
||||
:marked
|
||||
Notice the `[routerLink]` binding.
|
||||
|
||||
In the top level navigation in the [`AppComponent`
|
||||
Top level navigation in the [`AppComponent`
|
||||
template](#router-links) has router links set to fixed !{_pathVsName}s of the
|
||||
destination routes, "/dashboard" and "/heroes".
|
||||
|
||||
|
@ -622,53 +631,55 @@ block extract-id
|
|||
:marked
|
||||
Refresh the browser and select a hero from the dashboard; the app should navigate directly to that hero’s details.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Refactor routes to a _Routing Module_
|
||||
+ifDocsFor('ts')
|
||||
.l-main-section
|
||||
:marked
|
||||
## Refactor routes to a _Routing Module_
|
||||
|
||||
Almost 20 lines of `AppModule` are devoted to configuring four routes.
|
||||
Most application 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.
|
||||
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.
|
||||
|
||||
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).
|
||||
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:
|
||||
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.
|
||||
|
||||
+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.
|
||||
Create an `app-routing.module.ts` file as a sibling to `app.module.ts`. Give it the following contents extracted from the `AppModule` class:
|
||||
|
||||
* 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.
|
||||
+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 module `providers` for guard services if you have them; there are none in this example.
|
||||
* Add `RouterModule.forRoot(routes)` to `imports`.
|
||||
|
||||
### Update _AppModule_
|
||||
* Add `RouterModule` to `exports` so that the components in the companion module have access to Router declarables
|
||||
such as `RouterLink` and `RouterOutlet`.
|
||||
|
||||
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).
|
||||
* 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.
|
||||
|
||||
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.
|
||||
.l-main-section
|
||||
:marked
|
||||
## Select a Hero in the *HeroesComponent*
|
||||
|
@ -738,7 +749,7 @@ figure.image-display
|
|||
1. *Cut-and-paste* the template contents into a new <span ngio-ex>heroes.component.html</span> file.
|
||||
1. *Cut-and-paste* the styles contents into a new <span ngio-ex>heroes.component.css</span> file.
|
||||
1. *Set* the component metadata's `templateUrl` and `styleUrls` properties to refer to both files.
|
||||
1. *Set* the `moduleId` property to `module.id` so that `templateUrl` and `styleUrls` are relative to the component.
|
||||
<li if-docs="ts">. *Set* the `moduleId` property to `module.id` so that `templateUrl` and `styleUrls` are relative to the component.</li>
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
|
@ -764,11 +775,12 @@ block heroes-component-cleanup
|
|||
1. Implement `gotoDetail` by calling the `router.navigate` method
|
||||
|
||||
+makeExcerpt('app/heroes.component.ts', 'gotoDetail')
|
||||
|
||||
: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`
|
||||
back in the `DashboardComponent`.
|
||||
Here's the fully revised `HeroesComponent` class:
|
||||
|
||||
+makeExcerpt('app/heroes.component.ts', 'class')
|
||||
|
@ -934,7 +946,7 @@ block file-tree-end
|
|||
- We shared the `HeroService` among multiple components.
|
||||
- We moved HTML and CSS out of the component file and into their own files.
|
||||
- We added the `uppercase` pipe to format data.
|
||||
- We refactored routes into a `Routing Module` that we import.
|
||||
<li if-docs="ts"> We refactored routes into a `Routing Module` that we import.</li>
|
||||
|
||||
### The Road Ahead
|
||||
|
||||
|
|
|
@ -2,4 +2,3 @@
|
|||
# <grep-pattern-to-match-file-path> # reason / issue number
|
||||
|
||||
/[jt]s/.*/api/forms/index/NG_VALIDATORS-let.html # RC6 contains broken example tags
|
||||
/dart/latest/tutorial/toh-pt5.html
|
||||
|
|
Loading…
Reference in New Issue