Routing Basics
Angular routing enables users to navigate between different views of an application without reloading the page. It is component-based and provides a robust API for defining routing rules, handling navigation logic, and passing parameters.
Installing and Importing RouterModule
Ensure the @angular/router package is installed in your Angular project, then import RouterModule and Routes in the root module.
// app.module.ts
import { RouterModule, Routes } from '@angular/router';
const appRoutes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'contact', component: ContactComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes),
// Other modules...
],
// ...
})
export class AppModule { }Setting Up the Router Outlet
In app.component.html, use the <router-outlet> tag to specify where routed content should be displayed.
<!-- app.component.html -->
<header>
<!-- Navigation menu -->
</header>
<main>
<router-outlet></router-outlet>
</main>
<footer>
<!-- Footer -->
</footer>Configuring Route Parameters
Route parameters include path parameters (:id) and query parameters (?queryParam=value). Define path parameters in the route configuration with a colon prefix.
const appRoutes: Routes = [
{ path: 'user/:id', component: UserComponent },
// ...
];Access these parameters in a component using the ActivatedRoute service.
// user.component.ts
import { ActivatedRoute } from '@angular/router';
constructor(private route: ActivatedRoute) {
this.route.params.subscribe(params => {
const userId = params['id'];
// ...
});
}Route Guards
Route guards control access to routes, with common guards including CanActivate and CanDeactivate.
// auth.guard.ts
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.isLoggedIn() ? true : this.router.createUrlTree(['/login']);
}
// Apply the guard in route configuration
const routes: Routes = [
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] },
// ...
];Child Routes
Child routes allow defining a subset of routes within a parent component.
const appRoutes: Routes = [
{ path: 'dashboard', component: DashboardComponent, children: [
{ path: 'settings', component: SettingsComponent },
// ...
]},
// ...
];Use a nested <router-outlet> in the parent component.
<!-- dashboard.component.html -->
<h2>Dashboard</h2>
<router-outlet></router-outlet>Route Redirects and Empty Paths
- Redirect: Use the
redirectToproperty. - Empty Path: Typically used for the default route.
const appRoutes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
// ...
];Route Events and Navigation
Use the Router service for manual navigation or to listen to route events.
// Manual navigation
this.router.navigate(['/about']);
// Listen to route events
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
console.log('Navigation ended', event.urlAfterRedirects);
}
});Dynamic Routing and Route Activation
Dynamically generate route configurations or use RouteReuseStrategy to control component instance reuse.
Route Animations
Combine with @angular/animations to add transition effects for route changes.
Route Preloading
Preloading strategies load components at application startup or before navigation, improving user experience by reducing wait times.
// app.routing.module.ts
import { PreloadAllModules, Route } from '@angular/router';
const appRoutes: Routes = [
// Route configurations...
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes, { preloadingStrategy: PreloadAllModules })
],
exports: [RouterModule]
})
export class AppRoutingModule { }Lazy Loading
Lazy loading delays module loading until the user navigates to the corresponding route, reducing initial load time and improving performance.
{
path: 'lazy',
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
}Route Metadata
Add metadata to route configurations for extra information, such as titles or descriptions.
const routes: Routes = [
{
path: 'product',
component: ProductComponent,
data: { title: 'Product Page', description: 'View our products.' }
},
// ...
];Router Link Directive
Use the routerLink directive to create navigation links without JavaScript.
<a routerLink="/about">About Us</a>Route Resolvers
Resolvers fetch data before activating a route, ensuring components have necessary data upon rendering.
// product.resolver.ts
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { ProductService } from './product.service';
@Injectable()
export class ProductResolver implements Resolve<Product> {
constructor(private productService: ProductService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Product> {
return this.productService.getProductById(route.params['id']);
}
}Apply the Resolver in Route Configuration:
const routes: Routes = [
{
path: 'product/:id',
component: ProductComponent,
resolve: { product: ProductResolver }
},
// ...
];Parameter Passing and Route Guards
Parameter Passing
Path Parameters
Path parameters are embedded in the URL to identify specific resources, e.g., /users/:userId where :userId is the parameter.
// app.routing.module.ts
const routes: Routes = [
{ path: 'users/:userId', component: UserDetailComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }Access path parameters in a component using ActivatedRoute.
// user-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
template: `<h1>User Detail: {{ userId }}</h1>`
})
export class UserDetailComponent implements OnInit {
userId: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.userId = this.route.snapshot.paramMap.get('userId');
}
}Query Parameters
Query parameters appear in the URL after a question mark, e.g., /search?query=angular, where query is the parameter.
// search.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-search',
template: `<h1>Search Results for: {{ query }}</h1>`
})
export class SearchComponent implements OnInit {
query: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.query = this.route.snapshot.queryParams['query'];
}
}Route Data
Route data is metadata attached to route configurations, useful for component initialization.
// app.routing.module.ts
const routes: Routes = [
{
path: 'about',
component: AboutComponent,
data: { title: 'About Us' }
}
];Access route data via ActivatedRoute’s data property.
// about.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-about',
template: `<h1>{{ title }}</h1>`
})
export class AboutComponent implements OnInit {
title: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.title = this.route.snapshot.data['title'];
}
}Route Guards
Route guards control access to specific routes, with the following types:
CanActivate: Runs before route activation to protect routes.CanDeactivate: Runs before leaving a route to confirm navigation.Resolve: Runs before route activation to preload data.
CanActivate
// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.authService.isLoggedIn()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}Apply the Guard in Route Configuration:
// app.routing.module.ts
const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard]
}
];CanDeactivate
// can-deactivate.guard.ts
import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { CanDeactivateComponent } from './can-deactivate.component';
@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanDeactivateComponent> {
canDeactivate(component: CanDeactivateComponent, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): boolean {
return component.canDeactivate();
}
}Implement the CanDeactivate Interface in the Component:
// can-deactivate.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-can-deactivate',
template: '<h1>Can Deactivate Component</h1>'
})
export class CanDeactivateComponent {
canDeactivate(): boolean {
return window.confirm('Are you sure you want to leave?');
}
}Apply the Guard in Route Configuration:
// app.routing.module.ts
const routes: Routes = [
{
path: 'can-deactivate',
component: CanDeactivateComponent,
canDeactivate: [CanDeactivateGuard]
}
];Resolve
// data-resolver.service.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { DataResolverService } from './data-resolver.service';
@Injectable()
export class DataResolver implements Resolve<any> {
constructor(private dataResolverService: DataResolverService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
return this.dataResolverService.getData();
}
}Apply the Resolver in Route Configuration:
// app.routing.module.ts
const routes: Routes = [
{
path: 'data',
component: DataComponent,
resolve: {
data: DataResolver
}
}
];Access Resolved Data in the Component:
// data.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-data',
template: '<h1>Data: {{ data }}</h1>'
})
export class DataComponent implements OnInit {
data: any;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.data = this.route.snapshot.data['data'];
}
}Dynamic Routing and Nested Routing
Dynamic Routing
Dynamic routing enables loading components based on URL parameters, commonly used for displaying resources like user profiles or product details.
Configuring Dynamic Routes
Dynamic routes use path parameters, denoted by a colon prefix, e.g., /:id.
// app.routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserDetailComponent } from './user-detail/user-detail.component';
const routes: Routes = [
{ path: 'users/:id', component: UserDetailComponent },
// Other routes...
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }Accessing Dynamic Parameters
Access path parameters in a component using ActivatedRoute.
// user-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
templateUrl: './user-detail.component.html',
styleUrls: ['./user-detail.component.css']
})
export class UserDetailComponent implements OnInit {
userId: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.userId = this.route.snapshot.paramMap.get('id');
}
}Nested Routing
Nested routing allows defining multiple routes within a parent component, enabling multi-level navigation structures, such as “Settings” and “Profile” within a “Dashboard” component.
Configuring Nested Routes
Use the children property in the parent route configuration to define child routes.
// app.routing.module.ts
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
children: [
{ path: 'settings', component: SettingsComponent },
{ path: 'profile', component: ProfileComponent }
]
},
// Other routes...
];Using Nested Routes
Render child route content in the parent component using <router-outlet>.
<!-- dashboard.component.html -->
<div class="dashboard">
<nav>
<a routerLink="/dashboard/settings">Settings</a>
<a routerLink="/dashboard/profile">Profile</a>
</nav>
<router-outlet></router-outlet>
</div>Combining Dynamic Loading with Nested Routes
Combining lazy loading with nested routes enhances performance by loading components only when needed.
// app.routing.module.ts
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule),
children: [
{ path: 'settings', component: SettingsComponent },
{ path: 'profile', component: ProfileComponent }
]
},
// Other routes...
];Complex Nested Routing
In large applications, multi-level nested routes may be required. For example, a “Users” section might include “List,” “Details,” and “Edit” pages, with “Details” containing “Photos” and “Comments.”
const routes: Routes = [
{
path: 'users',
component: UsersComponent,
children: [
{ path: '', component: UsersListComponent },
{
path: ':id',
component: UserDetailsComponent,
children: [
{ path: 'photos', component: PhotosComponent },
{ path: 'comments', component: CommentsComponent }
]
},
{ path: ':id/edit', component: EditUserComponent }
]
},
// Other routes...
];Route Redirects
Redirect users from one route to another, e.g., redirecting unauthenticated users to a login page.
const routes: Routes = [
{
path: 'protected',
component: ProtectedComponent,
canActivate: [AuthGuard],
children: [
{ path: 'details', component: DetailsComponent }
]
},
{ path: '', redirectTo: '/login', pathMatch: 'full' }
];Route Guards with Dynamic Routes
Combine route guards with dynamic routes for fine-grained access control, e.g., restricting user edit pages to administrators.
const routes: Routes = [
{
path: 'admin/users/:id/edit',
component: EditUserComponent,
canActivate: [AdminGuard]
}
];Utilizing Route Data
Route data is a powerful feature for passing additional information to components, such as page titles or SEO metadata.
const routes: Routes = [
{
path: 'product/:id',
component: ProductComponent,
data: { title: 'Product Detail', breadcrumb: 'Product' }
}
];Access route data via ActivatedRoute’s data property.
// product.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-product',
templateUrl: './product.component.html'
})
export class ProductComponent implements OnInit {
title: string;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.title = this.route.snapshot.data['title'];
}
}Route Resolvers
Resolvers preload data before component activation, improving user experience by avoiding delays from asynchronous data requests.
// product.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { ProductService } from './product.service';
@Injectable()
export class ProductResolver implements Resolve<Product> {
constructor(private productService: ProductService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Product {
return this.productService.getProductById(route.params['id']);
}
}Apply the Resolver in Route Configuration:
const routes: Routes = [
{
path: 'product/:id',
component: ProductComponent,
resolve: { product: ProductResolver }
}
];



