Briebug Blog

Sharing our thoughts with the community

Mastering RouterLink

Mastering RouterLink


Introduction

The goal here is to give a comprehensive introduction to the topic of routing via the Angular template using the RouterLink directive. Along the way, we'll discover a few patterns and nuances to make development easier and give you, the developer, a clear route (get it? GET IT??) to mastery. By the end of this article, you will understand and be able to implement the following concepts:


  1. The basics of routing from the template
  2. Routing to different levels relative to the current route
  3. Adding named parameters, optional parameters, and fragments to routes
  4. Providing custom data to a route change without showing that data in the URL
  5. Styling elements based on the activated route
  6. Dictate how a route change should reflect in browser history
  7. Strategies for disabling a RouterLink instance in the template


Let's dive in!

Setup

We're going to keep it simple with the following route configuration. The majority of the code examples will use this configuration and any references to "the provided route config" are referencing this configuration:

There is also an example Stackblitz that illustrates some of these concepts and is useful as a visual companion. You are encouraged to clone it and make changes as you read this article!

Getting Started

Turning an element into a link is very straightforward: just add the routerLink attribute to the element and give it a valid Angular route as the value. Here are a few examples:

These examples would result in the following compiled DOM elements:

The RouterLink directive translates the given route into an href attribute for anchor tags, and simply creates a click event handler and adds a tab index for other elements.


There is another, more flexible syntax for routes: the "array" syntax. This allows for specifying individual route segments as values in an array, which then get turned into strings and concatenated together to form the final URL. Here is an example of the array syntax and the resulting DOM element:

Quick Timeout

Now that I have just shown you how to turn any element into a link, I am going to tell you why you shouldn't. Even though any HTML element can contain a RouterLink directive, you should only add links to elements that you would normally use for navigation: which is basically the anchor (<a>) tag. Why? One word: "accessibility". The anchor tag is purpose-built to indicate a link to another view and using a different tag to indicate a navigation element can confuse assistive technology and result in a user not knowing that an element is a link. For more information, check out this link accessibility article.

Now that we understand the basics of template routing, let's see how we can navigate in a slightly more complex scenario.

Relative linking

The URL paths provided to the RouterLink directive follow the same convention as file paths. Specifically, prepending a forward slash at the front of the path makes that path "absolute" while adding no qualifiers or prepending the path with some combination of periods and forward slashes marks the path as "relative". For example, to link to the dashboard, given the provided route config, all of the following are equivalent:

When using relative paths, the router calculates route changes against the currently activated route. So, given the provided route config, routing directly from items to dashboard would look like this:

Here is an example that uses relative routing and the array syntax:

Ok; so we understand how to navigate in a flexible manner but how do we pass data to a route?

Parameters

There are a few different ways to pass persistent state via the URL bar. Let's investigate!

Named Parameters

Named parameters are parameters specified by a colon within a route definition. Let's take an example from our original route definition:

In this example route, the path :id is the path segment that the router will replace with an actual value whenever /items/:id is the navigation target. Here's an example:

But what if we want to pass some state in the URL without adding additional route configuration or if we want the values in the URL to be dynamic? The answer is "optional parameters".

Optional Parameters

There are two types of optional parameters used in Angular routing: "query" parameters and "matrix" parameters. Let's take a closer look at each.

Query Parameters

Query parameters are probably the most well-known optional parameter type. They are specified in a URL like so: https://example.com/route?param1=value&param2=othervalue. In frontend applications, some kind of parsing is generally employed to turn the query parameters into a JavaScript object.


Specifying query parameters from the RouterLink directive is very straightforward. Simply add the queryParams input to any element containing the RouterLink directive and pass it a valid JS object. For example:

Clicking this link would navigate to /dashboard?show=items. At this point, you could respond to the query parameters in the dashboard component via ActivatedRoute.queryParams (an example of consuming query params can be found in the DashboardComponent in the sample app stackblitz).


When navigating with query params, we can specify how params from the source and destination routes should be handled via the queryParamsHandling input on the RouterLink directive. There are two options: "merge" or "preserve". "Preserve" keeps the source query params when navigating to a new route, even at the expense of existing parameters. Take the following example given that the current route is /dashboard?show=items:

Clicking the above link will result in this URL /items?show=items. The query params on the new route essentially get overwritten.


On the other hand, "merge" actually keeps params from both the source and destination. So, given the same source route and router link as above, the resulting URL will be /items?show=items&foo=bar.


Let's take a look at another optional parameter mechanism called "matrix" parameters.

Matrix Parameters

Matrix parameters are similar to query parameters but are more flexible in that they can be used at multiple URL segment levels. For example: /items;foo=bar/1;bar=baz could be used to specify two hierarchical routes ("route" and "sub-route"), each with their own parameters.


Routing with matrix parameters is a bit different than query parameters. Instead of passing a separate input, the parameters are passed as JS objects within the RouterLink route array itself; directly after the URL segment that they apply to. Here's an example:

Clicking this link would result in the example URL given at the start of this section: /items;foo=bar/1;bar=baz. These matrix parameters can be retrieved via ActivatedRoute.params and remember: only the parameters specified at a particular level will be returned in the associated component. So, given the route shown above, when accessing ActivatedRoute.params in the Items component only {foo: 'bar'} will be returned. Accessing ActivatedRoute.params in the Item component will yield {bar: 'baz'} (along with {id: 1} since the named parameter :id is also defined on that route).


Next up, let's see how to route to fragments with the RouterLink directive.

Fragments

URI fragments allow linking to a specific element in a template by referencing that element's ID in the URI, prefixed with a #. For example, /dashboard#graph would load the dashboard route and then move the viewport down to the element with the ID graph.


Linking to a fragment on a page in Angular is accomplished with the fragment input property available on any element with the RouterLink directive.


Here's an example:

If a fragment needs to be preserved across a route change, add the [preserveFragment] input to the destination route and set it to true. So if the current route is /dashboard#graph and we want to route to /items but keep the same fragment, this would be the appropriate RouterLink usage:

Clicking that link would navigate to /items#graph, preserving the existing fragment.


We've seen how to persist state using the URL but how do we go about passing state from one route to the other without using the URL? Let's check it out!

Routing With Data

Sometimes there is a need to pass state to a new route without actually putting that state in the URL. The tool for this is the state input on the RouterLink directive; which takes the provided data and sets it in the current browser History entry. An example:

This will navigate to the items route and pass {foo: 'bar'} along with the rest of the navigation event data to the History entry. After clicking that link, accessing history.state in the browser console would return an object like this: {foo: "bar", navigationId: 4}. Navigating to a different route and then clicking the back button would still result in {foo: "bar"} being included in history.state.


This state can be retrieved anywhere that the Router is injected via router.getCurrentNavigation(); Once that object is returned, the actual state is retrieved by drilling down into extras.state and grabbing whatever data was provided. Here's an example:

Let's explore a couple of additional options that let give us fine-grained control over the URL and browser history.

Navigation Options

There are a couple inputs that allow us to control how navigation events are handled in the URL and subsequently browser history: skipLocationChange and replaceUrl. On the surface, they seem to do similar things but with subtle differences.

skipLocationChange

The skipLocationChange input tells the router to perform the navigation "silently" without changing the URL or pushing a new state entry into history. Consider the following example, assuming that a user is starting on the root route ("/"):

When the user clicks "Dashboard", the URL will not actually update and will stay at "/". Furthermore, once the user clicks "Items" and then clicks the browser "Back" button, they will not be taken back to the dashboard page but will be taken back to the previous route or the root route /. As far as the browser history is concerned, /dashboard was never navigated to. Similarly, if a user then clicks the "Forward" button, they will jump straight back to /items once again skipping /dashboard.

replaceUrl

The replaceUrl input tells the router to perform the navigation and reflect that change in the URL but, instead of adding a new routing event to the stack, it replaces the current URL in history. Consider the following example:

At this moment, when the user clicks the browser back button, they will not be taken back to /items but they will go back to /dashboard. Similarly, going forward will skip /items and navigate directly to /items/1.


Though both skipLocationChange and replaceUrl seem to do pretty much the same thing, the difference lies in the route that is targeted for skipping. With skipLocationChange, we are telling the browser: "don't show the URL for this route or put it in browser history". With replaceUrl, we are telling the browser: "remove the previous route and URL from history once this route is activated".


Let's look at one last routing format, "Auxillary Outlets".

Routing to Multiple Outlets

Auxillary (Aux) routing could be a whole article itself, so we'll just cover the basics here. Consider the following route config with multiple routes and router-outlets:

Clicking the first link would display just the "items" route on the left side of the page. Clicking the second link would show the "widgets" route on the right hand side of the page and clicking the last link would show both side by side effectively creating a dashboard. The URLs would be /(left:'items'), /(right:'widgets') and /(left:'items'//right:'widgets') respectively; also, since the outlet is specified in the URL, we could actually deep link to the exact UI configuration we want, allowing us to create highly flexible UIs and keeping that flexibility in the URL. We could dive deeper but that's for another time.


Now that we've done a deep dive into all the RouterLink directive has to offer, it's time to switch gears and get stylish with the RouterLinkActive directive!

Styling Activated Routing Elements

We often want to give a particular routing element a conditional style based on whether or not the associated route is activated. This is accomplished with the RouterLinkActive directive; available on any element that also has the RouterLink directive. It takes either a string of whitespace-separated class names, or an array of class names, and appends those classes to that element when the associated route is active. Here are a couple examples:

By default, the RouterLinkActive directive will mark an element as active when the active route merely contains the linked route. So, in the previous example, both /items and /items/1 would result in the "active" classes being applied.


If an element should only be active when the route is matched exactly, add the routerLinkActiveOptions input and set it to {exact: true}. Here's an example:

Now, the anchor element will only be activated when the route is /items exactly. This means that /items/1 will not append the active classes to the element. Bear in mind that this also includes optional parameters; so, in the above example, a URL of /items?foo=bar will not append the activated class.


The RouterLinkActive directive is very flexible in that it will not just look at the element to which it is bound for the active route but will actually search for any child elements with the RouterLink directive in order to calculate the active status. Consider the following:

In this example, the containing div will have the class active-container if the URL contains either /dashboard or /items. Once again it is possible to leverage [routerLinkActiveOptions]="{exact: true}" for a parent element to narrow down when an element should be considered "active".


It is also possible to expose the underlying RouterLinkActive directive as a template variable; which allows for complex use cases that the patterns we've talked about so far don't cover. To expose the directive for use in the template, add a template variable to the element with the RouterLinkActive directive and then assign the routerLinkActive instance to that variable. Here's an example:

Once the directive is assigned to the template variable rla, it can be accessed anywhere in the template and used to perform more complex operations on the active state of the link.


Let's take a look at another pattern for styling links: disabling them.

Disabling a Router Link

Disabling a RouterLink depends on the type of element to which it was added. For example, disabling a RouterLink button is easy: just bind to the disabled attribute of the button.


Most other elements, including anchor tags, don't have a disabled property to use yet. In the meantime, we recommend using an ng-if-else statement for elements that can't be disabled. Here's an example:

Recap

After reading this article, you should now have a pretty thorough understanding of how to use the RouterLink directive, the related RouterLinkActive directive and how to achieve most template routing patterns that crop up in day-to-day development.


Specifically, we covered the following topics:


  1. The basics of routing from the template
  2. Routing to different levels relative to the current route
  3. Adding named parameters, optional parameters, and fragments to routes
  4. Providing custom data to a route change without showing that data in the URL
  5. Styling elements based on the activated route
  6. Dictate how a route change should reflect in browser history
  7. Strategies for disabling a RouterLink instance in the template


Thanks for reading!

View Details
- +
Sold Out