Angular2: 如何使路由路径大小写不敏感

41

我有以下路由配置。

@RouteConfig([
    {
        path: '/home',
        name: 'Homepage',
        component: HomepageComponent,
        useAsDefault: true
    }
)
export class AppComponent {
}
无论浏览器指向 /home 还是 /Home 或其他大小写变化的情况,该路由都只能针对 /home。如何让路由器不区分大小写地路由到组件?
谢谢。

2
你知道这个问题是否有开放的解决方案吗?看起来这应该被整合到新路由器中。Angular 1路由器具有caseInsensitiveMatch=true。 - Mike Lunn
7个回答

51

这是我所做的。

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()); 
    }
}

并且
@NgModule({
    imports: [
      ...
    ],
    declarations: [AppComponent],
    providers: [
        {
            provide: UrlSerializer,
            useClass: LowerCaseUrlSerializer
        }
    ],
    bootstrap: [AppComponent]
})

1
我不得不在@ NgModule上方添加以下导入: import { UrlSerializer, DefaultUrlSerializer } from '@angular/router'; - Captain America
1
当我使用routerLink并将一些大写字母放入其中,例如routerLink="Home"时,这对我不起作用,但是如果我直接在浏览器中输入URL路径,例如/Home,它可以正常工作。 - Dainius Lukša
使用此序列化时要注意,如果您添加任何令牌(例如标识令牌),它们将被转换为小写,导致它们无法正确工作,因为整个 URL 受到影响。 - Marko
6
处理包含查询字符串参数的URL缺失逻辑。我添加了以下逻辑: const urlParts = url.split('?'); urlParts[0] = urlParts[0].toLowerCase(); // 如果您在可选步骤中将其转换为小写, // 则在将其传递给下一个函数时无需使用 "toLowerCase" return super.parse(urlParts.join('?')); - Feng
@Marko - 我刚刚发现了这个问题。你有没有偶然找到替代方案? - Seth
显示剩余4条评论

11
稍作修改 Timothy的答案,使匹配参数名和值时不区分大小写。
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 };
  };
}

编辑:除了上述解释的问题外,还有另一个微妙的错误已经得到解决。现在应该使用matchSegments而不是segments来迭代for循环。


这看起来像是我问题的一个可能解决方案。我想在我的Angular应用程序中尝试一下,通过在shared.module.ts中导出此方法。然后可以在app.module.ts和任何具有子路由配置的子模块中引用它吗?我是TypeScript新手,请原谅我的无知。 - Eakan Gopalakrishnan
@EakanGopalakrishnan,你可以按照Timothy的回答中所述,在RouterModule.forRoot(...)中使用它,或在特定路由定义中使用它。 - Alireza Mirian

8

更新

这个还没有加入新路由器。

原文

最近引入了正则表达式匹配器。这可能有助于您的使用情况。

请参见https://github.com/angular/angular/pull/7332/files

以及来自Brandon Roberts的Plunker

@RouteConfig([
  { name : 'Test',
    //path : '/test',
    regex: '^(.+)/(.+)$', 
    serializer: (params) => new GeneratedUrl(`/${params.a}/${params.b}`, {c: params.c}),
    component: Test
//   useAsDefault: true
  }
])

对于预定义路径,使用正则表达式匹配是否比根据评论中某人建议的 caseInsensitiveMatch: true 有更多优势?只是问问而已,因为它在不需要额外更改的情况下运行良好。 - user5539517
我自己还没用过。只是记得这是最近引入的。 caseInsensitiveMatch:true 在哪里提到?我还没有看到过。 - Günter Zöchbauer
该评论已被删除。 - user5539517
1
我同意Gunter的看法,似乎caseInsensitiveMatch是angular2/router中旧路由器的一部分 - @angular/router中的新路由器尚未具备此功能。 - J King

7

注意:以下内容适用于Angular 4.x

我只是使用了一个匹配函数:

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 };
    }
}

使用:

@NgModule({
    imports: [
        ...
        RouterModule.forRoot([
            { matcher: CaseInsensitiveMatcher('user/:id'), component: ProfileComponent },
        ])
    ],
    declarations: [
        ...
    ],
    bootstrap: [
        AppComponent
    ]
})

编辑:我刚刚发现,为了使用AoT编译,@NgModule部分应该如下所示:

export function profileMatch() {
    return CaseInsensitiveMatcher('user/:id').apply(this, arguments);
}

@NgModule({
    imports: [
        ...
        RouterModule.forRoot([
            { matcher: profileMatch, component: ProfileComponent },
        ])
    ],
    declarations: [
        ...
    ],
    bootstrap: [
        AppComponent
    ]
})

1
谢谢,它可以工作,但它还会将参数名称和值转换为小写,这可能会带来麻烦。例如,如果您将路径定义为 users/:userId,则路由中的 userId 将是 undefined。我添加了解决此问题的方法 - Alireza Mirian
你好,我看到了你的回答,很棒。但是我们如何确定AoT是否正在工作呢?我已经在你/@AlirezaMirian的答案中附加了更多的代码,因此我对其影响产生了好奇。 - petrosmm

6
我在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());
        }
      }
    });
  }
}

在router-link上需要添加这个属性,以便在选中时使其处于活动状态:

<a routerLink="/heroes" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: false }">Heroes</a>

2
这里是更新后的 LowerCaseUrlSerializer 版本,不会将查询参数转换为小写。
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}`: ''));
    }
}

1

这个问题的解决方法:

/* 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
            }
        });
    }
}

这适用于RC1路由器,并与路由变体配合良好,例如:
'{domain}:{port}/HoMe'
'{domain}:{port}/homE'
'{domain}:{port}/Home'
...

你的路由参数会发生什么?它们也会被转换为小写吗? - Jim Buck
是的,我没有考虑到这些,说实话,在上面开发的应用程序中,路由并不是一个问题,所以我没有考虑到它。所以很遗憾,似乎它不能涵盖所有情况。 - Vlad Jerca

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接