Книга: Angular 2 Cookbook
Назад: Building stateful route behavior with RouterLinkActive
Дальше: Working with matrix URL parameters and routing arrays

Implementing nested views with route parameters and child routes

Angular 2's component router offers you the necessary concept of child routes. As you might expect, this brings the concept of recursively defined views to the table, which affords you an incredibly useful and elegant way of building your application.

Note

The code, links, and a live example of this are available at .

Getting ready

Begin with the Array and anchor-tag-based implementation shown in Navigating with routerLinks recipe.

Your goal is to extend this simple application to include /article, which will be the list view, and /article/:id, which will be the detail view.

How to do it...

First, modify the route structure for this simple application by extending the /article path to include its subpaths: / and /:id. Routes are defined hierarchically, and each route can have child routes using the children property.

Adding a routing target to the parent component

First, you must modify the existing ArticleComponent so that it can contain child views. As you might expect, the child view is rendered in exactly the same way as it is done from the root component, using RouterOutlet:

[app/article.component.ts]      import {Component} from '@angular/core';      @Component({     template: `       <h2>Article</h2>       <router-outlet></router-outlet>     `   })   export class ArticleComponent {}   

This won't do anything yet, but adding RouterOutlet describes to Angular how route component hierarchies should be rendered.

Defining nested child views

In this recipe, you would like to have the parent ArticleComponent contain a child view, either ArticleListComponent or ArticleDetailComponent. For the simplicity of this recipe, you can just define your list of articles as an array of integers.

Define the skeleton of these two components as follows:

[app/article-list.component.ts]      import {Component} from '@angular/core';      @Component({     template: `       <h3>Article List</h3>     `   })   export class ArticleListComponent {     articleIds:Array<number> = [1,2,3,4,5];   }   [app/article-detail.component.ts]      import {Component} from '@angular/core';      @Component({     template: `       <h3>Article Detail</h3>       <p>Showing article {{articleId}}</p>     `   })   export class ArticleDetailComponent {     articleId:number;   }   

Defining the child routes

At this point, nothing in the application yet points to either of these child routes, so you'll need to define them now.

The children property of a route should just be another Route, which should represent the nested routes that are appended to the parent route.

Note

In this way, you are defining a sort of routing "tree," where each route entry can have many child routes defined recursively. This will be discussed in greater detail later in this chapter.

Furthermore, you should also use the URL parameter notation to declare :articleId as a variable in the route. This allows you to pass values inside the route and then retrieve these values inside the component that is rendered.

Add these route definitions now:

[app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {RouterModule, Routes} from '@angular/router';   import {RootComponent} from './root.component';   import {DefaultComponent} from './default.component';   import {ArticleComponent} from './article.component';   import {ArticleListComponent} from './article-list.component';   import {ArticleDetailComponent} from './article-detail.component';      const appRoutes:Routes = [     {path: 'article', component: ArticleComponent,       children: [         {path: '', component: ArticleListComponent},          {path: ':articleId', component: ArticleDetailComponent}       ]     },     {path: '**', component: DefaultComponent},   ];      @NgModule({     imports: [       BrowserModule,       RouterModule.forRoot(appRoutes)     ],     declarations: [       DefaultComponent,       ArticleComponent,       ArticleListComponent,       ArticleDetailComponent,       RootComponent     ],     bootstrap: [       RootComponent     ]   })   export class AppModule {}   

You'll note that ArticleListComponent is keyed by an empty string. This should make sense, as each of these routes are joined to their parent routes to create the full route. If you were to join each route in this tree with its ancestral path to get the full route, the route definition you've just created would have the following three entries:

/article   => ArticleComponent                   ArticleListComponent   /article/4 => ArticleComponent                   ArticleDetailComponent<articleId=4>   /**        => DefaultComponent   

Note

Note that in this case, the number of actual routes corresponds to the number of leaves of the URL tree since the article parent route will also map to the child article's + '' route. Depending on how you configure your route structure, the leaf/route parity will not always be the case.

Defining child view links

With the routes being mapped to the child components, you can flesh out the child views. Starting with ArticleList, create a repeater to generate the links to each of the child views:

[app/article-list.component.ts]      import {Component} from '@angular/core';      @Component({     template: `       <h3>Article List</h3>       <p *ngFor="let articleId of articleIds">         <a [routerLink]="articleId">           Article {{articleId}}         </a>       </p>     `   })   export class ArticleListComponent {     articleIds:Array<number> = [1,2,3,4,5];   }   

Note

Note that routerLink is linking to the relative path of the detail view. Since the current path for this view is /article, a relative routerLink of 4 will navigate the application to /article/4 upon a click.

These links should work, but when you click on them, they will take you to the detail view that cannot display articleId from the route since you have not extracted it yet.

Inside ArticleDetailComponent, create a link that will take the user back to the article/ route. Since routes behave like directories, you can just use a relative path that will take the user up one level:

[app/article-detail.component.ts]      import {Component} from '@angular/core';      @Component({     template: `       <h3>Article Detail</h3>       <p>Showing article {{articleId}}</p>       <a [routerLink]="'../'">Back up</a>     `   })   export class ArticleDetailComponent {     articleId:number;   }   

Extracting route parameters

A crucial difference between Angular 1 and 2 is the reliance on Observable constructs. In the context of routing, Angular 2 wields Observables to encapsulate that routing occurs as a sequence of events and that values are produced at different states in these events and will be ready eventually.

More concretely, route params in Angular 2 are not exposed directly, but rather through an Observable inside ActivatedRoute. You can set Observer on its params Observable to extract the route params once they are available.

Inject the ActivatedRoute interface and use the params Observable to extract articleId and assign it to the ArticleDetailComponent instance member:

[app/article-detail/article-detail.component.ts]      import {Component} from '@angular/core';   import {ActivatedRoute} from '@angular/router';      @Component({     template: `       <h3>Article Detail</h3>       <p>Showing article {{articleId}}</p>       <a [routerLink]="'../'">Back up</a>     `   })   export class ArticleDetailComponent {     articleId:number;     constructor(private activatedRoute_: ActivatedRoute) {       activatedRoute_.params         .subscribe(params => this.articleId = params['articleId']);     }   }   

With this, you should be able to see the articleId parameter interpolated into ArticleDetailComponent.

How it works...

In this application, you have nested components, AppComponent and ArticleComponent, both of which contain RouterOutlet. Angular is able to take the routing hierarchy you defined and apply it to the component hierarchy that it maps to. More specifically, for every Route you define in your routing hierarchy, there should be an equal number of RouterOutlets in which they can render.

There's more...

To some, it will feel strange to need to extract the route params from an Observable interface. If this solution feels a bit clunky to you, there are ways of tidying it up.

Refactoring with async pipes

Recall that Angular has the ability to interpolate Observable data directly into the template as it becomes ready. Especially since you should only ever expect the param Observable to emit once, you can use it to insert articleId into the template without explicitly setting an Observer:

[app/article-detail.component.ts]      import {Component} from '@angular/core';   import {ActivatedRoute } from '@angular/router';      @Component({     template: `       <h3>Article Detail</h3>       <p>Showing article           {{(activatedRoute.params | async).articleId}}</p>       <a [routerLink]="'../'">Back up</a>     `   })   export class ArticleDetailComponent {     constructor(activatedRoute: ActivatedRoute) {}   }   

Even though this works perfectly well, using a private reference to an injected service directly into the template may feel a bit funny to you. A superior strategy is to grab a reference to the public Observable interface you need and interpolate that instead:

[app/article-detail.component.ts]      import {Component} from '@angular/core';   import {Observable} from 'rxjs/Observable';   import {ActivatedRoute, Params} from '@angular/router';      @Component({     template: `       <h3>Article Detail</h3>       <p>Showing article {{(params | async).articleId}}</p>       <a [routerLink]="'../'">Back up</a>     `   })   export class ArticleDetailComponent {     params:Observable<Params>;     constructor(private activatedRoute_: ActivatedRoute) {       this.params = activatedRoute_.params;     }   }   

See also

  • Navigating with routerLinks demonstrates how to navigate around Angular applications
  • Navigating with the Router service uses an Angular service to navigate around an application
  • Building stateful RouterLink behavior with RouterLinkActive shows how to integrate application behavior with a URL state
  • Working with matrix URL parameters and routing arrays demonstrates Angular's built-in matrix URL support
  • Adding route authentication controls with route guards details the entire process of configuring protected routes in your application
Назад: Building stateful route behavior with RouterLinkActive
Дальше: Working with matrix URL parameters and routing arrays

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already