Reading route data with ActivatedRoute
Working with Angular routes often means reading values that describe the current navigation state. These values can be query parameters (such as test?username=…) or route parameters (such as test/:testId).
Beyond those, the data property on a Route object lets you supply arbitrary values to a route, as illustrated here:
export const appRoutes: Route[] = [
{
path: 'test/:testId',
loadComponent: () => import('./test.component'),
data: {
permission: 'admin',
},
},
];
ActivatedRoute
No matter which approach you take, the starting point is injecting ActivatedRoute into your component. This service exposes the route context in which the component is rendered.
private activatedRoute = inject(ActivatedRoute);
Inside this service, there are two distinct ways to read route values.
Snapshot
The first is the snapshot object. As the name implies, it captures the route state at a given moment and lets you work with that static picture.
testId = this.activatedRoute.snapshot.params['testId'];
permission = this.activatedRoute.snapshot.data['permission'];
user = this.activatedRoute.snapshot.queryParams['user'];
Since the snapshot holds static values, any change to the parameters will go unnoticed unless the component is reloaded.
In most cases, the next approach is the safer bet:
Observable
The alternative is to treat each parameter as an observable stream. While that may seem more involved, the payoff is that your component gets notified whenever the value shifts.
testId$ = this.activatedRoute.params.pipe(map((p) => p['testId']));
permission$ = this.activatedRoute.data.pipe(map((d) => d['permission']));
user$ = this.activatedRoute.queryParams.pipe(map((q) => q['user']));
Subscribing to these streams — whether through the asyncPipe or the subscribe function — ensures your UI responds to parameter updates. That makes for more adaptable and interactive components.
Ngrx Router Store
For applications that already rely on Ngrx, there is a way to pull route parameters through selectors. Start by adding the @ngrx/router-store npm package and registering it inside bootstrapApplication in main.ts:
import { provideStore } from '@ngrx/store';
import { provideRouterStore, routerReducer } from '@ngrx/router-store';
import { AppComponent } from './app.component';
bootstrapApplication(AppComponent, {
providers: [
//...
provideStore({
router: routerReducer,
}),
provideRouterStore()
],
});
Ngrx then exposes a getRouterSelector function, which returns a set of selectors ready for use. You can destructure it like this:
import { getRouterSelectors, RouterReducerState } from '@ngrx/router-store';
// Other selectors are available:
// https://next.ngrx.io/guide/router-store/selectors
export const {
selectQueryParam,
selectRouteParam,
selectRouteDataParam,
} = getRouterSelectors();
Inside your component, you can then reach the route parameter properties in this manner:
testId$ = this.store.select(selectRouteParam('testId'));
permission$ = this.store.select(selectRouteDataParam('permission'));
user$ = this.store.select(selectQueryParam('user'));
The result is a set of observables, meaning you must subscribe to them to stay informed of changes.
RouterInput in Angular v16
Angular v16 shipped with a range of DX-focused improvements, and one of them is RouterInput. This feature allows you to obtain route information through component inputs.
If you haven't had a chance to look at AngularChallenges or completed the router input challenge, now is a good moment to do so. It's a hands-on way to practice this new API. Challenge details are available here: AngularChallenges - Router Input.
To enable RouterInput, adjust your route provider in main.ts as shown below:
import { provideRouter, withComponentInputBinding } from '@angular/router';
bootstrapApplication(AppComponent,
providers: [provideRouter(appRoutes,
withComponentInputBinding() // 👈
)]
)
With the updated provider in place, route parameters become available as input bindings:
@Input() testId!: string;
@Input() permission!: string;
@Input() user!: string;
Notes:
- When the
testIdinput changes, your component receives a notification (delivered as an observable stream). - Router Inputs work only within the component directly tied to the route. To access these parameters inside a child component, you'll need one of the earlier techniques or pass the input down through parent-child binding.
- If you'd like a more expressive decorator name, you can remap it in its definition as follows:
import {Input as RouterInput} from '@angular/router'
export class Component {
@RouterInput() testId!: string;
}
Take a moment to try out this new skill on my Angular challenge #22 and see what you can build with it.
Find me on Twitter or Github. Feel free to reach out with any questions.
