Application URL Routing
Angular Routing SPA Typescript Visual Studio Code

Routing with Optional Parameters in an Angular Application

Welcome to today’s post.

In today’s post I will be discussing optional routing parameters that can be used within an Angular application.

Angular applications require routing to navigate from one component to another. It allows us to define routes between the parent module into child and feature modules and vice-versa. The default mode of routing, know as required routing is convention-based, which depends on component paths. In most cases, we can use the required mode of routing to get to our destination component. We can use optional routing to not only get to our destination component, but to also provide additional parameters to our component for additional needs. I will go through this in the sections that follow.

There are two main types of routing used in an Angular application:

  1. Required parameter routing
  2. Optional parameter routing

I will first give a brief overview of required routing parameters.

Required Routing Parameters

The most common type of routing utilized within an Angular application is required parameter routing.

An example of this is shown below in an excerpt of a routing module definition:

const routes: Routes = [
  { path: 'my-component', component: MyComponent },
  { path: 'my-component/:id', component: MyComponentDetail },
  { path: '', redirectTo: '/my-component', pathMatch: 'full' }, 
  { path: '**', component: PageNotFoundComponent },  
];

We have components MyComponent and MyComponentDetail, whose activation is determined by the route path, which is specified without parameters, or with one parameter id. The component MyComponent is matched to the route /my-component and called with no parameters. The route in this case will support either no parameters or at least one optional parameter.

The resolved route is then:

/my-component

The component MyComponentDetail is called with exactly one parameter, id. Any route that matches the route /my-component:/id will successfully activate the component in the routing path. An example resolved path is then:

/my-component/1

We can get a little bit more complicated with our required paths by specifying additional parameters in the routing pattern like this:

{ path: 'my-component/:id1/:id2', component: MyComponentExtraDetail },

A pattern like this would be suitable for when we wish to make our component MyComponentExtraDetail take additional parameters that allow it to shape its functionality. What this means is that we cannot call the component with only one of the two parameters id1 or id2, and we will need to specify both in the route like this:

my-component/1/2  

In the next section I will give an overview of optional routing parameters.

Optional Routing Parameters

With optional routing parameters the specification of parameters is not mandatory. We can specify any number of parameters in the route, and the routing pattern will resolve and activate the component.

An example route definition including an optional route is shown below:

const routes: Routes = [
  { path: 'my-component', component: MyComponent },
  { path: '',   redirectTo: '/my-component', pathMatch: 'full' }, 
  { path: '**', component: PageNotFoundComponent },  
];

With optional routing parameters, we don’t specify the route parameters in the routing path, which allows the user to select routing paths that are different and add logic within our component to either action a different business rule or re-route the path with an additional parameter. This gives us dynamic control of the routing path.

I will show an example of how this is done:

An example of Optional Routing

The example below will illustrate the following concepts of optional routing:

  1. Use of optional route matching
  2. Use of router links in HTML templates
  3. Dynamically re-routing using URL segments
  4. Dynamically re-routing using the active route

Optional Route Matching

Matching optional routes is done on either from a RouterLink or from within a component by extracting the active route and URL segments. In a routing module, we will have to specify the base route path without parameters as shown:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DemoEmitterComponent } from './demo-emitter/demo-emitter.component';

const routes: Routes = 
[
  { path: 'demos', component: DemoEmitterComponent },
  { path: 'demo-emitter', component: DemoEmitterComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class DemoRoutingModule { }

The above is a route definition within a feature module, with the application routing module declaring it as a child module as shown:

const routes: Routes = [
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
  { path: '', redirectTo: 'home', pathMatch: 'prefix'},
  { path: 'demo', loadChildren: () => import('./demo/demo.module')
    .then(m => m.DemoModule)},
  …
];

The URL segment for the path of the feature module is:

demo/demo-emitter

Specifying the Router Link with Optional Parameters

A router link is an attribute within a HTML hyperlink that allows you to specify a URL route to an application component.

In the example below, I specify router links which contain optional parameters which resolve to match valid URL routes to our component from the route path patterns.

<button mat-menu-item>
  <a [routerLink]="['/demo/demo-emitter']" 
    routerLinkActive="activebutton">
    Component with No Parameters
  </a>
</button>
<button mat-menu-item>
  <a [routerLink]="['/demo/demo-emitter', {id1:1, id2:2}]" 
    routerLinkActive="activebutton">
    Component with Two Parameters
  </a>
</button>
<button mat-menu-item>
  <a [routerLink]="['/demo/demo-emitter', {id1:1}]" 
    routerLinkActive="activebutton">
    Component with One Parameter
  </a>
</button>  
<button mat-menu-item>
  <a [routerLink]="['/demo/demo-emitter', {id2:2}]" 
    routerLinkActive="activebutton">
    Component with One Parameters
  </a>
</button>

The general format to specify optional parameters in a [routerLink] attribute is:

<a [routerLink]=
  "[relativepath, {parameter1:value1, parameter2:value2}]" 
  …
</a>

The above router link for two parameters will resolve to the general URL path:

http://localhost:4200/[relativepath];parameter1=value1;parameter2=value2;

So, when we select the following router link:

<a [routerLink]=”[‘/demo/demo-emitter’, {id2:2}]” .. />

The URL route will resolve to:

http://localhost:4200/demo/demo-emitter;id2=2

In our component we can check the this.activatedRoute.snapshot value in the debugger and see the params property showing as

{ id2: ‘2’}

The debugger breakpoint with the snapshot parameters is shown below:

In the next section, I will show how to extract parameters from the activatedRoute object.

Extracting Optional Parameters from paramMap

The paramMap array property of the activatedRoute object contains the optional parameters passed into the URL route. A single optional parameter is shown below:

In our debugger we just read off the array keys of the property:

this.activatedRoute.paramMap

We can also extract the routing path as follows from:

this.activatedRoute.snapshot.routeConfig.path

Recall this is just the URL path match of the component’s selector we specified in the router path pattern in our routing module:

There is another property param of the activatedRoute which is an observable that contains the current parameters passed into the component. To obtain the correct values, we will need to subscribe to it. I will later show how to subscribe to the params property to obtain the routing attributes. The last emitted value id of the _value property corresponding to the routing attributes is shown from a debug snapshot:

Multiple Optional Parameters

Below is an example of a multiple optional parameter route in the _value variable taken from the activatedRoute.snapshot.params observable:

The structure of multiple optional parameters paramMap.params is a JSON object of the form:

{ parameter1: value1, parameter2: value2, … , parameterN: valueN }

This is shown below in the debug breakpoint, where we can read the params property of activatedRoute.snapshot.paramMap:

In the next section, I will show how to extract the relative URL path from the activatedRoute.snapshot property.

Determining the Relative URL Route Path

A relative URL route consists of multiple URL segments within the activatedRoute.snapshot property

Below is a debug snapshot of the URL Segments array:

This helps us construct the relative path from the URL segments with the code snip below:

let route = this.activatedRoute.snapshot;
let urlSegments = route['_urlSegment'].segments;
let relativePath: string = '';
urlSegments.forEach((pathSegment: UrlSegment) => {
  relativePath = relativePath.concat(pathSegment.path, '/');
});

For the above code we will need to import the router URLSegment interface as shown:

import { UrlSegment } from '@angular/router';

Routing to the Relative Path with Router.navigate()

Once we have our relative path, we can route from the relative path within our component as shown:   

The syntax for routing two optional parameters is shown below:

this.router.navigate([relativePath, {id1:firstParam, id2:mySecondParam}]);

Subscribing to the paramMap and re-routing based on current Optional Parameters

In the code excerpt below which is within a component that takes the optional parameters we do the following:

  1. Obtain the relative URL route from the URL segments (shown earlier).
  2. Subscribe to the BehaviorSubject observable this.activatedRoute.paramMap
  3. Obtain the optional parameter values.
  4. Re-route using the navigate() method.
import { ActivatedRoute, ParamMap, Router, UrlSegment } 
  from '@angular/router';

…

  routeSubscription: Subscription;

  constructor(private activatedRoute: ActivatedRoute, 
    private router: Router) { }

  ngOnInit(): void {
    let route = this.activatedRoute.snapshot;
    let urlSegments = route['_urlSegment'].segments;
    let relativePath: string = '';
    urlSegments.forEach((pathSegment: UrlSegment) => {
      relativePath = relativePath.concat(pathSegment.path, '/');
    });
    this.routeSubscription = this.activatedRoute.paramMap
      .subscribe((params: ParamMap) =>
      {
        const firstParam: string = params.get('id1');
        const secondParam: string = params.get('id2');
        if (firstParam)
          console.log('param id1 is used in route!');
        if (secondParam)
          console.log('param id2 is used in route!');
        if (firstParam && !secondParam)
        {
          let mySecondParam: string = "3";
          this.router.navigate([relativePath, 
            {id1:firstParam,id2:mySecondParam}]);
        }
      });
  }

In the component, we shouldn’t forget to release the observable:

ngOnDestroy()
{
  if (this.routeSubscription)
    this.routeSubscription.unsubscribe();
}

The example above will allow a route to our component with the first parameter then re-route back to the same component with the relative path of the original route. This should also work within a feature module.

Using the relativeTo option of router.Navigate()

There is an alternative way of specifying the relative path when re-routing.  the relative path can specified without quantifying the individual path segments in the active route. This is done using the relativeTo option within router.Navigate().

To make use of the relativeTo option, import the NavigationExtras interface as follows:

import { ActivatedRoute, NavigationExtras, ParamMap, Router, UrlSegment } 
  from '@angular/router';

We can then specify and extend the navigation as follows:

this.router.navigate(
  [
    { id1:firstParam, id2:mySecondParam }
  ], 
  { relativeTo: this.activatedRoute }
);

Under the hood this extracts the relative route URL prefix from the components injected ActivatedRoute instance.

Additional routing details can be referenced from the Angular developer site.

That is all for today’s post.

I hope you have found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial