Child Routes In Angular
In this article, We will learn how to create a child or nested route to an Angular route. Child Routes, also known as nested routes in the Angular router, are a powerful new feature. Routes within routes are referred to as nested routes. We'll show you how to make a child route and display the child components in this article. We may nest child routes under other child routes in Angular, thereby building a Tree of routes.
Child Routes
Components are the foundation of Angular 2 apps. The Components are organized in a tree structure, with the root component at the top. Then we can add child components to construct loosely coupled components that look like a Tree.
Angular's Routes follow the component tree structure and allow us to construct nested or child routes.
Example
Consider the Component Tree below.
The ProductComponent in the preceding example displays a list of Products. The ProductDetailsComponent displays the details of the selected Product as a child of the ProductComponent.
/Product and /Product/Details/:Id would be our routes.
How to Create Child Routes
This article expands on the application we created in the Passing Parameters to Route article.
ProductDetailsComponent has already been established, however, it is not supposed to be a child route of ProductComponent. Let's make it a child route of the Product route by updating the code.
Define the Routes
To begin, open the app.routing.ts file
In our application, the following routes are defined.
The ProductDetailComponent is defined as a sibling, not a child, of the ProductComponent.
{ path: 'product', component: ProductComponent }, { path: 'product/:id', component: ProductDetailComponent },
We need to add the children key to the product route, which is an array of all child routes, as shown below, to make ProductDetailComponent a child of ProductComponent.
{ path: 'product', component: ProductComponent, children: [ { path: 'detail/:id', component: ProductDetailComponent } ],
import { Routes } from '@angular/router'; import { HomeComponent} from './home.component' import { ContactComponent} from './contact.component' import { ProductComponent} from './product.component' import { ErrorComponent} from './error.component' import { ProductDetailComponent} from './product-detail.component' export const appRoutes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'contact', component: ContactComponent }, { path: 'product', component: ProductComponent, children: [ { path: 'detail/:id', component: ProductDetailComponent } ] }, { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '**', component: ErrorComponent } ];
Display the component using Router-outlet
<h1>Product List</h1> <div class='table-responsive'> <table class='table'> <thead> <tr> <th>ID</th> <th>Name</th> <th>Price</th> </tr> </thead> <tbody> <tr *ngFor="let product of products;"> <td>{{product.productID}}</td> <td><a [routerLink]="['detail',product.productID]">{{product.name}} </a> </td> <td>{{product.price}}</td> </tr> </tbody> </table> </div> <router-outlet></router-outlet>
The Product Detail Component remains unchanged.
import { Component, OnInit } from '@angular/core'; import { Router,ActivatedRoute } from '@angular/router'; import { ProductService } from './product.service'; import { Product } from './product'; @Component({ templateUrl: './product-detail.component.html', }) export class ProductDetailComponent { product:Product; id; sub; constructor(private _Activatedroute:ActivatedRoute, private _router:Router, private _productService:ProductService){ } ngOnInit() { this.id=this._Activatedroute.snapshot.params['id']; let products=this._productService.getProducts(); this.product=products.find(p => p.productID==this.id); } }
Testing the Child Route
Why subscribe to route params
Why?
Using the Subscribe method to retrieve the parameters in child routes
ngOnInit() { this.sub=this._Activatedroute.params.subscribe(params => { this.id = params['id']; let products=this._productService.getProducts(); this.product=products.find(p => p.productID==this.id); }); }
To avoid memory leaking, we must unsubscribe when the component is deleted.
ngOnDestroy() { this.sub.unsubscribe(); }
Nesting Children under a child
Defining the child Route
export const appRoutes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'contact', component: ContactComponent }, { path: 'product', component: ProductComponent, children: [ { path: 'detail/:id', component: ProductDetailComponent, children : [ { path: 'overview', component: ProductOverviewComponent }, { path: 'spec', component: ProductSpecComponent }, { path: '', redirectTo:'overview', pathMatch:"full" } ] } ] }, { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '**', component: ErrorComponent } ];
Mapping the action to View
<h1>Product Details Page</h1> product : {{product.name}} price : {{ product.price}} <ul class="nav navbar-nav"> <li><a [routerLink]="['overview']">OverView </a></li> <li><a [routerLink]="['spec']">Specification </a></li> </ul> <router-outlet></router-outlet> <p> <a class='btn btn-default' (click)="onBack()">Back to Product List </a> </p>
When binding the path to the routerLink directive, we use a relative path. A forward slash / will be used to start absolute paths. When a relative path is used, the router appends the path to the parent route path to create the final URL.
<ul class="nav navbar-nav"> <li><a [routerLink]="['overview']">OverView </a></li> <li><a [routerLink]="['spec']">Specification </a></li> </ul>
The ProductDetailComponent renders both the ProductOverviewComponent and the ProductSpecComponent. As a result, we must include a <router-outlet></router-outlet> in the ProductDetailComponent Template.
<router-outlet></router-outlet>
The Child Components
import { Component, OnInit } from '@angular/core'; import { Router,ActivatedRoute } from '@angular/router'; import { ProductService } from './product.service'; import { Product } from './product'; @Component({ template: `<h3> Overview of {{product.name}} <h3>` }) export class ProductOverviewComponent { product:Product; id; sub; constructor(private _Activatedroute:ActivatedRoute, private _router:Router, private _productService:ProductService){ } ngOnInit() { this.sub=this._Activatedroute.parent.params.subscribe(params => { this.id = params['id']; let products=this._productService.getProducts(); this.product=products.find(p => p.productID==this.id); }); } ngOnDestroy() { this.sub.unsubscribe(); } }
ProductSpecComponent is identical to ProductSpecComponent.
import { Component, OnInit } from '@angular/core'; import { Router,ActivatedRoute } from '@angular/router'; import { ProductService } from './product.service'; import { Product } from './product'; @Component({ template: `<h3> Specification of {{product.name}} <h3>` }) export class ProductSpecComponent { product:Product; id; sub; constructor(private _Activatedroute:ActivatedRoute, private _router:Router, private _productService:ProductService){ } ngOnInit() { this.sub=this._Activatedroute.parent.params.subscribe(params => { this.id = params['id']; let products=this._productService.getProducts(); this.product=products.find(p => p.productID==this.id); }); } ngOnDestroy() { this.sub.unsubscribe(); } }