How to use Angular Router State?

The Angular Router supports passing custom state information with every navigation. Here is an example of using routerLink with some attached state:

As you can see, state is an @Input of the routerLink directive. You can pass any number of objects you want to that state. Such a state gets added to the browser’s history.state object, which can be helpful if you need to pass some information to a non-Angular library, for instance.

The receiving component can access the data using router.getCurrentNavigation(). The state data is then nested under extras.state:

You can find an example in action here on Stackblitz. The example is an app with a list of products. Clicking on a product opens a product details component. The selected product is passed to such component using router state instead of using a resolver and URL parameters, which leaves our router config lightweight:

The end result looks like this:

Animated Route Transitions with Angular 17

Yesterday, we covered how to generate CSS animations for our Angular applications. Today, let’s see how to use such animations as route transitions when navigating from one route to the next:

The first thing we need to do is add the right providers for animations and transitions along with our routing config:

Then, we can create two different animations: One to exit the current “page” and one to enter the next “page.” In my example, I made two animations called entrance and exit using the CSS animation generator tool:

Angular has two specific selectors for view transitions: ::view-transition-old(root) for the exit of the old page and ::view-transition-new(root) for the entrance of the new page. Our next step is to “plug” our animations into these selectors:

And… that’s it! Router animations are now in place. You can see the code for that example in action on Stackblitz.

routerLinkActive directive

Have you ever wondered how to highlight a specific HTML element, such as a link or a button, when a particular URL is selected? Something similar to this:

The good news is that there is a specific directive for this called routerLinkActive. Here is how you can use it:

The value set to routerLinkActive is the name of a CSS class (or several CSS classes) that gets applied to the HTML element when the current URL path matches the path of the routerLink directive on that same element. In other words, if the path is page1, then the button element has an active class applied to it.

As a result, all I did to style the above example was add that CSS class definition to my global CSS file:

And that’s it! Note that the directive supports several other options documented here:

You can see my example in action on Stackblitz here.

Accessing route information with Angular

The Angular router comes with many interesting features, such as lazy-loading, guards, resolvers, or nested routes.

One of these features is the ability to access route parameters and route information within the component displayed by the router. This can be done by injecting the ActivatedRoute service in our component.

This ActivatedRoute contains a lot of information:

The most commonly used attributes are:

  • paramMap: An Observable of route parameters such as id in the route /product/:id/info
  • queryParamMap: An Observable of query parameters such as q in the route /product/search?q=test
  • data: An Observable of the data resolved by route resolvers such as hero in this example of a resolver.

snapshot gives us the current value of those same parameters instead of using Observables.

Another option appeared with Angular 16. We can bind router information to component inputs instead of using ActivatedRoute.

This works for the three categories of parameters covered earlier: Route params, query params, and resolved route data.

This means that this old and verbose approach:

can be replaced with the following concise and router-independent code:

The only config needed for this to work is the one line of code described in this article.

NG16 Preview: Helpers to convert class guards to functional

As covered a few newsletters ago, Route guards are meant to be functions with Angular 15+. Angular 16 removes the support for class guards.

Does that mean we have to migrate our old class guards manually? Of course not! The Angular team has our back, and the Angular CLI will remove the deprecated interfaces automatically when upgrading to Angular 16 as follows:

Then, if you want to keep your guards as classes, you can do so using a helper function called mapToCanActivate:

Or you can, of course, decide to use a function instead:

Angular 16 Preview: Binding Router Information to Component Inputs

Passing parameters to routes is a frequent task with Angular. Before Angular 16, we had to inject the ActivatedRoute service and retrieve the parameters from that service using the snapshot or params properties.

With Angular 16, we can get those values automatically bound to the component inputs using the following router config:

If you’re using the RouterModule.forRoot() syntax, then enabling component input binding looks like this:

RouterModule.forRoot(routes, {bindToComponentInputs: true});Code language: JavaScript (javascript)

According to the documentation, this option can bind all route data with key-value pairs to component inputs: static or resolved route data, path parameters, matrix parameters, and query parameters.

For instance, assuming we have a URL parameter called id, if we enable the component input binding feature, then all we need in our component is an @Input decorator to receive that value:

What I like about this option is that our components do not need the ActivatedRoute service anymore, meaning they don’t have to be used just as routed components. Instead, they could be used in other scenarios where the id is passed as an HTML attribute, just like any other non-routed component. As a result, our components are more generic and not tied to a single use case.

Named router outlets

Using the Angular Router is one of the best ways to emulate multiple pages/screens in an Angular application. However, we might sometimes want even more flexibility by having the equivalent of “sub-pages” where sibling elements on a web page can display dynamic content based on the current URL.

Something like this where three different divs can display different components without a parent/child relationship:

Angular allows us to have named router outlets to support such scenarios. We can define several sibling outlets by giving them distinctive names like so:

Then, in our router config, we can define which component goes in which outlet for which path:

Finally, when routing a component to an outlet, we have to repeat some of that config in the form of a rather lengthy command:

The above code will load two different components in two different named outlets. You can see a live example on Stackblitz here.

Using a resolver function or a title strategy to change the page title

Before we get into today’s topic: The Angular Team’s Request for Comments (RFC) for Angular Signals is now open to the public. Feel free to take a look and give your feedback.

Yesterday, we saw how to change an application’s title using the router config. Today, let’s cover more complex scenarios when we need a dynamic title set depending on other factors.

Using a resolver function

First, we might need some processing using a function to extract information from a service or API. In that case, we can use a resolver function that gets that data and returns it:

Note that the signature of that function allows us to return a string, an Observable, or a Promise. That way, we can make an API request to fetch some extra data if needed. For instance, in the above example, getUsername() returns an Observable (code here). This is another excellent example of using the inject function we covered earlier in this newsletter.

Using a title strategy service

For more complex scenarios, Angular allows us to create a custom TitleStrategy. Such strategy takes the form of a service that has to implement an updateTitle method:

Here is an example where I add a prefix to the title, then use the default title from the title property in the router config. Note that we could easily inject other services and perform async operations before setting the new title if needed:

Such title strategy service has to be provided as the default title strategy in our dependency injection configuration as follows:

You can see that example in action here on Stackblitz.

Changing the page title using Angular Router

This is a quick post to showcase a recent addition to the framework that is useful and very simple to implement. We can now have a title property in every route config:

The provided title is set as the page title (in your browser tab) when the user navigates from one page to the next. Just like that:

You can see a code example here on Stackblitz with a live version here. I used standalone components with the new provideRouter function just for the fun of it:

Note that there is also a Title service that can be used if you need to change that title dynamically later. Here’s a short tutorial of mine on that topic: How to change the page title and meta tags with Angular?

Resolver function for the Angular Router

The last router function of our guard series is resolve. It is the replacement for class-based resolvers. A resolver function runs after all guards have run and before the destination component is rendered. Such a function is typically used to preload component data before rendering it on the screen.

For instance, in this example:

heroResolver is a function that returns either some data or a Promise or an Observable of that data. Such data will be stored in the hero property of a data object returned by an Observable from the ActivatedRoute service as follows:

Of course, we could resolve multiple different pieces of data using the following syntax:

resolve: {user: userResolver, session: sessionResolver},Code language: JavaScript (javascript)

A resolver function can use the inject function to use services and resolve data as follows:

export const userResolver: ResolveFn<User> = () => { 
    return inject(UserService).getCurrentUser(); 
 };Code language: TypeScript (typescript)