Context
Angular provides two approaches for routing, called Location Strategies. They are the HashLocationStrategy
and the PathLocationStrategy
. One key difference is that the HashLocationStrategy
puts a #
in all component URLs, whereas the PathLocationStrategy
does not. This means that it's a breaking change to move from one to the other, because any bookmarks your users have will stop working.
I was recently working with an application which had been set up using the HashLocationStrategy
. I implemented an OAuth 2 login process and needed a callback URL which should be within the angular app. However, the OAuth 2 specification does not allow callback URLs to include a #
character.
I didn't want to break users' bookmarks by changing to use the PathLocationStrategy
, but I also needed a URL which didn't contain a #
for one specific component. What could I do which would meet both these requirements?
The solution
I decided to implement my own CustomHashLocationStrategy
, based on the built-in HashLocationStrategy
. The callback route will not have a #
, and the CustomHashLocationStrategy
is responsible for detecting this route and adding the #
to the front of it. This is the only custom behaviour, it is otherwise identical to HashLocationStrategy
. This way, existing bookmarks will continue to work, because the app is still using the HashLocationStrategy
.
Code
This is the class I wrote. The only custom logic it contains is a single if
statement.
import { APP_BASE_HREF, HashLocationStrategy, PlatformLocation } from '@angular/common';
import { Inject, Injectable, Optional } from '@angular/core';
@Injectable()
export class CustomHashLocationStrategy extends HashLocationStrategy {
constructor(private platformLocation: PlatformLocation,
@Optional() @Inject(APP_BASE_HREF) _baseHref?: string) {
super(platformLocation, _baseHref);
}
path(includeHash: boolean = false): string {
let path = this.platformLocation.hash;
if (path == null) {
path = '#';
}
// This if block is the only custom code in this class.
// The rest is the same as the HashLocationStrategy.
// https://github.com/angular/angular/blob/13.3.2/packages/common/src/location/hash_location_strategy.ts#L65
if (location.pathname === '/path-to-callback') {
path = `#${location.pathname}/${location.search}`;
}
return path.length > 0 ? path.substring(1) : path;
}
}