Reputation: 3870
I'm replacing an existing AngularJS 1.6.x SPA with an Angular 5.x SPA and I want to make the change transparent to my users.
I'm concerned about users who have bookmarks to the existing app because it has hashes in the URLs (for example: example.com/#/menu
and example.com/#/item/37
);
However, the new app does not have hashes in the URLs (for example: example.com/menu
and example.com/item/37
).
The paths and routing are all the same, with the exception of the #/
in the current app.
Is there a way I can configure the Angular routing to drop the #/
and use the hash-free routing configuration of the new app?
I could duplicate all of my routing to accommodate paths with and without the hash, but there must be a way that doesn't require doubling my code.
// Don't really want to do this:
const routes: Routes = [
{
path: 'menu',
component: MenuComponent
},
{
path: '#/menu',
component: MenuComponent
},
// etc.
];
Similarly, redirecting every #/
path would double the code, too.
// Don't really want to do this:
const routes: Routes = [
{
path: 'menu',
component: MenuComponent
},
{
path: '#/menu',
redirectTo: 'menu'
},
// etc.
];
I'm hoping there is something along these lines:
{
path: '#/*',
redirectTo: '*' // Somehow reference the wildcard part of the path here
}
Thanks in advance for your assistance.
Upvotes: 21
Views: 11563
Reputation: 755
You can use a canActivate
guard to redirect to hash-free URLs when needed:
export const hashRedirectGuard = () =>
((_route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const router = inject(Router);
const url = state.url;
const newUrl = '/' + trimStart(url, '/#');
return url === newUrl || router.parseUrl(newUrl);
}) satisfies CanActivateFn;
// app-routing.module.ts
const APP_ROUTES: Routes = [
{
path: '',
canActivateChild: [hashRedirectGuard()], // Wrap all routes, see: https://stackoverflow.com/a/43487962/12292636
children: [...],
}
];
Upvotes: 0
Reputation: 2982
Migrated an app from HashLocationStrategy
to PathLocationStrategy
and ended up doing this in AppComponent
> ngOnInit
:
this.router.events
.pipe(filter((event) => event instanceof NavigationStart && /^\/#/.test(event.url)))
.subscribe((event: NavigationStart) => this.router.navigateByUrl(event.url.replace('/#', '')));
Note that router.navigate
will strip any query params so I used router.navigateByUrl
instead.
Once this is in place you can safely toggle useHash
to useHash: false
in your RoutingModule and both new and old URLs will continue working!
Upvotes: 0
Reputation: 3870
The answer posted by @Yanis almost worked, but required a few slight tweaks. His answer definitely deserves an upvote; however, below is the working solution I implemented:
export class AppComponent implements OnInit {
constructor (private router: Router) { }
ngOnInit() {
this.router.events.subscribe(event => {
if (event instanceof NavigationStart) {
if (!!event.url && event.url.match(/^\/#/)) {
this.router.navigate([event.url.replace('/#', '')]);
}
}
});
}
}
Upvotes: 22
Reputation: 7875
i don't know if is right way to do it. But you can do something like this : Goal is to subscribe to NavigationChange, then you check if your current route start by '#!', if yes, you redirect to the right route.
class AppComponent implement OnInit {
constructor(router: Router) {
//When url change, we check if actual url have #! on it, then we redirect to the route without it.
router.events.subscribe((event: NavigationEvent): void => {
this.url = router.url;
if (this.url.match('/^#!/')) {
this.router.navigate(
this.url.replace('#!','')
);
}
}
);
}
}
Another approch more complicated in my opinion is to use custom "matcher". More information here :
https://github.com/angular/angular/issues/12442
Upvotes: 11