Reputation:
I have the following routing configuration.
@RouteConfig([
{
path: '/home',
name: 'Homepage',
component: HomepageComponent,
useAsDefault: true
}
)
export class AppComponent {
}
whenever the browser is pointed to /home
this route works but not for /Home
or any other case variations. How can I make the router to route to the component without caring the case.
thanks
Upvotes: 42
Views: 25833
Reputation: 442
Here is an updated version of LowerCaseUrlSerializer
to not lowercase the query parameters.
import { DefaultUrlSerializer, UrlTree } from '@angular/router';
export class LowerCaseUrlSerializer extends DefaultUrlSerializer {
parse(url: string): UrlTree {
const path = url.split('?')[0];
const query = url.split('?')[1] || '';
return super.parse(path.toLowerCase() + (query !== '' ? `?${query}`: ''));
}
}
Upvotes: 3
Reputation: 531
Here's what I did.
import { DefaultUrlSerializer, UrlTree } from '@angular/router';
export class LowerCaseUrlSerializer extends DefaultUrlSerializer {
parse(url: string): UrlTree {
// Optional Step: Do some stuff with the url if needed.
// If you lower it in the optional step
// you don't need to use "toLowerCase"
// when you pass it down to the next function
return super.parse(url.toLowerCase());
}
}
And
@NgModule({
imports: [
...
],
declarations: [AppComponent],
providers: [
{
provide: UrlSerializer,
useClass: LowerCaseUrlSerializer
}
],
bootstrap: [AppComponent]
})
Upvotes: 52
Reputation: 6652
A little fix to Timothy's answer, which doesn't change the case of matched parameter names and values:
import {Route, UrlSegment, UrlSegmentGroup} from '@angular/router';
export function caseInsensitiveMatcher(url: string) {
return function(
segments: UrlSegment[],
segmentGroup: UrlSegmentGroup,
route: Route
) {
const matchSegments = url.split('/');
if (
matchSegments.length > segments.length ||
(matchSegments.length !== segments.length && route.pathMatch === 'full')
) {
return null;
}
const consumed: UrlSegment[] = [];
const posParams: {[name: string]: UrlSegment} = {};
for (let index = 0; index < matchSegments.length; ++index) {
const segment = segments[index].toString().toLowerCase();
const matchSegment = matchSegments[index];
if (matchSegment.startsWith(':')) {
posParams[matchSegment.slice(1)] = segments[index];
consumed.push(segments[index]);
} else if (segment.toLowerCase() === matchSegment.toLowerCase()) {
consumed.push(segments[index]);
} else {
return null;
}
}
return { consumed, posParams };
};
}
Edit: beside the problem explained above, there is another subtle bug which is resolved now. The for loop should iterate over matchSegments
instead of segments
.
Upvotes: 11
Reputation: 3231
Note: The following works in Angular 4.x
I just use a matcher function:
import { RouterModule, UrlSegment, UrlSegmentGroup, Route } from '@angular/router';
function CaseInsensitiveMatcher(url: string) {
url = url.toLowerCase();
return function(
segments: UrlSegment[],
segmentGroup: UrlSegmentGroup,
route: Route
) {
let matchSegments = url.split('/');
if (
matchSegments.length > segments.length ||
(matchSegments.length !== segments.length && route.pathMatch === 'full')
) {
return null;
}
let consumed: UrlSegment[] = [];
let posParams: {[name: string]: UrlSegment} = {};
for (let index = 0; index < segments.length; ++index) {
let segment = segments[index].toString().toLowerCase();
let matchSegment = matchSegments[index];
if (matchSegment.startsWith(':')) {
posParams[matchSegment.slice(1)] = segments[index];
consumed.push(segments[index]);
}
else if (segment === matchSegment) {
consumed.push(segments[index]);
}
else {
return null;
}
}
return { consumed, posParams };
}
}
With:
@NgModule({
imports: [
...
RouterModule.forRoot([
{ matcher: CaseInsensitiveMatcher('user/:id'), component: ProfileComponent },
])
],
declarations: [
...
],
bootstrap: [
AppComponent
]
})
Edit: I just found out that in order to work with AoT compilation, the @NgModule portion would look more like this:
export function profileMatch() {
return CaseInsensitiveMatcher('user/:id').apply(this, arguments);
}
@NgModule({
imports: [
...
RouterModule.forRoot([
{ matcher: profileMatch, component: ProfileComponent },
])
],
declarations: [
...
],
bootstrap: [
AppComponent
]
})
Upvotes: 7
Reputation: 481
I did this in my app.component.ts:
export class AppComponent implements OnInit {
constructor(private _route: Router) { }
public ngOnInit() {
this._route.events.subscribe(event => {
if (event instanceof NavigationStart) {
let url = (<NavigationStart>event).url;
if (url !== url.toLowerCase()) {
this._route.navigateByUrl((<NavigationStart>event).url.toLowerCase());
}
}
});
}
}
and this is needed on router-link to make it active when selected:
<a routerLink="/heroes" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: false }">Heroes</a>
Upvotes: 6
Reputation: 657058
update
This didn't make it into the new router yet
original
Regex matchers were introduced recently. This might help for your use case.
See https://github.com/angular/angular/pull/7332/files
And this Plunker from Brandon Roberts
@RouteConfig([
{ name : 'Test',
//path : '/test',
regex: '^(.+)/(.+)$',
serializer: (params) => new GeneratedUrl(`/${params.a}/${params.b}`, {c: params.c}),
component: Test
// useAsDefault: true
}
])
Upvotes: 8
Reputation: 2224
My workaround for this issue:
/* IMPORTS */
let routeEntries = [
{ path: '/home', component: HomePage }
/* DEFAULT ROUTE */
{ path: '/', component: HomePage },
/* NOT FOUND ROUTE */
{ path: '*', component: HomePage }
];
@Component(/* SETUP */)
@Routes(routeEntries)
export App implements OnInit {
/* STUFF */
ngOnInit() {
// subscribe to router changes
this.router.changes.forEach(() => {
let routerLink = location.pathname;
let routerLinkLower = routerLink.toLowerCase();
// if the route does not exist, check for the lowercase version
if (!routeEntries.find((route) => { return route.path === routerLink; })) {
this.router.navigate([routerLinkLower]);
}
// if the route is correct, set the active page
else {
// do something here if required
}
});
}
}
This works with the RC1 router and goes along well with route variations, eg:
'{domain}:{port}/HoMe'
'{domain}:{port}/homE'
'{domain}:{port}/Home'
...
Upvotes: 1