Angular — use APP_BASE_HREF to dynamically calculate case-insenstive baseHref
Use case
Let’s say you are building an app with multiple modules and you want all your app’s baseHref
to be case-insensitive, so that when no matter visiting /baseHref/route
or /BaseHref/route
all are able to be redirect to /route
successfully (most of the time you probably would handle this in backend or configuration level, but assume for whatever reason you want to handle it in client side).
What’s the tricky part?
At first glance, you probably would think there might be something you can do to handle it in Router
level somewhere, at least that’s where I fell through at the first try. After digging into angular’s routing process, I finally realize that baseHref
is a low level thing that should be handled before even passing to do routing calculation.
What’s the default behavior?
Assume baseHref='baseHref'
, when visiting a url http://xxxx/BASEHREF/route?param='param'
, the routes are calculated in the follow processes:
- default
PathLocationStrategy
calculatespath
with normalizing query qarameters and withplatformLocation.pathname
as it is (see How angular location_strategy calculates path) - then
Location
calcultespath
by removing thebaseHref
(see location.ts). becausebaseHref='baseHref'
, visiting URL’sBASEHREF
is not matching thebaseHref
,Location
‘sPath
would be calculated as/BASEHREF/route?param='param'
other than expected/route?param='param'
- angular’s
Router
module then would navigate to/BASEHREF/route
other than the expected/route
Solution
So if dynamcally calculate baseHref
based on the visiting URL(eg: if configured baseHref=’baseHref'
, when visiting http://xxxx/BASEHREF/route
dynamically set baseHref='BASEHREF'
), then in process 2) Location
would calculate path
correctly.
step1: provide APP_BASE_HREF
in the app module with custom factory CaseInsensitiveBaseHref
and deps: [PlatformLocation]
providers: [ { provide: APP_BASE_HREF, useFactory: CaseInsensitiveBaseHref, deps: [PlatformLocation], }],
step2: create a CaseInsensitiveBaseHref
function to calculate baseHref
based on PlatformLocation.getBaseHrefFromDom()
as follows.
Note: below function hanldes couple edge cases based on my project requirements, you can twist it a bit based on your own project needs, you get the idea.
function CaseInsensitiveBaseHrefFactory(platformLocation: PlatformLocation): string {
const path = platformLocation.pathname;
const baseHrefFromDOM = platformLocation.getBaseHrefFromDOM(); // Get baseHref from Dom and remove start and end backslash.
const baseHref = baseHrefFromDOM.replace(/(^\/*|\/*$)/g, ''); // Case-insensitive match baseHref in the path ignoring start backslash.
const caseInSensitiveBaseHrefRegexp = new RegExp(`^\/*${baseHref}`, 'i');
const matchedBaseHref = path.match(caseInSensitiveBaseHrefRegexp);
if (matchedBaseHref) {
const remainingPath = path.slice(matchedBaseHref[0].length);
// Deal with case which contains basePath but should not use matchedBaseHref. Eg: base = 'base', path='BaseBall'.
// matchedBaseHref is the end of the path or follows with white space or a new segment which starts with backslash.
const isValidMatch = !remainingPath || !!remainingPath.match(/^[\/\s]/);
return isValidMatch ? matchedBaseHref[0] : baseHrefFromDOM;
}
return baseHrefFromDOM;
}
The end!