Angular Child Routes and Relative Navigation Example

By Arvind Rai, August 27, 2021
This page will walk through Angular child routes and relative navigation example. A route can have one or more than one child routes. Child routes can have their own sub-child routes and so on. A route is created using Angular Route interface and array of Route is of Routes type. Route has different properties to handle route such as path, component, outlet, children, loadChildren etc. To create child route for a route, we need to use children property. To load child module lazily in application module, we need to use loadChildren property. Module can be loaded eagerly, lazily and preloaded. In lazy loading and preloading, modules are loaded asynchronously. The component corresponding to a child route will open in router outlet of a component related to immediate parent route of the given child route. Child can have path parameters and also it can have empty path. Router supports relative path using ActivatedRoute. When we navigate to a route using Router.navigate() method then we need to pass instance of ActivatedRoute using relativeTo property. When we navigate to a route using RouterLink directive, we need not to pass instance of ActivatedRoute because RouterLink automatically supports ActivatedRoute.
In our example we will create two features, country feature and person feature. Both features will have its own folder, feature module and feature routing module that will follow "Separation of Concerns principle" recommended by Angular. Now we will discuss complete example step-by-step.

Technologies Used

Find the technologies being used in our example.
1. Angular 12.1.0
2. Node.js 12.14.1
3. NPM 7.20.3

Understanding Child Routes

Suppose base URL is "/" configured by <base href="/"> in index.html. We will create a diagram that will display the child routes for a component. The components shown in diagram will be used in our demo application.
Angular Child Routes and Relative Navigation Example
Find the child routes corresponding to the components.
1. URL: /
Components: AppComponent

2. URL: /country
Components: AppComponent -> CountryComponent

3. URL: /country/add
Components: AppComponent -> CountryComponent -> AddCountryComponent

4. URL: /country/list
Components: AppComponent -> CountryComponent -> CountryListComponent

5. URL: /country/list/view/1
Components: AppComponent -> CountryComponent -> CountryListComponent -> CountryDetailComponent

6. URL: /country/list/edit/1
Components: AppComponent -> CountryComponent -> CountryListComponent -> CountryEditComponent

7. URL: /person
Component: AppComponent -> PersonComponent -> PersonListComponent

8. URL: /person/1
Component: AppComponent -> PersonComponent -> PersonListComponent -> PersonEditComponent

9. URL: /address
Component: AppComponent -> AddressComponent

Child Router Outlet

Find the diagram that shows child router outlet. We are using all components of our example in the diagram and by arrow we are showing which component will open in which component's <router-outlet>.
Angular Child Routes and Relative Navigation Example
A component related to a child route is opened in <router-outlet> of a component related to immediate parent route. For example find the following components with their child routes.
1. Component: CountryComponent, URL: /country
2. Component: CountryListComponent, URL: /country/list
3. Component: CountryDetailComponent, URL: /country/list/view/1

We observe that path of CountryListComponent is the immediate child route of CountryComponent and hence CountryListComponent will open in the <router-outlet> of CountryComponent. In the same way the path of CountryDetailComponent is the immediate child route of CountryListComponent and hence CountryDetailComponent will open in the <router-outlet> of CountryListComponent.

Properties of Angular Routes Array

Routes is an array of Angular Route interface. Route is used to define a path. Find the properties of Route interface.
path: Defines path for a component.
component: Name of the component.
outlet: Name of the outlet component should be placed into.
data: Additional data provided to component via ActivatedRoute.
children: Array of child route definitions.
loadChildren: Reference to lazy loaded child routes.
redirectTo: Defines a URL to redirect current matched URL.

Find the sample examples.
1. Simple configuration with children property.
const countryRoutes: Routes = [
    { 
	 path: 'country',
         component: CountryComponent,
         children: [ 
	    {
		path: 'add',
		component: AddCountryComponent
	    }
         ]
    }
] 
2. Using redirectTo property.
[{ 
    path: 'country',
    component: CountryComponent,
    children: [ 
      {
	path: 'add',
	component: AddCountryComponent
      },
      {
	path: 'item/create',
	redirectTo: 'add'
      }
    ]
}] 

Child Routes Module

While creating our angular applications, we should follow angular recommended pattern as given below.
1. Each feature area should reside in its own folder.
2. Each feature should have its own feature module as well as feature routing module.
3. Each area should have its own area root component.
4. Each area root component should have its own router outlet and child routes.

Suppose we have two basic features in our demo application such as country and person, then we should use following folder structure and modules.
angular-demo
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--country
|   |   |    | 
|   |   |    |--........
|   |   |    |--country-routing.module.ts (CountryRoutingModule) 
|   |   |    |--country.module.ts (CountryModule)
|   |   |
|   |   |--person
|   |   |    | 
|   |   |    |--........
|   |   |    |--person-routing.module.ts (PersonRoutingModule)
|   |   |    |--person.module.ts (PersonModule)
|   |   |
|   |   |--........
|   |   |--app-routing.module.ts (AppRoutingModule)
|   |   |--app.module.ts (AppModule) 

Creating and Configuring Child Routes

Child routes are created using children property of Angular Route interface. Child routes are configured using forChild() method of RouterModule. Suppose we have country feature. Now find the routing module for this feature.
country-routing.module.ts
const countryRoutes: Routes = [
	{ 
	  path: 'country',
          component: CountryComponent,
          children: [ 
	    {
	       path: 'add',
	       component: AddCountryComponent
	    },
	    {
	       path: 'list',
	       component: CountryListComponent,
	       children: [
		   {
		       path: 'view/:country-id',
		       component: CountryDetailComponent
		   },
		   {
		       path: 'edit/:country-id',
		       component: CountryEditComponent
		   },			   
		]
	     }	
	   ]
	}  
];

@NgModule({
  imports: [ RouterModule.forChild(countryRoutes) ],
  exports: [ RouterModule ]
})
export class CountryRoutingModule{ } 
Find the route description.
1. URL: /country
Components: AppComponent -> CountryComponent

2. URL: /country/add
Components: AppComponent -> CountryComponent -> AddCountryComponent

3. URL: /country/list
Components: AppComponent -> CountryComponent -> CountryListComponent

4. URL: /country/list/view/1
Components: AppComponent -> CountryComponent -> CountryListComponent -> CountryDetailComponent

5. URL: /country/list/edit/1
Components: AppComponent -> CountryComponent -> CountryListComponent -> CountryEditComponent

Child Routes with Parameters

To create routes with path parameter, define the path as given below.
country-routing.module.ts
const countryRoutes: Routes = [
	{ 
	  path: 'country',
          component: CountryComponent,
          children: [ 
	    {
	       path: 'list',
	       component: CountryListComponent,
	       children: [
		   {
		       path: 'view/:country-id',
		       component: CountryDetailComponent
		   }
		]
	     }	
	   ]
	}  
]; 
The CountryDetailComponent will open for a country id. country-id is a path variable that will accept country id. Find the URL for country id 101.
/country/list/view/101 
This URL will be accepted by following path.
{
       path: 'view/:country-id',
       component: CountryDetailComponent
}

Empty Child Routes

A route can be empty. If we want to open a component by default then we can leave route empty. Find the example.
person-routing.module.ts
const personRoutes: Routes = [
   { 
	  path: 'person',
          component: PersonComponent,
	  children: [ 
	    {
		 path: '',
		 component: PersonListComponent,
		 children: [
		   {
		      path: ':id',
		      component: PersonEditComponent
		   }
		 ]			
	    }
	 ]
   }  
]; 

@NgModule({
  imports: [ RouterModule.forChild(personRoutes) ],
  exports: [ RouterModule ]
})
export class PersonRoutingModule{ } 
The path for PersonListComponent is empty. So the path for PersonListComponent will be calculated as given below. /person + '' = /person
Find the description of routes now.
1. URL: /person
Components: AppComponent -> PersonComponent -> PersonListComponent
2. URL: /person/1
Components: AppComponent -> PersonComponent -> PersonEditComponent

Eager Loading of Modules

Eager loading is loading modules before application starts. Our main application module (i.e. AppModule) always loads eagerly. To load feature modules eagerly we need to import them into AppModule. We have two feature modules in our example i.e. CountryModule and PersonModule. We will import them into AppModule as following.
app.module.ts
import { CountryModule }  from './country/country.module';
import { PersonModule }  from './person/person.module';

@NgModule({
  imports: [     
        ------
	CountryModule,
	PersonModule,
  ],
  ------
})
export class AppModule { } 
Our AppRoutingModule will be as following.
app-routing.module.ts
const routes: Routes = [
	{
	   path: 'address',
	   component: AddressComponent
	},	
	{
	   path: '',
	   redirectTo: '/country',
	   pathMatch: 'full'
	},
        {
	   path: '**',
	   component: PageNotFoundComponent 
        }	
];
@NgModule({
  imports: [ 
          RouterModule.forRoot(routes) 
  ],
  exports: [ 
          RouterModule 
  ]
})
export class AppRoutingModule{ } 
To load routes in main application routing module, we need to use forRoot as RouterModule.forRoot(routes).

Lazy Loading of Modules

Lazy loading is loading modules on demand. To load modules lazily we need to use loadChildren property. Find the steps to load our CountryModule and PersonModule lazily.
Step-1: Do not import CountryModule and PersonModule in AppModule.
Step-2: Use loadChildren property in AppRoutingModule as following.
app-routing.module.ts
const routes: Routes = [
	{
	   path: 'country',
           loadChildren: () => import('./country/country.module').then(mod => mod.CountryModule)
	},
	{
	   path: 'person',
           loadChildren: () => import('./person/person.module').then(mod => mod.PersonModule)
	},
	{
	   path: 'address',
	   component: AddressComponent
	},	
	{
	   path: '',
	   redirectTo: '/country',
	   pathMatch: 'full'
	},
        {
	   path: '**',
	   component: PageNotFoundComponent 
        }	
];
@NgModule({
  imports: [ 
          RouterModule.forRoot(routes) 
  ],
  exports: [ 
          RouterModule 
  ]
})
export class AppRoutingModule{ } 
Step-3: Make parent path empty in PersonRoutingModule and CountryRoutingModule.
country-routing.module.ts
const countryRoutes: Routes = [
	{ 
	  path: '',
          component: CountryComponent,
          children: [ 
	    {
		 path: 'add',
		 component: AddCountryComponent
	    },
	    {
	         path: 'list',
		 component: CountryListComponent,
		 children: [
		    {
			path: 'view/:country-id',
		        component: CountryDetailComponent
		    },
		    {
		        path: 'edit/:country-id',
		        component: CountryEditComponent
		    }	   
		 ]
	     }	
	  ]
	}  
]; 
person-routing.module.ts
const personRoutes: Routes = [
	{ 
	  path: '',
          component: PersonComponent,
	  children: [ 
	    {
		path: '',
		component: PersonListComponent,
		children: [
		   {
		      path: ':id',
		      component: PersonEditComponent
		   }
		]			
	    }
	  ]
	}  
]; 

Preloading of Modules

Preloading is loading modules in background just after application starts. To preload modules we need do a slight change in our lazy loading module code. Find steps to preload CountryModule and PersonModule.
Step-1: Do not import CountryModule and PersonModule in AppModule.
Step-2: Use loadChildren property in AppRoutingModule as we did in lazy loading.
Step-3: Configure PreloadAllModules strategy using forRoot as following.
RouterModule.forRoot(routes,
   {
     preloadingStrategy: PreloadAllModules
   }) 
PreloadAllModules is imported from @angular/router library. PreloadAllModules preloads all modules that are configured with loadChildren property. It means CountryModule and PersonModule both will be preloaded. Our AppRoutingModule will look like as following.
app-routing.module.ts
const routes: Routes = [
	{
	   path: 'country',
           loadChildren: () => import('./country/country.module').then(mod => mod.CountryModule)
	},
	{
	   path: 'person',
           loadChildren: () => import('./person/person.module').then(mod => mod.PersonModule)
	},
	{
	   path: 'address',
	   component: AddressComponent
	},	
	{
	   path: '',
	   redirectTo: '/country',
	   pathMatch: 'full'
	},
        {
	   path: '**',
	   component: PageNotFoundComponent 
        }	
];
@NgModule({
  imports: [ 
	RouterModule.forRoot(routes,
	   {
		  preloadingStrategy: PreloadAllModules
	   })
  ],
  exports: [ 
        RouterModule 
  ]
})
export class AppRoutingModule{ } 
Angular router supports relative navigation. For route name lookup, we use directory-like syntax in our path.
./ or no leading slash is relative to current level.
../ is used to go up one level in path.

Now we will understand relative navigation using Router.navigate() method and RouterLink directive.

1. Using Router.navigate() method

To support relative navigation to Router.navigate(), we need to pass ActivatedRoute using which router comes to know where it is in current route tree. To pass ActivatedRoute we need to use relativeTo property. To get the instance of ActivatedRoute and Router use the constructor.
constructor(
	private countryService: CountryService,
	private route: ActivatedRoute,
        private router: Router) { } 
Now suppose a component has following source and target route.
Source route from where the component has opened: /country/add
Target route to navigate: /country/list/view/101

Now Router.navigate() will be used as follows.
goToView(country:Country) {
     this.router.navigate([ '../list/view', country.countryId ], { relativeTo: this.route });
}
If we want to use absolute path, then write the full path.
this.router.navigate(['/country/list/view', country.countryId]); 
Disadvantage of absolute path is that if the path /country is changed then we need to edit our component code to change the path, too.

2. Using RouterLink Directive

RouterLink directive has support of ActivatedRoute automatically for relative navigation. We can use relative path and absolute path both with RouterLink directive.
Now suppose a component has following source and target route.
Source route from where the component has opened: /country/add
Target route to navigate: /country/list/view/101
Using relative path:
<a [routerLink]="['../list/view', country.countryId]" routerLinkActive="active">View</a>
Using absolute path:
<a [routerLink]="['/country/list/view', country.countryId]" routerLinkActive="active">View</a>

Complete Example

In our example we have two features. Every feature resides in its own folder with its associated files. We will provide code feature wise. We will load our feature modules eagerly.

1. Project Structure

Find the project structure of our application.
angular-demo
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--country
|   |   |    | 
|   |   |    |--country.component.ts
|   |   |    |--country.ts
|   |   |    |--country.module.ts
|   |   |    |--country-routing.module.ts
|   |   |    |
|   |   |    |--add-country
|   |   |    |    |  
|   |   |    |    |--add-country.component.html
|   |   |    |    |--add-country.component.ts
|   |   |    |    
|   |   |    |--country-list
|   |   |    |    |
|   |   |    |    |--country.list.component.html
|   |   |    |    |--country.list.component.ts
|   |   |    |    |
|   |   |    |    |--detail
|   |   |    |    |   |
|   |   |    |    |   |--country.detail.component.html
|   |   |    |    |   |--country.detail.component.ts
|   |   |    |    |
|   |   |    |    |--edit
|   |   |    |    |   |
|   |   |    |    |   |--country.edit.component.html
|   |   |    |    |   |--country.edit.component.ts
|   |   |    |    |
|   |   |    |    
|   |   |    |--service
|   |   |    |    |
|   |   |    |    |--country.service.ts
|   |   |
|   |   |--person
|   |   |    | 
|   |   |    |--person.component.ts
|   |   |    |--person.ts
|   |   |    |--person-routing.module.ts
|   |   |    |--person.module.ts
|   |   |    | 
|   |   |    |--person-list 
|   |   |    |    |
|   |   |    |    |--person.list.component.html 
|   |   |    |    |--person.list.component.ts
|   |   |    |    |
|   |   |    |    |--edit
|   |   |    |    |   |
|   |   |    |    |   |--person.edit.component.html
|   |   |    |    |   |--person.edit.component.ts
|   |   |    | 
|   |   |    |--service
|   |   |    |    |  
|   |   |    |    |--person.service.ts
|   |   |
|   |   |--address.component.ts
|   |   |--page-not-found.component.ts
|   |   |--app.component.ts
|   |   |--app-routing.module.ts
|   |   |--app.module.ts
|   |   
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json

2. Feature One Complete Code of Demo Application

Here we will provide complete code of country feature.
add-country.component.html
<h3>Add Country</h3>
<form [formGroup]="countryForm" (ngSubmit)="onFormSubmit()">
   <p> Name: <input formControlName="name"> </p>
   <p> Capital: <input formControlName="capital"> </p>
   <p> Currency: <input formControlName="currency"> </p>
   <p> <button>Add</button> </p>
</form> 
add-country.component.ts
import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

import { CountryService } from '../service/country.service';
import { Country } from '../country';
@Component({
	templateUrl: './add-country.component.html'
})
export class AddCountryComponent {
	constructor(
		private countryService: CountryService,
		private route: ActivatedRoute,
		private router: Router) { }

	countryForm = new FormGroup({
		name: new FormControl(),
		capital: new FormControl(),
		currency: new FormControl()
	});
	onFormSubmit() {
		const name = this.countryForm.get('name')?.value;
		const capital = this.countryForm.get('capital')?.value;
		const currency = this.countryForm.get('currency')?.value;

		const country = new Country(0, name, capital, currency);
		this.countryService.addCountry(country)
			.then(data =>
				this.router.navigate(['../list/view', data.countryId], { relativeTo: this.route })
			);
	}
} 
country.detail.component.html
<h3>Country Detail </h3>
<div *ngIf="country">
<p><b>Id:</b> {{country.countryId}},  
<b>Name:</b> {{country.countryName}} </p>
<p><b>Capital:</b> {{country.capital}},
<b>Currency:</b> {{country.currency}}</p>
</div> 
country.detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { switchMap } from 'rxjs/operators';

import { CountryService } from '../../service/country.service';
import { Country } from '../../country';

@Component({
  templateUrl: './country.detail.component.html'
})
export class CountryDetailComponent implements OnInit {
  country = {} as Country | undefined;
  constructor(
    private countryService: CountryService,
    private route: ActivatedRoute) { }
  ngOnInit() {
    this.route.params.pipe(
      switchMap((params: Params) => this.countryService.getCountry(+params['country-id']))
    ).subscribe(country => this.country = country);
  }
} 
country.edit.component.html
<h3>Edit Country</h3>
<p *ngIf="country"><b>Country Id: {{country.countryId }} </b></p>
<form [formGroup]="countryForm" (ngSubmit)="onFormSubmit()">
   <p> Name: <input formControlName="name"> </p>
   <p> Capital: <input formControlName="capital"> </p>
   <p> Currency: <input formControlName="currency"> </p>
   <p> <button>Update</button> </p>
</form>  
country.edit.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { FormControl, FormGroup } from '@angular/forms';
import { switchMap } from 'rxjs/operators';

import { CountryService } from '../../service/country.service';
import { Country } from '../../country';

@Component({
	templateUrl: './country.edit.component.html'
})
export class CountryEditComponent implements OnInit {
	country = {} as Country;
	constructor(
		private countryService: CountryService,
		private route: ActivatedRoute,
		private router: Router) { }

	ngOnInit() {
		this.route.params.pipe(
			switchMap((params: Params) => this.countryService.getCountry(+params['country-id']))
		).subscribe(country => {
			this.country = country ?? {} as Country;
			this.setFormValues();
		});
	}
	countryForm = new FormGroup({
		name: new FormControl(),
		capital: new FormControl(),
		currency: new FormControl()
	});
	setFormValues() {
		this.countryForm.setValue({
			name: this.country.countryName,
			capital: this.country.capital, currency: this.country.currency
		});
	}
	onFormSubmit() {
		this.country.countryName = this.countryForm.get('name')?.value;
		this.country.capital = this.countryForm.get('capital')?.value;
		this.country.currency = this.countryForm.get('currency')?.value;

		this.countryService.updateCountry(this.country)
			.then(() =>
				this.router.navigate(['../../'], { relativeTo: this.route })
			);
	}
} 
country.list.component.html
<h3>Country List</h3>
<div *ngFor="let country of countries | async" [ngClass]= "'sub-child-menu'">
 <p>{{country.countryId}}. {{country.countryName}}
	<a [routerLink]="['view', country.countryId]" routerLinkActive="active">View</a> |
	<a [routerLink]="['edit', country.countryId]" routerLinkActive="active">Edit</a>
 </p>
</div>
<div [ngClass]= "'sub-child-container'">
	<router-outlet></router-outlet>  
</div> 
country.list.component.ts
import { Component, OnInit } from '@angular/core';

import { CountryService } from '../service/country.service';
import { Country } from '../country';

@Component({
  templateUrl: './country.list.component.html'
})
export class CountryListComponent implements OnInit {
  countries: Promise<Country[]>;
  constructor(private countryService: CountryService) {
    this.countries = this.countryService.getCountries();
  }
  ngOnInit() {
  }
} 
country.service.ts
import { Injectable } from '@angular/core';

import { Country } from '../country';

const COUNTRIES = [
	new Country(1, 'India', 'New Delhi', 'INR'),
	new Country(2, 'China', 'Beijing', 'RMB')
];
let countriesPromise = Promise.resolve(COUNTRIES);

@Injectable()
export class CountryService {
	getCountries() {
		return countriesPromise;
	}
	getCountry(id: number) {
		return this.getCountries()
			.then(countries => countries.find(country => country.countryId === id));
	}
	updateCountry(country: Country) {
		return this.getCountries()
			.then(countries => {
				let countryObj = countries.find(ob => ob.countryId === country.countryId);
				countryObj = country;
				return countryObj;
			});
	}
	addCountry(country: Country) {
		return this.getCountries()
			.then(countries => {
				let maxIndex = countries.length - 1;
				let countryWithMaxIndex = countries[maxIndex];
				country.countryId = countryWithMaxIndex.countryId + 1;
				countries.push(country);
				return country;
			}
			);
	}
} 
country.ts
export class Country { 
	constructor(public countryId:number, public countryName:string,
            	public capital:string, public currency:string) {
	}
} 
country.component.ts
import { Component } from '@angular/core';
@Component({
  template: `<h2>Welcome to Country Home</h2>
   <nav [ngClass] = "'child-menu'">
      <ul>
	<li><a [routerLink]="['add']" routerLinkActive="active">Add Country</a></li>
	<li><a [routerLink]="['list']" routerLinkActive="active">Country List</a></li>
      </ul>  
   </nav>  
   <div [ngClass] = "'child-container'">	
	  <router-outlet></router-outlet>	
   </div>
  `
})
export class CountryComponent { 
} 
country-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { CountryComponent }  from './country.component';
import { CountryListComponent }  from './country-list/country.list.component';
import { AddCountryComponent }  from './add-country/add-country.component';
import { CountryDetailComponent }  from './country-list/detail/country.detail.component';
import { CountryEditComponent }  from './country-list/edit/country.edit.component';

const countryRoutes: Routes = [
	{ 
	  path: 'country',
          component: CountryComponent,
          children: [ 
	    {
		   path: 'add',
		   component: AddCountryComponent
	    },
	    {
	           path: 'list',
		   component: CountryListComponent,
		   children: [
		       {
			   path: 'view/:country-id',
		           component: CountryDetailComponent
		       },
		       {
			   path: 'edit/:country-id',
		           component: CountryEditComponent
		       },			   
		   ]
	    }	
	  ]
	}  
];

@NgModule({
  imports: [ RouterModule.forChild(countryRoutes) ],
  exports: [ RouterModule ]
})
export class CountryRoutingModule{ } 
country.module.ts
import { NgModule }   from '@angular/core';
import { CommonModule }   from '@angular/common';
import { ReactiveFormsModule }    from '@angular/forms';

import { CountryComponent }  from './country.component';
import { AddCountryComponent }  from './add-country/add-country.component';
import { CountryListComponent }  from './country-list/country.list.component';
import { CountryDetailComponent }  from './country-list/detail/country.detail.component';
import { CountryEditComponent }  from './country-list/edit/country.edit.component';
import { CountryService } from './service/country.service';
import { CountryRoutingModule }  from './country-routing.module';

@NgModule({
  imports: [     
        CommonModule,
	ReactiveFormsModule,
	CountryRoutingModule
  ], 
  declarations: [
	CountryComponent,
	AddCountryComponent,
	CountryListComponent,
	CountryEditComponent,
	CountryDetailComponent
  ],
  providers: [ CountryService ]
})
export class CountryModule { } 
CommonModule includes all the basic Angular directives such as NgIf, NgFor, NgClass etc. Now find the print screen of feature one output.
Angular Child Routes and Relative Navigation Example

3. Feature Two Complete Code of Demo Application

Here we will provide complete code of person feature.
person.edit.component.html
<h3>Edit Person</h3>
<p *ngIf="person"><b>Person Id: {{person.personId }} </b></p>
<form [formGroup]="personForm" (ngSubmit)="onFormSubmit()">
   <p> Name: <input formControlName="name"> </p>
   <p> City: <input formControlName="city"> </p>
   <p> <button>Update</button> </p>
</form> 
person.edit.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { FormControl, FormGroup } from '@angular/forms';
import { switchMap } from 'rxjs/operators';

import { PersonService } from '../../service/person.service';
import { Person } from '../../person';

@Component({
	templateUrl: './person.edit.component.html'
})
export class PersonEditComponent implements OnInit {
	person = {} as Person;
	constructor(
		private personService: PersonService,
		private route: ActivatedRoute,
		private router: Router) { }

	ngOnInit() {
		this.route.params.pipe(
			switchMap((params: Params) => this.personService.getPerson(+params['id']))
		).subscribe(person => {
			this.person = person ?? {} as Person;
			this.setFormValues();
		});
	}
	personForm = new FormGroup({
		name: new FormControl(),
		city: new FormControl()
	});
	setFormValues() {
		this.personForm.setValue({ name: this.person.name, city: this.person.city });
	}
	onFormSubmit() {
		this.person.name = this.personForm.get('name')?.value;
		this.person.city = this.personForm.get('city')?.value;

		this.personService.updatePerson(this.person)
			.then(() =>
				this.router.navigate(['../'], { relativeTo: this.route })
			);
	}
} 
person.list.component.html
<h3>Person List</h3>
<div *ngFor="let person of persons | async" [ngClass]= "'sub-child-menu'">
<p>{{person.personId}}. {{person.name}}, {{person.city}}
   <button type="button" (click)="goToEdit(person)">Edit</button>
</p>
</div>
<div [ngClass]= "'sub-child-container'">
	<router-outlet></router-outlet>  
</div>  
person.list.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';

import { PersonService } from '../service/person.service';
import { Person } from '../person';

@Component({
  templateUrl: './person.list.component.html'
})
export class PersonListComponent implements OnInit {
  persons: Promise<Person[]>;
  constructor(
    private personService: PersonService,
    private route: ActivatedRoute,
    private router: Router) {
    this.persons = this.personService.getPersons();
  }
  ngOnInit() {
  }
  goToEdit(person: Person) {
    this.router.navigate([person.personId], { relativeTo: this.route });
  }
} 
person.service.ts
import { Injectable } from '@angular/core';

import { Person } from '../person';

const PERSONS = [
	new Person(1, 'Mahesh', 'Varanasi'),
	new Person(2, 'Ram', 'Ayodhya'),
	new Person(3, 'Kishna', 'Mathura')
];
let personsPromise = Promise.resolve(PERSONS);

@Injectable()
export class PersonService {
	getPersons() {
		return personsPromise;
	}
	getPerson(id: number) {
		return this.getPersons()
			.then(persons => persons.find(person => person.personId === id));
	}
	updatePerson(person: Person) {
		return this.getPersons()
			.then(persons => {
				let personObj = persons.find(ob => ob.personId === person.personId);
				personObj = person;
				return personObj;
			});
	}
} 
person.ts
export class Person { 
	constructor(public personId:number, public name:string, public city:string) {
	}
} 
person.component.ts
import { Component } from '@angular/core';
@Component({
  template: `<h2>Welcome to Person Home</h2>
	     <div [ngClass] = "'child-container'">	
	        <router-outlet></router-outlet>	
	     </div>
  `
})
export class PersonComponent { 
} 
person-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { PersonComponent }  from './person.component';
import { PersonListComponent }  from './person-list/person.list.component';
import { PersonEditComponent }  from './person-list/edit/person.edit.component';

const personRoutes: Routes = [
	{ 
	  path: 'person',
          component: PersonComponent,
	  children: [ 
	    {
		path: '',
		component: PersonListComponent,
		children: [
		   {
		     path: ':id',
		     component: PersonEditComponent
		   }
		]			
	    }
	  ]
	}  
];

@NgModule({
  imports: [ RouterModule.forChild(personRoutes) ],
  exports: [ RouterModule ]
})
export class PersonRoutingModule{ } 
person.module.ts
import { NgModule }   from '@angular/core';
import { CommonModule }   from '@angular/common';
import { ReactiveFormsModule }    from '@angular/forms';

import { PersonComponent }  from './person.component';
import { PersonListComponent }  from './person-list/person.list.component';
import { PersonEditComponent }  from './person-list/edit/person.edit.component';
import { PersonService } from './service/person.service';
import { PersonRoutingModule }  from './person-routing.module';

@NgModule({
  imports: [     
        CommonModule,
	ReactiveFormsModule,
	PersonRoutingModule
  ], 
  declarations: [
	PersonComponent,
	PersonListComponent,
	PersonEditComponent
  ],
  providers: [ PersonService ]
})
export class PersonModule { } 
Find the print screen of feature two output.
Angular Child Routes and Relative Navigation Example

4. Application Module and Other Components

Find application module and other components.
address.component.ts
import { Component } from '@angular/core';
@Component({
  template: `
      <h3>ADDRESS</h3>
          <p><b> Article: Child routing & Relative navigation </b></p>
	  <p><b> Category: Angular </b></p>
	  <p><b> Website: CONCRETEPAGE.COM </b></p>
     <div>
         <a [routerLink]="['/location']">Find Location</a>
     </div> 
  `
})
export class AddressComponent { 
} 
page-not-found.component.ts
import { Component } from '@angular/core';
import { Location } from '@angular/common';

@Component({
  template: `<h2>Page Not Found.</h2>
             <div>
                <button (click)="goBack()">Go Back</button>
	     </div>
            `
})
export class PageNotFoundComponent {
	constructor(private location: Location) { }
	goBack(): void {
           this.location.back();
        }
} 
app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `
	<nav [ngClass] = "'parent-menu'">
	  <ul>
		 <li><a routerLink="/country" routerLinkActive="active">Country</a></li>
		 <li><a routerLink="/person" routerLinkActive="active">Person</a></li>
		 <li><a routerLink="/address" routerLinkActive="active">Address</a></li>
	  </ul> 
	</nav>  
	<div [ngClass] = "'parent-container'">	
	  <router-outlet></router-outlet>	
	</div>
  `
})
export class AppComponent { 
} 
app-routing.module.ts
import { NgModule }      from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AddressComponent }  from './address.component';
import { PageNotFoundComponent }  from './page-not-found.component';

const routes: Routes = [
	{
	   path: 'address',
	   component: AddressComponent
	},	
	{
	   path: '',
	   redirectTo: '/country',
	   pathMatch: 'full'
	},
        {
	   path: '**',
	   component: PageNotFoundComponent 
        }	
];
@NgModule({
  imports: [ 
          RouterModule.forRoot(routes) 
  ],
  exports: [ 
          RouterModule 
  ]
})
export class AppRoutingModule{ }  
app.module.ts
import { NgModule }   from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { AddressComponent }  from './address.component';
import { PageNotFoundComponent }  from './page-not-found.component';

import { CountryModule }  from './country/country.module';
import { PersonModule }  from './person/person.module';
import { AppRoutingModule }  from './app-routing.module';

@NgModule({
  imports: [     
        BrowserModule,
	CountryModule,
	PersonModule,
	AppRoutingModule,
  ],
  declarations: [
        AppComponent,
	AddressComponent,
	PageNotFoundComponent
  ],
  providers: [ ],
  bootstrap: [ AppComponent ]
})
export class AppModule { } 
index.html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Angular Demo</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <app-root>Loading...</app-root>
</body>
</html> 
styles.css
.parent-menu ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #333;
}
.parent-menu li {
    float: left;
}
.parent-menu li a {
    display: block;
    color: white;
    text-align: center;
    padding: 15px 15px;
    text-decoration: none;
}
.parent-menu li a:hover:not(.active) {
    background-color: #111;
}
.parent-menu .active{
    background-color: #4CAF50;
}
.parent-container {
    padding-left: 10px;
}
.child-container {
    padding-left: 10px;
}
.sub-child-container {
    padding-left: 10px;
}
.child-menu  {
    padding-left: 25px;
}
.child-menu .active{
    color: #4CAF50;
}
.sub-child-menu {
    background-color: #f1f1f1;  
    width: 275px;
    list-style-type: none;	
    margin: 0;
    padding: 0;
}
.sub-child-menu .active{
    color: #4CAF50;
}
button {
    background-color: #008CBA;
    color: white;
} 

Run Application

To run the demo application, find following steps.
1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200

References

Routing & Navigation
Angular Routing and Navigation Example

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us