Closed nasihere closed 8 years ago
Set the base href
** <script>document.write('<base href="' + document.location + '" />');</script>**
Set the
The Component Router uses the browser's history.pushState for navigation. Thanks to pushState, we can make our in-app URL paths look the way we want them to look, e.g. localhost:3000/crisis-center. Our in-app URLs can be indistinguishable from server URLs.
Modern HTML 5 browsers were the first to support pushState which is why many people refer to these URLs as "HTML 5 style" URLs.
We must add a
Add the base element just after the
tag. If the app folder is the application root, as it is for our application, set the href value exactly as shown here.Booting with the router service providers
Our app launches from the main.ts file in the /app folder so let's start there. It's short and all of it is relevant to routing.
main.ts
import {AppComponent} from './app.component';
import {bootstrap} from 'angular2/platform/browser';
import {ROUTER_PROVIDERS} from 'angular2/router';
bootstrap(AppComponent, [
ROUTER_PROVIDERS
]);
We import our root AppComponent and Angular's bootstrap function as expected.
We also import ROUTER_PROVIDERS from the router library. The router is a service implemented by a collection of Dependency Injection providers, most of which are identified in the ROUTER_PROVIDERS array.
We're booting Angular with AppComponent as our app's root component and registering providers, as we often do, in the providers array in the second parameter of the bootstrap function. Providing the router providers at the root makes the Component Router available everywhere in our application.
@RouteConfig()
A router holds a list of route definitions. The list is empty for a new router. We must configure it.
A router also needs a Host Component, a point of origin for its navigations.
It's natural to combine the creation of a new router, its configuration, and its assignment to a host component in a single step. That's the purpose of the @RouteConfig decorator which we put to good use here:
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
@Component({ ... })
@RouteConfig([
{path:'/crisis-center', name: 'CrisisCenter', component: CrisisListComponent},
{path:'/heroes', name: 'Heroes', component: HeroListComponent}
])
export class AppComponent { }
New route definition with route parameter
The new Heroes feature has two interacting components, the list and the detail. The list view is self-sufficient; we navigate to it, it gets a list of heroes and displays them. It doesn't need any outside information.
The detail view is different. It displays a particular hero. It can't know which hero on its own. That information must come from outside.
In our example, when the user selects a hero from the list, we navigate to the detail view to show that hero. We'll tell the detail view which hero to display by including the selected hero's id in the route URL.
With that plan in mind, we return to the app.component.ts to make changes to the router's configuration
First, we import the two components from their new locations in the app/heroes/ folder:
import {HeroListComponent} from './heroes/hero-list.component';
import {HeroDetailComponent} from './heroes/hero-detail.component';
import {HeroService} from './heroes/hero.service';
Then we update the @RouteConfig route definitions :
@Component({ ... })
@RouteConfig([
{path:'/crisis-center', name: 'CrisisCenter', component: CrisisListComponent},
{path:'/heroes', name: 'Heroes', component: HeroListComponent},
{path:'/hero/:id', name: 'HeroDetail', component: HeroDetailComponent}
])
export class AppComponent { }
The CrisisCenter and Heroes definitions didn't change. While we moved hero-list.component.ts to a new location in the app/heroes/ folder, that only affects the import statement; it doesn't affect its route definition.
We added a new route definition for the HeroDetailComponent — and this definition has a twist.
{path:'/hero/:id', name: 'HeroDetail', component: HeroDetailComponent}
Notice the :id token in the path. That creates a slot in the path for a Route Parameter. In this case, we're expecting the router to insert the id of a hero into that slot.
If we tell the router to navigate to the detail component and display "Magneta", we expect hero id (15) to appear in the browser URL like this:
localhost:3000/hero/15
If a user enters that URL into the browser address bar, the router should recognize the pattern and go to the same "Magneta" detail view.
Route parameter or query parameter?
Embedding the route parameter token, :id, in the route definition path is a good choice for our scenario because the id is required by the HeroDetailComponent and because the value 15 in the path clearly distinguishes the route to "Magneta" from a route for some other hero.
A query parameter might be a better choice if we were passing an optional value to HeroDetailComponent.
Navigate to the detail imperatively
We don't navigate to the detail component by clicking a link. We won't be adding a new anchor tag to the shell navigation bar.
Instead, we'll detect when the user selects a hero from the list and command the router to present the hero detail view of the selected hero.
We'll adjust the HeroListComponent to implement these tasks, beginning with its constructor which acquires the router service and the HeroService by dependency injection:
constructor(
private _router: Router,
private _service: HeroService) { }
We make a few changes to the template:
template: `
<h2>HEROES</h2>
<ul class="items">
<li *ngFor="#hero of heroes"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
`
The template defines an *ngFor repeater such as we've seen before. There's a (click) EventBinding to the component's onSelect method which we implement as follows:
onSelect(hero: Hero) {
this._router.navigate( ['HeroDetail', { id: hero.id }] );
}
It calls the router's navigate method with a Link Parameters Array. This array is similar to the link parameters array we met earlier in an anchor tag while binding to the RouterLink directive. This time we see it in code rather than in HTML.
Setting the route parameters object
We're navigating to the HeroDetailComponent where we expect to see the details of the selected hero. We'll need two pieces of information: the destination and the hero's id.
Accordingly, the link parameters array has two items: the name of the destination route and a route parameters object that specifies the id of the selected hero.
['HeroDetail', { id: hero.id }] // {id: 15}
The router composes the appropriate two-part destination URL from this array:
localhost:3000/hero/15
Getting the route parameter
How does the target HeroDetailComponent learn about that id? Certainly not by analyzing the URL! That's the router's job.
The router extracts the route parameter (id:15) from the URL and supplies it to the HeroDetailComponent via the RouteParams service.
As usual, we write a constructor that asks Angular to inject that service among the other services that the component require and reference them as private variables.
constructor(
private _router:Router,
private _routeParams:RouteParams,
private _service:HeroService){}
Later, in the ngOnInit method, we ask the RouteParams service for the id parameter by name and tell the HeroService to fetch the hero with that id.
ngOnInit() {
let id = this._routeParams.get('id');
this._service.getHero(id).then(hero => this.hero = hero);
}
Angular calls the ngOnInit method shortly after creating an instance of the HeroDetailComponent.
We put the data access logic in the ngOnInit method rather than inside the constructor to improve the component's testability. We explore this point in greater detail in the OnInit appendix below.
Navigating back to the list component
The HeroDetailComponent has a "Back" button wired to its gotoHeroes method that navigates imperatively back to the HeroListComponent.
The router navigate method takes the same one-item link parameters array that we wrote for the [routerLink] directive binding. It holds the name of the HeroListComponent route:
gotoHeroes() {
// Like <a [routerLink]="['Heroes']">Heroes</a>
this._router.navigate(['Heroes']);
}
to map anchor link by using [routerLink] directive
ex:
<a [routerLink]="['RepertoryBook',{ book: item.name }]" class="thumbnail">
Setup
The Angular Component Router is an optional service that presents a particular component view for a given URL. It is not part of the Angular 2 core. It is in its own library within the Angular npm bundle. We make it available by loading its script in our index.html, after the Angular core script.
Most routing applications should add a element just after the
tag to tell the router how to compose navigation URLs.If the app folder is the application root, as it is for our sample application, set the href value exactly as shown here.
Configuration
When the browser's URL changes, the router looks for a corresponding RouteDefinition from which it can determine the component to display.
A router has no route definitions until we configure it. The preferred way to simultaneously create a router and add its routes is with a @RouteConfig decorator applied to the router's host component.
In this example, we configure the top-level AppComponent with three route definitions
app.component.ts (excerpt)
@Component({ ... }) @RouteConfig([ {path:'/crisis-center', name: 'CrisisCenter', component: CrisisListComponent}, {path:'/heroes', name: 'Heroes', component: HeroListComponent}, {path:'/hero/:id', name: 'HeroDetail', component: HeroDetailComponent} ]) export class AppComponent { }
Router Outlet
Now we know how the router gets its configuration. When the browser URL for this application becomes /heroes, the router matches that URL to the RouteDefintion named Heroes and displays the HeroListComponent in a RouterOutlet that we've placed in the host view's HTML.