Accessing route params in Angular
Tags: angular
Published: 2023-05-29 20:35:28

There are various scenarios in which you may need to read parameters from your route. These parameters can be **query parameters** _(e.g.: test?username=…)_ or **route parameters** _(e.g.: test/:testId)_.

Additionally, you can pass any desired value via the data property inside the Route object in Angular routing as shown below:

```ts
export const appRoutes: Route[] = [
  {
    path: 'test/:testId',
    loadComponent: () => import('./test.component'),
    data: {
      permission: 'admin',
    },
  },
];
```

## ActivatedRoute
In all scenarios, you will start by injecting the ActivatedRoute into your component. This class gives access to the route on which your component is defined. 

```ts
private activatedRoute = inject(ActivatedRoute);
```

Within this class, you have two options for retrieving your route parameters.

### Snapshot
The first option is using the snapshot object. As the name suggests, it allows you to take a snapshot of the route state and work with it.

```ts
testId = this.activatedRoute.snapshot.params['testId'];
permission = this.activatedRoute.snapshot.data['permission'];
user = this.activatedRoute.snapshot.queryParams['user'];
```
Using the snapshot object provides static values, meaning that if the parameters change, you will not be notified unless you reload your component.

It is generally considered safer to choose the next option:

### Observable
Alternatively, each parameter can be listened to as an observable. Although this may feel more complex, it provides the advantage of being notified when the value changes.

```ts
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']));
```
By using observables, you can subscribe to these streams _(using `asyncPipe` or `subscribe` function_) and receive notifications whenever the values of the parameters change. 
This allows for more dynamic and responsive behavior in your components.

## Ngrx Router Store
If you are using Ngrx within your application, you might be interested in retrieving route parameters through Selectors.
First, you need to add the `@ngrx/router-store` npm package and include it in your bootstrapApplication function in the `main.ts` file:

```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()
  ],
});
```
Next, Ngrx provides a getRouterSelectorfunction with a variety of selectors that you can use. You can destructure the function as follow:

```ts
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();
```
Finally, within your component, you can access your route parameter properties as follows:

```ts
testId$ = this.store.select(selectRouteParam('testId'));
permission$ = this.store.select(selectRouteDataParam('permission'));
user$ = this.store.select(selectQueryParam('user'));
```
You end up with observables and you must subscribe to them to get notified.

## RouterInput in Angular v16
Angular v16 was released with a lots of new features aiming to improve the developer experience (DX). One of these features is RouterInput, which allows you to retrieve route information through inputs.

> If you haven't yet explored AngularChallenges or completed the router input challenge, I encourage you to do so now or at a later time to practice using this new API. You can find the challenge details here: [AngularChallenges - Router Input](https://github.com/tomalaforge/angular-challenges/blob/main/apps/router-input/README.md).

To take advantage of RouterInput, you need to update your route provider in the main.ts file as follows:

```ts
import { provideRouter, withComponentInputBinding } from '@angular/router';

bootstrapApplication(AppComponent, 
  providers: [provideRouter(appRoutes, 
      withComponentInputBinding() // 👈
    )]
)
```
With the updated route provider, you can now listen to route parameters through input bindings in your component:

```ts
@Input() testId!: string;
@Input() permission!: string;
@Input() user!: string;
```

**Notes:**
- if the `testId` input changes, your component will be notified _(as an observable stream)_.
- Router Inputs are only accessible within the routed component. If you need to access these parameters within a child component, you can use one of the methods mentioned earlier or pass the input through input binding between the parent and child components.
- If you prefer a more descriptive decorator name, you can remap it in its definition as follow:

```ts
import {Input as RouterInput} from '@angular/router'

export class Component {
 @RouterInput() testId!: string;
}
```

---

Feel free to explore and test your new skill on my [Angular challenge #22](https://github.com/tomalaforge/angular-challenges/blob/main/apps/router-input/README.md), and enjoy experimenting with its capabilities in your projects.

You can find me on [Twitter](https://twitter.com/laforge_toma) or [Github](https://github.com/tomalaforge).Don't hesitate to reach out to me if you have any questions.