Introduction

Angular is a popular JavaScript framework that allows us to build dynamic web applications. As your Angular application grows in complexity, passing information between different routes becomes increasingly important. Whether you’re working on a small or large-scale project, there are several ways to pass data between routes in Angular apps.

By the end of this article, you will have a solid understanding of the different methods available for passing data between routes in Angular and be able to choose the most appropriate approach for your specific use case. Without further ado, let’s start!

Route Navigation in Angular apps

As users perform application tasks, they need to move between the different views or pages that you have defined inside your app.

In a single-page application, you change what the user sees by showing or hiding portions of the display that correspond to particular components, rather than going out to the server to get a new page.

To handle the navigation from one view to the next, you can use the Angular Router.

The Router enables navigation by interpreting a browser URL as an instruction to change the view.

Routing allows us to express some aspects of the application’s state in the URL. Adding routing, allows the user to go straight into certain aspects of the application. This is very convenient as it can keep your application linkable and bookmarkable and allow users to share links with others.

Routing allows us to:

  • Maintain the state of the application
  • Implement modular applications
  • Implement the application based on roles (certain roles have access to certain URLs)

The Angular Router module enables many possibilities, so make sure you check the Angular Routing documentation for more information.

Passing information between routes

As a user navigates our application, we want to pass information from one route to another. Angular can pass data to a route in several ways such as:

  1. Using URL or Route Parameter
  2. Using Optional Parameters or Query Strings
  3. Using a URL Fragment
  4. Static data using the data property
  5. Dynamic data using state object

We will now see examples and use cases of each option.

About the scope of this post

Note that in this post we are talking about sending data between routes, and not specifically between components. I want to point this out because there are also different ways components can communicate and share data with each other.

The most popular way is the communication between parent and child components using @Input() and @Output() decorators. @Input() allows a parent component to update data in the child component and @Output() allows the child component to send data to a parent component. Also, if you are looking for a bi-directional communication within the Parent and children you can use a service. This topic would need another post, so I won’t go into details here, I just wanted to mention the difference. Let’s keep going.

  1. URL or route parameters


Consider a route /products that points to a list of products. Then, to fetch the details of a particular product, the route must look like /products/1 or /products/2, where the numbers 1 and 2 refer to the unique ID of a product. However, this number varies depending on which product the user clicked on.

Such dynamic values are handled using route parameters, in this example,1 and 2 are known as URL or route parameters.

To pass route parameters you need to define both the route and the navigation.

Define the route

The parameter is defined by adding a forward slash (/), followed by a colon (:) and a placeholder.

Consider the example below:

{path :’products/:id’, component: ProductItemComponent}

The above path matches all the products with URLs such as /products/1, /products/2, and so on.

In case you want to support multiple parameters you can add another slash, colon and placeholder.

Let’s see another example:

{ path: ‘products/:param1/:param2/:param3’, component: ProductDetailsComponent}

param1, param2, and param3 are placeholders for parameters. They refer to the values taken by the parameters.

Define the navigation

Consider the URL :/products/:id that fetches details of a particular product by its unique ID. The base path (products) and the route parameter (id) must be provided to the routerLink directive as shown below:

 {{product.name}} 

/products and product.ID are passed as the first parameter and second parameter respectively, to the routerLink array. Thus, product.ID is dynamically taken from the product object.

If you have more params just do:

<a>{{product.name}}</a>

Note that the routerLink directive passes an array which specifies the path and the route parameter. Alternatively we could navigate to the route programmatically:

goToProductDetails(id) {
this.router.navigate([‘/products’, id]);
}

Reading the route parameters in the Component

The ProductDetails component must read the parameter, then load the product based on the ID given in the parameter.

The ActivatedRoute service provides a params  Observable which we can subscribe to to get the route parameters.

this.route.params.subscribe(params => {
const id = params[‘id’];
});

The reason that the params property on ActivatedRoute is an Observable is that the router may not recreate the component when navigating to the same component. In this case the parameter may change without the component being recreated.

  1. Query parameters


Query parameters are those that appear after a question mark (?). The parameters and their corresponding values are separated by an equal to (=) sign.

Consider the URL http://localhost:4200/products?order=popular to display the most popular products.

The code to achieve the above URL is as follows:

showPopularProducts() {
this.router.navigate([‘/products/’], {queryParams:{order:’popular’}});
}

Query parameters are set using queryParams in router.navigate. Multiple query parameters can be passed by passing them as comma-separated parameters along with their corresponding values.

showPopularBlackProducts() {
this.router.navigate([‘/products/’], {queryParams: {order:’popular’, color:’black’} });
}

The resultant URL will be: http://localhost:4200/products?order=popular&color=black

Query parameters using queryParamsHandling

When a user navigates from one view to another, query parameters are lost. Setting queryParamsHandling to preserve or merge helps preserve the required query parameters.

For example, routing users from the products page with the query parameter {order:'popular'} to the /billing page – while also preserving the query parameter – can be achieved in Angular with:

goToBilling() {
this. router.navigate([‘/billing’], {queryParamsHandling:’preserve’});
}

The resultant URL will be http://localhost:4200/billing?order=popular.

As you can see from the URL, the user is taken to the billing page, yet the query parameter from the previous page (order=popular) is retained.

New query parameters can also be merged while still preserving the previous query parameter.

Suppose we want to route users to the /billing page while also passing the query parameter {payment:'paypal'}, we can accomplish the same with the following code:

goToBilling() {
this. router.navigate([‘/billing’], {queryParamsHandling:’merge’});
}

The resultant URL is http://localhost:4200/billing?order=popular&payment=paypal.

Note that the old parameter (order=popular) has been preserved while adding the new parameter (payment=paypal).

Query parameters using RouterLink

As shown in the section on URL parameters, if the routerLink directive is used for navigation, query parameters can be used along with it as follows:

<a>Products</a>

Further, preserve or merge can be added as another attribute to the tag by setting queryParamsHanding accordingly.

Reading the query parameters in the Component

The ActivatedRoute also provides programmatic access for the query parameters.

/** An observable of the query parameters shared by all the routes. */
queryParams: Observable;
public ngOnInit(): void {
this.route.queryParams.subscribe(
(params) => {
// do something with the params
},
(error) => console.log(error)
);
}
  1. URL fragment


An URL fragment or hash appears at the end of the URL. It’s a link that jumps or, in other words, scrolls down to the content whose ID is the same as that in the fragment of the Angular router.

It’s also known as a named anchor and is an internal page reference.

Consider the imaginary URL https://www.developers.com/tech#angular, it will take us to a page with details on different developers, and under tech, information on every tech specialization.

The fragment (angular) will scroll to the part of the page focusing on angular under tech. If a user clicks on, let’s say, react, the URL will change to https://www.developers.com/tech#react.

Note that the fragment changed from #angular to #react. The hash, therefore, keeps changing, depending on which tech specialization is clicked on by the user.

<a>
Link to Angular careers
</a>

Reading the fragment in the Component

The ActivatedRoute also provides programmatic access for the route fragment.

/** An observable of the URL fragment shared by all the routes. */
fragment: Observable;
public ngOnInit(): void {
this.route.fragment.subscribe(
(params) => {
// do something with the route fragment
},
(error) => console.log(error)
);
}
  1. Data property

The Angular route data property helps to pass static data to a route. It’s an array of key-value pairs and is used to store read-only static data. A typical example of this is page titles.

Consider a route with a data property set as follows:

{path:’static’, component: StaticComponent, data: {title: “Angular”}}

Here, {title: "Angular"} is the static data passed when the component, StaticComponent, is rendered.

Another use case can be: user roles, for example, if the route can be accessed for particular user roles we can define them in the route data and that roles can be retrieved inside the component.

{
path: ‘admin’,
loadChildren: loadChildren: () => import(‘./admin/admin.module’).then(m => m.AdminModule),
data: { userRoles: [UserRoles.OWNER, UserRoles.ADMIN, UserRoles.DATA_MANAGER] }
}

The Angular Router will pass the defined roles when the AdminModule is rendered. The data will be located in the data property of the ActivatedRoute service.

Reading the data in the Component

The ActivatedRoute also provides programmatic access for the route static data.

/** An observable of the static and resolved data of this route. */
data: Observable;
public ngOnInit(): void {
this.route.data.subscribe(
(params) => {
// do something with the route data
},
(error) => console.log(error)
);
}
  1. Dynamic data with State object

Dynamic data or user-defined objects can be passed using the state object stored in History API.

The state value can be provided using the routerLink directive or navigateByURL.

The snippet to use the routerLink directive is:

Dynamic Data Link

Dynamic data using navigateByURL

An alternative method to pass dynamic data is by using navigateByURL as follows:

this.router.navigateByUrl(‘/dynamic-route’, {
state: { id: item.id , name: item.name }});
The Router service provides the navigateByUrl(url: string | UrlTree, extras: NavigationBehaviorOptions = {...}); method.

Using the extras parameter, we can send NavigationBehaviorOptions which is an object containing properties that modify the navigation strategy. One of these options is the “state” property.

The state property is a developer-defined state that can be passed to any navigation. Access this value through the Navigation.extras object returned from the Router.getCurrentNavigation() method while a navigation is executing.

After a navigation completes, the router writes an object containing this value together with a navigationId to history.state. The value is written when location.go() or location.replaceState() is called before activating this route.

Reading the state in the Component

Based on our previous example:

this.router.navigateByUrl(‘/dynamic-route’, {
state: { id: item.id , name: item.name }});

We can get the state data like this:

constructor(public router: Router) {
const currentNav = this.router.getCurrentNavigation();
const id = currentNav.extras.state.id;
const name = currentNav.extras.state.name;
}

Wrapping up

Angular navigation is a very powerful module as it offers many possibilities. There’s so much more to learn about the angular router module. Make sure to have this link handy to check all the core router concepts.

In this post we talked about different ways to pass information from one route to another, something you will need to tackle in every angular app.

If you want to continue in your angular routing learning journey, I suggest you learn about the different ways and tools to define your app routing such as: child routes, nested routes, component-less routes, empty path routes, relative paths and lazy loading and also how to protect routes and prefetch route data using Angular guards.

As mentioned before, the router module offers lots of possibilities, here we just introduced the basic concepts so you can have a good overview of this topic. Hope you enjoyed this post!