A Neat Trick to Globally Preserve Query Params in Angular Router
The Angular router is a marvel of software engineering. It is based on simple axioms of web navigation.
- URL is nothing but a serialized tree. This means you can deserialize a URL into a tree that represents it.
- URL is the single source of truth for a web application (well…it should be!)
- Routes are a forrest of possible router configurations
- Each router configuration is a tree that can be serialized into a URL
At the moment there is no way to preserve query params globally on all route navigations. For example, if you were checking for the existence of a token
in the URLs to validate if a user can deep link into a URL or not. We can achieve this by Using the age-old Strategy Pattern and Dependency Injection.
Extend Router’s Default Location Strategy
import { PathLocationStrategy, APP_BASE_HREF, PlatformLocation } from '@angular/common';
import { Optional, Inject, Injectable } from '@angular/core';
import { UrlSerializer } from '@angular/router';
@Injectable()
export class PathPreserveQueryLocationStrategy extends PathLocationStrategy {
private get search(): string {
return this.platformLocation?.search ?? '';
}
constructor(
private platformLocation: PlatformLocation,
private urlSerializer: UrlSerializer,
@Optional() @Inject(APP_BASE_HREF) _baseHref?: string,
) {
super(platformLocation, _baseHref);
}
prepareExternalUrl(internal: string): string {
const path = super.prepareExternalUrl(internal);
const existingURLSearchParams = new URLSearchParams(this.search);
const existingQueryParams = Object.fromEntries(existingURLSearchParams.entries());
const urlTree = this.urlSerializer.parse(path);
const nextQueryParams = urlTree.queryParams;
urlTree.queryParams = { ...existingQueryParams, ...nextQueryParams };
return urlTree.toString();
}
}
Explanation:
- We extend the default
PathLocationStrategy
class to implement our query param preservation algorithm - We override the
prepareExternalUrl
method to handle query params in the URL - We Get the existing URLSearch Params as an instance of
[URLSearchParams](<https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams>)
- We create a
[URLTree](<https://angular.io/api/router/UrlTree>)
from the path and merge the existing and new query params and assign it to thequeryParams
of the newly created URL tree - We serialize our newly created URL Tree and return it as a string
Provide The Custom Location Strategy
// app.module.ts
import { LocationStrategy } from "@angular/common";
import { PathPreserveQueryLocationStrategy } from "./preserve-query-params.ts";
@NgModule({
...
providers: [
{ provide: LocationStrategy, useClass: PathPreserveQueryLocationStrategy }
],
...
})
export class AppModule {}
Update TS Config for Dom.iterable Utilities
Caveats
- Since all query params are preserved, once you add a query param to a route, all subsequent navigations will carry it with them
Conclusion
We learnt how we can globally preserve query params in Angular router. We saw how powerful Angular’s dependency injection system is and we leveraged it to override Angular’s default location strategy.
Here’s a code sandbox with a working example:
Thank you for reading and I hope this helps you as it helped me.
I would love to know in the comments below if this was useful!
You can follow me on GitHub or Twitter
Happy Engineering!