【问题标题】:How to get all route params/data如何获取所有路由参数/数据
【发布时间】:2017-02-17 14:41:19
【问题描述】:

给定一个路由配置

{
   path: '/root/:rootId',
   children: [{
       path: '/child1/:child1Id',
       children: [{
           path: '/child2/:child2Id
           component: TestComponent
       }]
   }]
}

在 TestComponent 中如何轻松获取所有路由参数。我想知道是否有比这更简单的方法

let rootId = route.parent.parent.snapshot.params;
let child1Id = route.parent.snapshot.params;
let child2Id = route.snapshot.params;

这似乎过于多余,尤其是当我正在观察可观察的路由参数而不是通过路由快照访问参数时。这种方法似乎也很脆弱,因为如果我移动了任何路线/参数,它就会中断。我习惯于有角度的 ui-router,其中为单个对象 $stateParams 提供了所有易于访问的参数数据。我对从路由树中的单个节点访问的路由解析数据也有同样的担忧。任何帮助将非常感激。提前致谢

【问题讨论】:

    标签: angular angular-router


    【解决方案1】:

    从 Angular 5.2 开始,您可以进行路由器配置以将所有参数继承到子状态。如果对血淋淋的细节感兴趣,请参阅this commit,但它是如何为我工作的:

    无论您在哪里调用RouterModule.forRoot(),都包括一个配置对象,其继承策略设置为always(默认为emptyOnly):

    import {RouterModule, ExtraOptions} from "@angular/router";
    
    export const routingConfiguration: ExtraOptions = {
      paramsInheritanceStrategy: 'always'
    };
    
    export const Routing = RouterModule.forRoot(routes, routingConfiguration);
    

    现在,当您在子组件中查看ActivatedRoute 时,祖先的参数会出现在那里(例如activatedRoute.params),而不是像activatedRoute.parent.parent.parent.params 这样的杂乱无章的东西。您可以直接访问该值(例如activatedRoute.params.value.userId)或通过activatedRoute.params.subscribe(...)订阅。

    【讨论】:

    • 你还需要订阅activatedRoute.params.subscribe(...),但是很有帮助!
    • 不幸的是,这仍然没有解决能够从顶级路由访问所有子路由参数的问题,这只适用于相反的情况。我想在顶级路线上获取所有子参数,甚至下降 3 或 4 级,如果不存在则为空字符串/未定义。冈特的回答应该适用于此。
    • @Lansana 确实,这并没有做到这一点。我首先使用了 Gunter 的解决方案,它适用于许多情况,但我认为我在解析器中遇到了麻烦,因为数据不可用,IIRC。不过,它是否适合您的需求值得一试。
    • 如果您可以使用ActivatedRouteSnapshot,很简单,只需致电activatedRouteSnapshot.parent.params
    • @Lansana 我不关注。从顶部访问是什么意思?每个路由都绑定到组件。如果某个组件有子组件,那么这些子组件也有自己的路由,这是合乎逻辑的,即使父组件也会被渲染,父组件也不应该能够访问子组件的参数。
    【解决方案2】:

    您需要迭代路线段。

    类似

    var params = [];
    var route = router.routerState.snapshot.root;
    do {
     params.push(route.params); 
     route = route.firstChild;
    } while(route);
    

    这将为您提供每个路线段的params 列表,然后您可以从中读取所需的参数值。

    Object.keys(params) 可能会从param 实例中获取所有可用的参数名称。

    【讨论】:

    【解决方案3】:
    constructor(private route: ActivatedRoute) {}
    
    data$ = of(this.route).pipe(
        expand(route => of(route['parent'])),
        takeWhile(route => !!route['parent']),
        pluck('snapshot', 'data'),
        reduce(merge));
    

    说明:

    在这里调用 expand 会为父链中的每个路由创建一个 observable。使用 Takewhile 以便在 route['parent'] 返回 null 时或在根路由处停止递归。

    然后,对于 observable 中的所有这些路由,pluck 会将每个路由映射到它的 'snapshot.data' 属性。

    最后,从 lodash 为 reduce 提供了合并功能,以将所有数据对象合并为一个对象。该流通过父路由聚合来自当前路由的所有数据

    【讨论】:

    • @Allan 当然。在这里调用 expand 会为父链中的每个路由创建一个 observable。使用 Takewhile 以便在 route['parent'] 返回 null 时或在根路由处停止递归。然后,对于 observable 中的所有这些路由,pluck 会将每个路由映射到它的 'snapshot.data' 属性。最后,从 lodash 为 reduce 提供了合并功能,以将所有数据对象合并为一个对象。该流通过父路由聚合来自当前路由的所有数据
    • so as "reduce((acc, val) => ({ ...acc, ...val }))"
    • 上帝的圣爱。其他人会不会觉得这是一项非常荒谬的工作,只是为了获取该死的 url 参数?
    【解决方案4】:
    /**
     * Get all activated route snapshot param under lazy load modules
     * @see RouterModule.forChild(routes)
     */
    public static getSnapshotParams(route: ActivatedRoute): { [key: string]: string } {
        const params: { [key: string]: string } = {};
        do {
            const param = route.snapshot.params;
            for (let key in param) {
                params[key] = param[key];
            }
            route = route.parent;
        } while (route);
        return params;
    }
    
    /**
     * Get all activated route stream observable param under lazy load modules
     * @see RouterModule.forChild(routes)
     * @see Observable
     * @see Params
     */
    public static getRouteParam(route: ActivatedRoute, routeKey: string): Observable<number> {
        const obs: Observable<Params>[] = [];
        do {
            obs.push(route.params);
            route = route.parent;
        } while (route);
        return merge(...obs).pipe(
            filter(param => !!param[routeKey]),
            map(param => param[routeKey])
        );
    }
    

    }

    【讨论】:

      【解决方案5】:

      来自所有活动 Outlets 的路由参数

      如果您有一个用例,您需要知道 secondary outlet 中路由的组件中 primary outlet 的参数,此函数会从 all 收集 all 参数> 递归的活跃网点:

      const getParams = (route) => ({
        ...route.params,
        ...route.children.reduce((acc, child) =>
          ({ ...getParams(child), ...acc }), {}) 
      });
      
      getParams(router.routerState.snapshot.root);
      

      如果当前 URL 是 /path/to/42(outlet:path/to/99),则生成的对象可能是:

      { primaryParam: '42', outletParam: '99' }
      

      对象中的键取决于您在 Angular 中的路由配置。

      【讨论】:

        【解决方案6】:

        我创建了以下服务,以便能够以ParamMap 的形式获取所有路由参数。其主要思想是递归解析子路由中的所有参数。

        Github gist

        import {Injectable} from '@angular/core';
        import {
          ActivatedRoute,
          Router,
          NavigationEnd,
          ParamMap,
          PRIMARY_OUTLET,
          RouterEvent
        } from '@angular/router';
        import {Observable} from 'rxjs/Observable';
        import 'rxjs/add/operator/filter';
        import 'rxjs/add/operator/map';
        
        @Injectable()
        export class ParamMapService {
        
          paramMap: Observable<ParamMap>;
        
          constructor(private router: Router,
                      private route: ActivatedRoute) {
            this.paramMap = this.getParamMapObservable();
          }
        
          private getParamMap(route: ActivatedRoute): ParamMap {
            const map: Map<string, string | string[]> = new Map();
        
            while (route) {
              route.snapshot.paramMap.keys.forEach((key) => {
                map.set(key, this.getParamMapValue(route.snapshot.paramMap, key));
              });
              route = route.firstChild;
            }
        
            return <ParamMap>{
              keys: this.getParamMapKeys(map),
              has: this.getParamMapMethodHas(map),
              get: this.getParamMapMethodGet(map),
              getAll: this.getParamMapMethodGetAll(map)
            };
          }
        
          private getParamMapMethodGet(map: Map<string, string | string[]>): (name: string) => string | null {
            return (name: string): string | null => {
              const value = map.get(name);
              if (typeof value === 'string') {
                return value;
              }
              if (Array.isArray(value) && value.length) {
                return value[0];
              }
              return null;
            };
          }
        
          private getParamMapMethodGetAll(map: Map<string, string | string[]>): (name: string) => string[] {
            return (name: string): string[] => {
              const value = map.get(name);
              if (typeof value === 'string') {
                return [value];
              }
              if (Array.isArray(value)) {
                return value;
              }
              return [];
            };
          }
        
          private getParamMapMethodHas(map: Map<string, string | string[]>): (name: string) => boolean {
            return (name: string): boolean => map.has(name);
          }
        
          private getParamMapKeys(map: Map<string, string | string[]>): string[] {
            return Array.from(map.keys());
          }
        
          private getParamMapObservable(): Observable<ParamMap> {
            return this.router.events
              .filter((event: RouterEvent) => event instanceof NavigationEnd)
              .map(() => this.route)
              .filter((route: ActivatedRoute) => route.outlet === PRIMARY_OUTLET)
              .map((route: ActivatedRoute) => this.getParamMap(route));
          }
        
          private getParamMapValue(paramMap: ParamMap, key: string): string | string[] {
            return (paramMap.getAll(key).length > 1 ? paramMap.getAll(key) : paramMap.get(key));
          }
        }
        

        示例用法

        id;
        
        constructor(private paramMapService: ParamMapService) {
          this.paramMapService.paramMap.subscribe(paramMap => {
            this.id = paramMap.get('id');
          });
        }
        

        注意

        ES6 Map 正在服务中使用。要支持旧版浏览器,请执行以下操作之一:

        • 取消注释polyfills.ts 中的import 'core-js/es6/map'; 行,
        • 将所有 Map 实例转换为一个简单对象。

        【讨论】:

          【解决方案7】:

          角度 5:

          import { combineLatest } from 'rxjs/observable/combineLatest';
          
          
          
           constructor(
              private activatedRoute: ActivatedRoute
            ) {
              combineLatest(
                this.activatedRoute.data,
                this.activatedRoute.params
              ).subscribe(([{from}, params]) => {
                this.params.from = from;
                this.params.id = params['id'];
                this.loadData();
              });
            }
          

          【讨论】:

            【解决方案8】:

            我通过这种方式获得了第一个路由参数:

            console.log(this.route.snapshot.firstChild.params)

            【讨论】:

            • 这并不适用于任何地方。当路由对象已经被处理时,这将是在非常特定的场景中。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-10-14
            • 2013-02-19
            • 2013-04-30
            • 2021-09-18
            • 2013-12-16
            • 1970-01-01
            相关资源
            最近更新 更多