Angular’s routing system is a powerful feature that enable navigation between different components in your application. This guide will walk you though all essential routing concepts with practical example
Basic Route Setup
Enabling Routing
First, you need to setup routing in your angular application. Create a dedicated routing module:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Add the router outlet to your main app component:
// app.component.html
<nav>
<a routerLink="/">Home</a>
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>
Dynamic Routes
Setting Up Dynamic Routes
Dynamic routes allow you to pass parameters in the URL:
const routes: Routes = [
{ path: 'user/:id', component: UserComponent }
];
Extracting Route parameters
You can access route parameters in two ways:
- Using activatedRoute with Inputs:
// user.component.ts
export class UserComponent implements OnInit {
userId: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.userId = this.route.snapshot.params['id'];
}
}
2. Using Observables (recommended for dynamic changes):
export class UserComponent implements OnInit {
userId: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.params.subscribe(params => {
this.userId = params['id'];
});
}
}
Nested Routes
Nested routes allow you to create hierarchical navigation structures:
const routes: Routes = [
{
path: 'users',
component: UsersComponent,
children: [
{ path: ':id', component: UserDetailComponent },
{ path: ':id/edit', component: UserEditComponent }
]
}
];
Parent component template:
<!-- users.component.html -->
<div class="users-container">
<nav>
<a [routerLink]="['/users', user.id]" *ngFor="let user of users">
{{ user.name }}
</a>
</nav>
<router-outlet></router-outlet>
</div>
Query Parameters
Setting Query Parameters
// In template
<a [routerLink]="['/products']" [queryParams]="{ category: 'electronics', sort: 'price' }">
Electronics
</a>
// Programmatically
constructor(private router: Router) {}
navigateToProducts() {
this.router.navigate(['/products'], {
queryParams: { category: 'electronics', sort: 'price' }
});
}
Accessing Query Parameters
export class ProductsComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// Using snapshot
const category = this.route.snapshot.queryParams['category'];
// Using observable
this.route.queryParams.subscribe(params => {
const category = params['category'];
const sort = params['sort'];
});
}
}
Route Guards
Route guards help protect routes based on certain conditions:
// auth.guard.ts
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean | Promise<boolean> | Observable<boolean> {
if (this.authService.isAuthenticated()) {
return true;
}
this.router.navigate(['/login']);
return false;
}
}
Apply the guard to routes
const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard]
}
];
Route Resolvers
Resolvers help load data before a route is activated:
// user-resolver.service.ts
@Injectable({
providedIn: 'root'
})
export class UserResolver implements Resolve<User> {
constructor(private userService: UserService) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<User> {
return this.userService.getUser(route.params['id']);
}
}
Configure the resolver in routes:
const routes: Routes = [
{
path: 'user/:id',
component: UserComponent,
resolve: {
user: UserResolver
}
}
];
Access resolved data in component:
export class UserComponent implements OnInit {
user: User;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.data.subscribe(data => {
this.user = data['user'];
});
}
}
Not Found Routes and Redirects
Handle unknown routes and redirects:
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', component: NotFoundComponent }
];
Route Titles
Set dynamic titles for routes:
const routes: Routes = [
{
path: 'product/:id',
component: ProductComponent,
title: 'Product Details',
resolve: {
product: ProductResolver
},
title: (route: ActivatedRouteSnapshot) => {
const product = route.data['product'];
return `${product.name} - Details`;
}
}
];
Splitting Route Definitions
For larger applications, split routes into feature modules:
// feature-routing.module.ts
const featureRoutes: Routes = [
{
path: 'feature',
component: FeatureComponent,
children: [
{ path: 'list', component: ListComponent },
{ path: 'detail/:id', component: DetailComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(featureRoutes)],
exports: [RouterModule]
})
export class FeatureRoutingModule { }
Best Practices and Tips
- Always use
RouterLinkdirectives instead of href attributes for internal navigation - Implement proper error handling in resolvers
- Use child routes for related feature sets
- Implement loading indicators for async operations
- Consider using route guards for authentication and authorization
- Use proper typing for route parameters and query parameters
- Implement proper cleanup in components that subscribe to route observables
Example: Complete Navigation Structure
Here’s a complete example combining multiple concepts:
const routes: Routes = [
{
path: '',
component: LayoutComponent,
children: [
{
path: '',
redirectTo: 'dashboard',
pathMatch: 'full'
},
{
path: 'dashboard',
component: DashboardComponent,
title: 'Dashboard'
},
{
path: 'users',
loadChildren: () => import('./users/users.module')
.then(m => m.UsersModule),
canActivate: [AuthGuard]
},
{
path: 'products',
component: ProductsComponent,
resolve: {
products: ProductsResolver
},
children: [
{
path: ':id',
component: ProductDetailComponent,
resolve: {
product: ProductResolver
}
}
]
}
]
},
{
path: 'login',
component: LoginComponent
},
{
path: '**',
component: NotFoundComponent
}
];