Reading query string parameters (like ?category=electronics&sort=price) in Angular goes through ActivatedRoute's queryParamMap, which — being an Observable rather than a plain snapshot — needs to be subscribed to, not just read once, to stay correctly in sync.
Reading a query param via subscription
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
this.route.queryParamMap.subscribe(params => {
this.category = params.get('category');
this.sortBy = params.get('sort') ?? 'name';
});
}
Subscribing (rather than reading a one-time snapshot) is what correctly reacts to a query string change even when Angular reuses the same component instance for the new URL — a common scenario when only the query params change while the route's path stays the same.
Reading a one-time snapshot instead, when reactivity isn't needed
ngOnInit(): void {
this.category = this.route.snapshot.queryParamMap.get('category');
}
route.snapshot reads the query params exactly once, at the moment this code runs — appropriate only when the component is guaranteed to be freshly created for every URL change (rather than reused), since it won't pick up a subsequent in-place query param change on its own.
Reading multiple params, and handling an array-valued param
this.route.queryParamMap.subscribe(params => {
this.category = params.get('category');
this.tags = params.getAll('tag'); // ?tag=sale&tag=new → ['sale', 'new']
});
getAll(), rather than get(), is needed when the same query parameter key can legitimately appear multiple times in the URL — get() alone would only return the first matching value and silently drop the rest.
Updating the query string without a full navigation
constructor(private route: ActivatedRoute, private router: Router) {}
updateSort(sortBy: string): void {
this.router.navigate([], {
relativeTo: this.route,
queryParams: { sort: sortBy },
queryParamsHandling: 'merge',
});
}
queryParamsHandling: 'merge' combines the new query param with any existing ones already in the URL, rather than replacing the entire query string — without it, updating just the sort order would wipe out any other active filters (like category) already present in the URL.
Combining route params and query params together
// URL: /products/electronics?sort=price
this.route.paramMap.subscribe(params => {
this.categorySlug = params.get('categorySlug'); // route param
});
this.route.queryParamMap.subscribe(params => {
this.sortBy = params.get('sort'); // query param
});
Route params (paramMap, part of the URL's path structure) and query params (queryParamMap, the part after the ?) are read through two genuinely separate observables — a component using both needs to subscribe to each independently.