【问题标题】:Angular 2 optional route parameterAngular 2 可选路由参数
【发布时间】:2016-03-16 11:50:07
【问题描述】:

Angular 2 路由中是否可以有一个可选的路由参数?我在 RouteConfig 中尝试了 Angular 1.x 语法,但收到以下错误:

“原始异常:路径“/user/:id?”包含“?”,这在路由配置中是不允许的。”

@RouteConfig([
{
    path: '/user/:id?',
    component: User,
    as: 'User'
}])

【问题讨论】:

    标签: javascript angular


    【解决方案1】:

    你可以定义多条带参数和不带参数的路由:

    @RouteConfig([
        { path: '/user/:id', component: User, name: 'User' },
        { path: '/user', component: User, name: 'Usernew' }
    ])
    

    并处理组件中的可选参数:

    constructor(params: RouteParams) {
        var paramId = params.get("id");
    
        if (paramId) {
            ...
        }
    }
    

    另见相关 github 问题:https://github.com/angular/angular/issues/3525

    【讨论】:

    • 如果我错了,请纠正我,但这个解决方案只有在数组中的路由顺序颠倒时才对我有用,即带有参数的路由出现在另一个之前。在我这样做之前,路由器只匹配没有参数的路由。
    • 这个解决方案还适用吗?我注意到从“用户”移动到“用户新”将重新实例化“用户”组件
    • 老了,但是这种方法的一个主要问题是每条路由都被视为唯一的路由,这使得组件无法重用。
    • 正如@teleaziz 所指出的,附加参数将重新渲染组件。为了避免这种情况,Martin Cremer 的回答是;添加一个带有空白参数值的“redirectTo”根,对我来说效果很好:stackoverflow.com/a/49159166/1364650 - 但这很hacky,我认为它们应该只正确支持可选的路由参数。
    • 对于那些想知道为什么RouteParams 不导入组件的人,请检查:stackoverflow.com/a/36792261/806202。解决方法是使用ActivatedRoute:route.snapshot.params['routeParam']
    【解决方案2】:
    {path: 'users', redirectTo: 'users/', pathMatch: 'full'},
    {path: 'users/:userId', component: UserComponent}
    

    这样在添加参数时组件不会重新渲染。

    【讨论】:

    • 这个答案是最好的。它不会重新渲染同一个组件,也不需要多个组件。
    • 最好的答案,但是我添加了pathMatch: 'full' 来重定向,否则像users/admin 这样的路径在我的情况下也会被重定向
    • 只有当您在浏览器中查看的 URL 尾部斜杠没问题时,这个答案才是最好的。考虑一个表示“未定义 id”的值,例如 /users/all/users/home,将“全部”或“家庭”读作 id,如果它与您的魔法值匹配,则简单地忽略它。然后上面的第一行变成redirectTo: 'users/home' 或任何你决定的。对我来说,尾部的斜线真的很明显是有问题的。
    • @Simon_Weaver 我同意。我找到了另一个使用没有这个问题的匹配器的解决方案:stackoverflow.com/a/56391974/664533
    • 这是一个简单的咒语,但非常牢不可破 :D 最好的解决方案!
    【解决方案3】:

    当信息可选时,建议使用查询参数。

    路由参数还是查询参数?

    没有一成不变的规则。一般来说,

    首选路由参数时

    • 该值是必需的。
    • 该值是区分一条路径与另一条路径所必需的。

    首选查询参数时

    • 该值是可选的。
    • 该值是复杂的和/或多变量的。

    来自https://angular.io/guide/router#optional-route-parameters

    只需要从路由路径中取出参数即可。

    @RouteConfig([
    {
        path: '/user/',
        component: User,
        as: 'User'
    }])
    

    【讨论】:

    • 更改可选路由参数会重新渲染组件,但更改 queryParams 不会。此外,如果您在路线导航之前解析了一些数据,则每次更改可选路线参数时都会请求它。
    • 仅供参考,该锚链接不再起作用。新链接似乎是Route Parameters: Required or optional?
    【解决方案4】:

    Angular 4 - 解决可选参数排序的解决方案:

    这样做:

    const appRoutes: Routes = [
      {path: '', component: HomeComponent},
      {path: 'products', component: ProductsComponent},
      {path: 'products/:id', component: ProductsComponent}
    ]
    

    请注意,productsproducts/:id 路由的名称完全相同。对于没有参数的路由,Angular 4 将正确地遵循 products,如果有参数,它将遵循 products/:id

    但是,非参数路由products 的路径必须有斜杠,否则 Angular 会错误地将其视为参数路径。所以在我的例子中,我的产品有斜线,但它不起作用。

    不要这样做:

    ...
    {path: 'products/', component: ProductsComponent},
    {path: 'products/:id', component: ProductsComponent},
    ...
    

    【讨论】:

    • 如果两者都去ProductsComponent,你如何处理那里的可选参数?
    • 您可以像这样访问 ProductsComponent 中的 :id1、:id2 等参数以及请求的 url: this.route.url.first() .mergeMap((url) => { / / console.log('1: 检测到 url 更改' + url); return this.route.params.do((params) => { // console.log('2: 检测到 url + params 更改' + params[" id1"] + ' ' + params["id2"]); this.id1 = params["id1"]; this.id2 = params["id2"]; }) })
    • 请记住,您也可以将data 传递给组件,即使是相同的组件,每个路由也可能不同。可以使用示例{path: 'products', component: ProductsComponent, data: { showAllProducts: true} },,然后检查showAllProducts。比检查 null 好一点,但对于更简单的情况,两者都可以。
    • 不幸的是,这个解决方案会阻止 Angular 在 products 和 products/:id 之间重用组件。该组件将被重新实例化。
    • @Kodiak - 我不认为这是正确的。我的理解是,在 app.module.ts 中,ProductsComponent 被实例化一次,然后角度引擎在每个可导航事件(产品和产品/:id 等)上重新使用实例化的 ProductsComponent。您能否解释或演示 ProductsComponent 如何在上面的代码中重新实例化,以及您将如何防止重新实例化?
    【解决方案5】:

    rerezz 的答案非常好,但它有一个严重的缺陷。它会导致User 组件重新运行ngOnInit 方法。

    当您在那里做一些繁重的事情并且当您从非参数路由切换到参数路由时不希望它重新运行时,这可能会出现问题。虽然这两个路由是为了模仿一个可选的 url 参数,而不是成为 2 个单独的路由。

    这是我解决问题的建议:

    const routes = [
      {
        path: '/user',
        component: User,
        children: [
          { path: ':id', component: UserWithParam, name: 'Usernew' }
        ]
      }
    ];
    

    然后您可以将负责处理参数的逻辑移动到UserWithParam 组件中,并将base 逻辑留在User 组件中。当您从 /user 导航到 /user/123 时,您在 User::ngOnInit 中所做的任何事情都不会再次运行。

    不要忘记将<router-outlet></router-outlet> 放入User 的模板中。

    【讨论】:

    • 如果性能至关重要,那么避免重新创建组件是一件好事。我还有另一个解决方案,它也避免了重新创建组件:stackoverflow.com/a/56391974/664533
    【解决方案6】:

    这里的建议答案,包括来自rerezzaccepted answer,它建议添加多个路由条目可以正常工作。

    但是,当在路由条目之间更改时,将重新创建组件,即在带有参数的路由条目和不带参数的条目之间。

    如果你想避免这种情况,你可以创建自己的路由匹配器来匹配两条路由:

    export function userPageMatcher(segments: UrlSegment[]): UrlMatchResult {
        if (segments.length > 0 && segments[0].path === 'user') {
            if (segments.length === 1) {
                return {
                    consumed: segments,
                    posParams: {},
                };
            }
            if (segments.length === 2) {
                return {
                    consumed: segments,
                    posParams: { id: segments[1] },
                };
            }
            return <UrlMatchResult>(null as any);
        }
        return <UrlMatchResult>(null as any);
     }
    

    然后在你的路由配置中使用匹配器:

    const routes: Routes = [
        {
            matcher: userPageMatcher,
            component: User,
        }
    ];
    

    【讨论】:

    • @KevinBeal 我已经实现了很多与 AOT 配合使用的匹配器。您在这里遇到什么错误?
    • 糟糕。那是另一回事。我的匹配器适用于 AOT。
    • 这有点棘手,但是解决这个问题的最佳方法
    • IMO,这是这个问题的最正确答案。我希望我能给这个答案更多的投票。我不敢相信互联网上没有更多关于此的帖子。我希望 Angular 文档的结构更好。目前,您几乎必须阅读他们所有的教程,以免错过有用的功能。
    【解决方案7】:

    使用 angular4 我们只需要在层次结构中组织路线

    const appRoutes: Routes = [
      { 
        path: '', 
        component: MainPageComponent 
      },
      { 
        path: 'car/details', 
        component: CarDetailsComponent 
      },
      { 
        path: 'car/details/platforms-products', 
        component: CarProductsComponent 
      },
      { 
        path: 'car/details/:id', 
        component: CadDetailsComponent 
      },
      { 
        path: 'car/details/:id/platforms-products', 
        component: CarProductsComponent 
      }
    ];
    

    这对我有用。这样路由器就可以根据选项 id 参数知道下一条路由是什么。

    【讨论】:

      【解决方案8】:

      在 Angular 8 中,您可以简单地添加参数而无需更改路由器配置。

      Angular Doc Optional param

      在你的Module.routing.module.ts中

      const routes: Routes = [
        { path: 'somePath/:RequiredParam', component: Yourcomponent }
      ];
      

      在您的模板中:

      <div [RouterLink] = ['somePath', requiredParamValue, {optionalParam: value}]></div>
      

      【讨论】:

      【解决方案9】:

      遇到了这个问题的另一个实例,并在寻找解决方案时来到这里。我的问题是我在做孩子,以及延迟加载组件以优化一些东西。简而言之,如果您延迟加载父模块。主要是我在路线中使用'/:id',并且抱怨'/'是其中的一部分。这里不是确切的问题,但它适用。

      来自父级的应用路由

      ...
      const routes: Routes = [
        {
          path: '',
          children: [
            {
              path: 'pathOne',
              loadChildren: 'app/views/$MODULE_PATH.module#PathOneModule'
            },
            {
              path: 'pathTwo',
              loadChildren: 'app/views/$MODULE_PATH.module#PathTwoModule'
            },
      ...
      

      子路由延迟加载

      ...
      const routes: Routes = [
        {
          path: '',
          children: [
            {
              path: '',
              component: OverviewComponent
            },
            {
              path: ':id',
              component: DetailedComponent
            },
          ]
        }
      ];
      ...
      

      【讨论】:

        【解决方案10】:

        使用此匹配器功能,您无需重新渲染组件即可获得理想的行为。当 url.length 等于 0 时,没有可选参数,当 url.length 等于 1 时,有 1 个可选参数。 id - 是可选参数的名称。

          const routes: Routes = [
          {
            matcher: (segments) => {
              if (segments.length <= 1) {
                return {
                  consumed: segments,
                  posParams: {
                    id: new UrlSegment(segments[0]?.path || '', {}),
                  },
                };
              }
              return null;
            },
            pathMatch: 'prefix',
            component: UserComponent,
          }]
        

        【讨论】:

          【解决方案11】:

          Master Detail 视图也有同样的问题。主视图可以在没有 :elementId 参数的情况下可见,但仍应在打开详细信息选择并在 url 中使用 :elementId 的情况下显示。

          我是这样解决的:

          const routes: Routes = [
            {
              path: '',
              component: MasterDetailComponent,
              children: [
                {
                  path: ':elementId',
                  children: [
                    {
                      path: 'details',
                      component: DetailComponent
                    },
                    {
                      path: '',
                      redirectTo: 'details'
                    }
                  ]
                }
              ]
            }
          ];
          

          然后在 MasterDetailComponent 中(例如在 ngOnInit 方法中)您可以使用子路由获取 :elementId

          const childRouteWithElementId = this.route.snapshot.children[0];
          const elementIdFromUrl = childRouteWithElementId.params.elementId;
          if (!!elementIdFromUrl ) {
            // Do what you need to with the optional parameter
          }
          

          当然,你可以在没有子路由的情况下做同样的事情,并且在 url 的末尾只有可选的 elementId

          【讨论】:

            【解决方案12】:

            有三种方法可以通过路由将路由参数从一个组件发送到另一个组件。但首先将这些库导入到组件相关的 .ts 文件中并在构造函数中定义

            private route: ActivatedRoute
            private router: Router
            

            第一种方式:必需的路由参数

            //Route Configuration
            {path: 'user/:id', component: UserDetailComponent}
            
            //Set Hyperlink/routerLink
            <a [routerLink]="['/user', user.id]"></a> 
            
             //Requesting Url after click on hyperlink
             http://localhost:4200/user/6
            
            //Now you can read id value in navigated component
            this.route.snapshot.paramMap.get('id');
            

            第二种方式:可选路径参数

            //Route Configuration
                {path: 'user', component: UserDetailComponent}
                
                //Set Hyperlink/routerLink
                <a [routerLink]=['/user', {name: userName, status: true}]"></a>
            
            
            //Requesting Url after click on hyperlink
                http://localhost:4200/user;name:userNameValue;status:true
            
            //Now you can read values in navigated component
                this.route.snapshot.paramMap.get('userId');
                this.route.snapshot.paramMap.get('userName');
            

            第三种方式:可选路径参数

            //Route Configuration
                {path: 'user', component: UserDetailComponent}
                
                //Set Hyperlink/routerLink
                <a [routerLink]="['/user']"  [queryParms]="{userId:'911', status:true}"></a>
            
                //Requesting Url after click on hyperlink
                http://localhost:4200/user?userId=911&status=true
            
                
                //Now you can read values in navigated component
                this.route.snapshot.paramMap.get('userId');
                this.route.snapshot.paramMap.get('userName');
            

            参考:https://qastack.mx/programming/44864303/send-data-through-routing-paths-in-angular

            【讨论】:

              【解决方案13】:

              我无法发表评论,但参考:Angular 2 optional route parameter

              Angular 6 的更新:

              import {map} from "rxjs/operators"
              
              constructor(route: ActivatedRoute) {
                let paramId = route.params.pipe(map(p => p.id));
              
                if (paramId) {
                  ...
                }
              }
              

              有关 Angular6 路由的更多信息,请参阅 https://angular.io/api/router/ActivatedRoute

              【讨论】:

                【解决方案14】:

                面对延迟加载的类似问题,我已经这样做了:

                const routes: Routes = [
                  {
                    path: 'users',
                    redirectTo: 'users/',
                    pathMatch: 'full'
                  },
                  {
                    path: 'users',
                    loadChildren: './users/users.module#UserssModule',
                    runGuardsAndResolvers: 'always'
                  },
                [...]
                

                然后在组件中:

                  ngOnInit() {
                    this.activatedRoute.paramMap.pipe(
                      switchMap(
                        (params: ParamMap) => {
                          let id: string = params.get('id');
                          if (id == "") {
                            return of(undefined);
                          }
                          return this.usersService.getUser(Number(params.get('id')));
                        }
                      )
                    ).subscribe(user => this.selectedUser = user);
                  }
                

                这边:

                • 没有/ 的路由被重定向到有的路由。因为pathMatch: 'full',只有这样特定的完整路由才会被重定向。

                • 然后,users/:id 被接收。如果实际路线是users/id"",则在ngOnInit 中查看并采取相应措施;否则,id 是 id 并继续。

                • 作用于selectedUser 的其余组件是否未定义(*ngIf 等)。

                【讨论】:

                  猜你喜欢
                  • 2018-03-06
                  • 1970-01-01
                  • 2018-10-06
                  • 1970-01-01
                  • 2016-07-17
                  • 1970-01-01
                  • 2017-05-10
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多