Skip to main content

Routing

Basic Setup

import { Routes } from '@angular/router';

export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'users/:id', component: UserDetailComponent },
{ path: '**', component: NotFoundComponent } // Wildcard
];

// main.ts
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';

bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)]
});

Router Outlet

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink],
template: `
<nav>
<a routerLink="/">Home</a>
<a routerLink="/about">About</a>
<a [routerLink]="['/users', userId]">User</a>
</nav>
<router-outlet></router-outlet>
`
})
export class AppComponent {}

Route Parameters

import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
template: `<p>User ID: {{ userId }}</p>`
})
export class UserDetailComponent {
route = inject(ActivatedRoute);
userId: string | null = null;

ngOnInit() {
// Snapshot (one-time)
this.userId = this.route.snapshot.paramMap.get('id');

// Observable (reactive)
this.route.paramMap.subscribe(params => {
this.userId = params.get('id');
});
}
}

Query Parameters

// Navigate with query params
router.navigate(['/search'], { queryParams: { q: 'angular', page: 1 } });
// URL: /search?q=angular&page=1

// Read query params
this.route.queryParamMap.subscribe(params => {
const query = params.get('q');
const page = params.get('page');
});

Programmatic Navigation

import { Router } from '@angular/router';

export class AppComponent {
router = inject(Router);

goToUser(id: number) {
this.router.navigate(['/users', id]);
}

goToSearch() {
this.router.navigate(['/search'], {
queryParams: { q: 'angular' },
fragment: 'section1' // URL: /search?q=angular#section1
});
}

goBack() {
this.router.navigate(['..'], { relativeTo: this.route });
}
}

Child Routes

export const routes: Routes = [
{
path: 'products',
component: ProductsComponent,
children: [
{ path: '', component: ProductListComponent },
{ path: ':id', component: ProductDetailComponent },
{ path: ':id/edit', component: ProductEditComponent }
]
}
];

// ProductsComponent template
@Component({
template: `
<h1>Products</h1>
<router-outlet></router-outlet> <!-- Child routes render here -->
`
})
<a routerLink="/home"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
Home
</a>

<style>
.active { font-weight: bold; color: blue; }
</style>

Route Data & Resolvers

// Static data
{
path: 'admin',
component: AdminComponent,
data: { title: 'Admin Panel', role: 'admin' }
}

// Read data
this.route.data.subscribe(data => {
console.log(data['title']); // 'Admin Panel'
});

Named Outlets

{
path: 'dashboard',
component: DashboardComponent,
children: [
{ path: '', component: MainContentComponent },
{ path: 'sidebar', component: SidebarComponent, outlet: 'sidebar' }
]
}

// Template
<router-outlet></router-outlet>
<router-outlet name="sidebar"></router-outlet>

// Navigate
router.navigate(['/dashboard', { outlets: { sidebar: ['sidebar'] } }]);

Best Practices

  • Use lazy loading for large modules
  • Always handle 404 with wildcard route
  • Use guards for authentication/authorization
  • Prefer relative navigation in child routes
  • Use resolvers to pre-fetch data