Route parameters and navigation
A route parameter is a variable part of the address. A post page does not need a hundred routes: one route posts/:id matches /posts/1, /posts/2 and so on, and the component receives the post id and loads the post.
A route with a parameter
ng generate component post-detail// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { PostList } from './post-list/post-list';
import { PostDetail } from './post-detail/post-detail';
export const routes: Routes = [
{ path: 'posts', component: PostList },
{ path: 'posts/:id', component: PostDetail },
];The colon marks a parameter named id.
A link with a parameter
In the post list the title becomes a link. An array in routerLink builds the address from parts:
<!-- src/app/post-list/post-list.html -->
@for (post of posts(); track post.id) {
<h3><a [routerLink]="['/posts', post.id]">{{ post.title }}</a></h3>
}Add RouterLink to the imports of the PostList component. The links lead to /posts/1, /posts/2 and so on.
The parameter as an input
The simplest way to get a parameter is to enable binding parameters to inputs in app.config.ts:
// src/app/app.config.ts
provideRouter(routes, withComponentInputBinding()),Import withComponentInputBinding from @angular/router. Now the id parameter goes into the input with the same name:
// src/app/post-detail/post-detail.ts
import { Component, OnInit, inject, input, signal } from '@angular/core';
import { RouterLink } from '@angular/router';
import { PostApi } from '../post-api';
import { Post } from '../post';
@Component({
selector: 'app-post-detail',
imports: [RouterLink],
templateUrl: './post-detail.html',
})
export class PostDetail implements OnInit {
private api = inject(PostApi);
id = input.required<string>();
post = signal<Post | null>(null);
ngOnInit() {
this.api.getPost(Number(this.id())).subscribe((p) => this.post.set(p));
}
}<!-- src/app/post-detail/post-detail.html -->
<a routerLink="/posts">← All posts</a>
@if (post(); as p) {
<h2>{{ p.title }}</h2>
<p>{{ p.body }}</p>
} @else {
<p>Loading...</p>
}Click a title in the list — the post page opens. Parameters from the address are always strings, so Number() turns id into a number.
With ActivatedRoute
Without withComponentInputBinding the parameter is read from the ActivatedRoute service:
// src/app/post-detail/post-detail.ts
private route = inject(ActivatedRoute);
ngOnInit() {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.api.getPost(id).subscribe((p) => this.post.set(p));
}Many older examples are written this way — the result is the same.
Navigating from code
The Router service moves to another page from a method — for example, after a form is saved:
// src/app/post-form/post-form.ts
private router = inject(Router);
save(title: string, body: string) {
this.api.createPost({ title, body }).subscribe(() => {
this.router.navigate(['/posts']);
});
}The navigate() method takes the same array as routerLink, for example ['/posts', 5]. There is also navigateByUrl('/posts') for a ready-made string.
Common mistake
When you go from /posts/1 to /posts/2, Angular does not create the component again but reuses it — ngOnInit is not called a second time, and the old post stays on the page. If such transitions are possible (for example, a "Next post" link), load the data in an effect() that reads this.id(): it runs on every new parameter value.