Child Routes In Angular

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.

Child Component Routes Tree In Angular

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 }
  ],

The definition of a child route is similar to that of a parent route. When the user navigates to the child route, it has a path and component that is called.

The parent route path is 'product,' while the child route is 'detail/:id' in the example above.

This will match the "/product/detail/id" URL route.

The router will begin looking for a match in the routes array when the user navigates to "/product/detail/id"

It starts with the first URL segment, 'product' and looks for a match in the path 'product' then instantiates the ProductComponent and displays it in the parent component's router-outlet> directive ( which is AppComponent)

The router then takes the remainder of the URL segment 'detail/id' and continues to look for the requested resource.

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

The components are always rendered in the parent component's <RouterOutlet>.

ProductComponent, not AppComponent, is the parent component for ProductDetailComponent.

As a result, we must include the <router-outlet></router-outlet> tag in the product.component.html file, as seen below.

<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);
   }
  
}

It's worth noting that we're retrieving the route parameter id using the snapshot approach.

Testing the Child Route

Start the app and go to the Product tab. You'll notice that the Product Page has appeared. When you click on any of the products, you'll be sent to the product details page.

Why subscribe to route params

When you click on a different product, you'll see that the Product Details page isn't updated with the new item.

Why?

Because if a component is already existent in the DOM, angular will not generate it. The component instance is reused.

When the user navigates back to the component, the ngOnInit life cycle hook is not triggered. The snapshot method is used to retrieve the parameter value in the ngOnInit. As a result, our component does not automatically update.

Subscribing to the observable params property will solve this problem. When the value of the parameter changes, our component will be alerted. So that we can make the necessary changes to the component.

Using the Subscribe method to retrieve the parameters in child routes

Change the ngOnInit method in product-detail.component.ts to subscribe to the params property as shown below.

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();
}

As you click on another product, the ProductDetailComponents will automatically change.

Nesting Children under a child

A child route can have child routes added to it.

What if we wanted to include a Product Overview & Specification section on the Product Details Page? The following is a diagram of our component tree.


Component tree Child Route In Angular


Defining the child Route

First, under the Product/Details route, we must add three child routes as shown below.

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 }
];

The first two child routes are rather straightforward. The ProductOverviewComponent is related to the 'overview' URL path, while the ProductSpecComponent is associated with the spec URL path.

'/product/detail/:id/overview' and '/product/detail/:id/spec' would be the Urls.

The final route is an empty path that leads to the 'Overview' route. PathMatch is set to 'full' in this case.

Mapping the action to View

Below is the revised code for the Product Details page.

<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

The ProductOverviewComponent just displays the message "Overview of <Product Name>"

The method for retrieving the product id from the route is the most crucial. We're subscribing to the parent component's params array.

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();
  }
  
}

Finally, remember to include both components in app.routes.ts and app.module.ts.

Conclusion

In this article, we learned how to use angular to create child routes.

I hope this article helps you and you will like it.👍

If you have any doubt or confusion then free to ask in the comment section.

Post a Comment

Previous Post Next Post