首页 文章

Angular 2:从父组件获取RouteParams

提问于
浏览
64

如何从父组件获取RouteParams?

App.ts

@Component({
  ...
})

@RouteConfig([
  {path: '/', component: HomeComponent, as: 'Home'},
  {path: '/:username/...', component: ParentComponent, as: 'Parent'}
])

export class HomeComponent {
  ...
}

然后,在 ParentComponent 中,我可以轻松获取用户名参数并设置子路由 .

Parent.ts

@Component({
  ...
})

@RouteConfig([
  { path: '/child-1', component: ChildOneComponent, as: 'ChildOne' },
  { path: '/child-2', component: ChildTwoComponent, as: 'ChildTwo' }
])

export class ParentComponent {

  public username: string;

  constructor(
    public params: RouteParams
  ) {
    this.username = params.get('username');
  }

  ...
}

但是,如何在这些子组件中获得相同的“用户名”参数?做与上面相同的技巧,不会这样做 . 因为这些参数是在ProfileComponent上定义的还是什么?

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

  constructor(
    public params: RouteParams
  ) {
    this.username = params.get('username');
    // returns null
  }

  ...
}

12 回答

  • 0

    使用RxJS的 Observable.combineLatest ,我们可以得到一些接近惯用的params处理:

    import 'rxjs/add/operator/combineLatest';
    
    import {Component} from '@angular/core';
    import {ActivatedRoute, Params} from '@angular/router';
    import {Observable} from 'rxjs/Observable';
    
    @Component({ /* ... */ })
    export class SomeChildComponent {
      email: string;
      id: string;
    
      constructor(private route: ActivatedRoute) {}
    
      ngOnInit() {
        Observable.combineLatest(this.route.params, this.route.parent.params)
            .forEach((params: Params[]) => {
              this.id = params[0]['id'];
              this.email = params[1]['email'];
            });
      }
    }
    
  • 3

    UPDATE:

    现在Angular2 final正式发布,正确的方法如下:

    export class ChildComponent {
    
        private sub: any;
    
        private parentRouteId: number;
    
        constructor(private route: ActivatedRoute) { }
    
        ngOnInit() {
            this.sub = this.route.parent.params.subscribe(params => {
                this.parentRouteId = +params["id"];
            });
        }
    
        ngOnDestroy() {
            this.sub.unsubscribe();
        }
    }
    

    ORIGINAL:

    以下是我使用“@ angular / router”:“3.0.0-alpha.6”包的方法:

    export class ChildComponent {
    
        private sub: any;
    
        private parentRouteId: number;
    
        constructor(
            private router: Router,
            private route: ActivatedRoute) {
        }
    
        ngOnInit() {
            this.sub = this.router.routerState.parent(this.route).params.subscribe(params => {
                this.parentRouteId = +params["id"];
            });
        }
    
        ngOnDestroy() {
            this.sub.unsubscribe();
        }
    }
    

    在此示例中,路由具有以下格式:/ parent /:id / child /:childid

    export const routes: RouterConfig = [
        {
            path: '/parent/:id',
            component: ParentComponent,
            children: [
                { path: '/child/:childid', component: ChildComponent }]
        }
    ];
    
  • 7

    您不应该尝试在 ChildOneComponent 中使用 RouteParams .

    请改用RouteRegistry

    @Component({
      ...
    })
    
    export class ChildOneComponent {
    
      public username: string;
    
      constructor(registry: RouteRegistry, location: Location) {
        route_registry.recognize(location.path(), []).then((instruction) => {
          console.log(instruction.component.params['username']);
        })
      }
    
    
      ...
    }
    

    UPDATE: 从此拉取请求(角度β9):https://github.com/angular/angular/pull/7163

    您现在可以在不使用 recognize(location.path(), []) 的情况下访问当前指令 .

    例:

    @Component({
      ...
    })
    
    export class ChildOneComponent {
    
      public username: string;
    
      constructor(_router: Router) {
        let instruction = _router.currentInstruction();
        this.username = instruction.component.params['username'];
      }
    
      ...
    }
    

    I haven't tried it, yet

    更多详情:

    https://github.com/angular/angular/blob/master/CHANGELOG.md#200-beta9-2016-03-09 https://angular.io/docs/ts/latest/api/router/Router-class.html

    UPDATE 2: 从角度2.0.0.beta15开始的小变化:

    现在 currentInstruction 不再是一个功能了 . 此外,您必须加载 root 路由器 . (感谢@ Lxrd-AJ报道)

    @Component({
      ...
    })
    
    export class ChildOneComponent {
    
      public username: string;
    
      constructor(_router: Router) {
        let instruction = _router.root.currentInstruction;
        this.username = instruction.component.params['username'];
      }
    
      ...
    }
    
  • 2

    如GünterZöchbauer所述,我使用了https://github.com/angular/angular/issues/6204#issuecomment-173273143的评论来解决我的问题 . 我使用 angular2/core 中的 Injector 类来获取父级的routeparams . 原来角度2不能处理深层嵌套的路线 . 也许他们将来会加上这个 .

    constructor(private _issueService: IssueService,
                private _injector: Injector) {}
    
    getIssues() {
        let id = this._injector.parent.parent.get(RouteParams).get('id');
        this._issueService.getIssues(id).then(issues => this.issues = issues);
    }
    
  • 0

    我找到了一个丑陋但有效的解决方案,通过请求父母(正是第二个祖先)注射器,并从这里获取 RouteParams .

    就像是

    @Component({
      ...
    })
    export class ChildOneComponent {
      public username: string;
    
      constructor(injector: Injector) {
        let params = injector.parent.parent.get(RouteParams);
    
        this.username = params.get('username');
      }
    }
    
  • 2

    RC5 + @angular/router": "3.0.0-rc.1 SOLUTION: 似乎 this.router.routerState.queryParams 已被弃用 . 您可以通过以下方式获取父路线参数:

    constructor(private activatedRoute: ActivatedRoute) {
    }    
    
    this.activatedRoute.parent.params.subscribe(
      (param: any) => {
        let userId = param['userId'];
        console.log(userId);
      });
    
  • 53

    您可以从注入器中获取子组件内部父路径的组件,然后从子组件中获取任何组件 . 在你的情况下这样

    @Component({
      ...
    })
    
    export class ChildOneComponent {
    
      public username: string;
    
      constructor(
        public params: RouteParams
        private _injector: Injector
    
      ) {
        var parentComponent = this._injector.get(ParentComponent)
    
        this.username = parentComponent.username;
        //or
        this.username = parentComponent.params.get('username');
      }
    
      ...
    }
    
  • 9

    如果要为代码编写单元测试,将Injector实例传递给子组件中的构造函数可能不太好 .

    解决此问题的最简单方法是在父组件中创建一个服务类,您可以在其中保存所需的参数 .

    @Component({
        template: `<div><router-outlet></router-outlet></div>`,
        directives: [RouterOutlet],
        providers: [SomeServiceClass]
    })
    @RouteConfig([
        {path: "/", name: "IssueList", component: IssueListComponent, useAsDefault: true}
    ])
    class IssueMountComponent {
        constructor(routeParams: RouteParams, someService: SomeServiceClass) {
            someService.id = routeParams.get('id');
        }
    }
    

    然后,您只需向子组件注入相同的服务并访问参数 .

    @Component({
        template: `some template here`
    })
    class IssueListComponent implements OnInit {
        issues: Issue[];
        constructor(private someService: SomeServiceClass) {}
    
        getIssues() {
            let id = this.someService.id;
            // do your magic here
        }
    
        ngOnInit() {
            this.getIssues();
        }
    }
    

    请注意,您应该使用父组件装饰器中的“providers”将此类服务的范围限定为父组件及其子组件 .

    我推荐这篇关于Angular 2中DI和范围的文章:http://blog.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

  • 1

    In RC6, router 3.0.0-rc.2 (也可能在RC5中工作),你可以从URL中获取路径参数作为快照,以防params不会改变,没有这个衬管的可观察量:

    this.route.snapshot.parent.params['username'];

    不要忘记按如下方式注入ActivatedRoute:

    constructor(private route: ActivatedRoute) {};

  • 2

    我最终为Angular 2 rc.1写了这种黑客

    import { Router } from '@angular/router-deprecated';
    import * as _ from 'lodash';
    
    interface ParameterObject {
      [key: string]: any[];
    };
    
    /**
     * Traverse route.parent links until root router and check each level
     * currentInstruction and group parameters to single object.
     *
     * e.g.
     * {
     *   id: [314, 593],
     *   otherParam: [9]
     * }
     */
    export default function mergeRouteParams(router: Router): ParameterObject {
      let mergedParameters: ParameterObject = {};
      while (router) {
        let currentInstruction = router.currentInstruction;
        if (currentInstruction) {
          let currentParams = currentInstruction.component.params;
          _.each(currentParams, (value, key) => {
            let valuesForKey = mergedParameters[key] || [];
            valuesForKey.unshift(value);
            mergedParameters[key] = valuesForKey;
          });
        }
        router = router.parent;
      }
      return mergedParameters;
    }
    

    现在在视图中我收集视图中的参数而不是读取 RouteParams 我只是通过路由器获取它们:

    @Component({
      ...
    })
    
    export class ChildishComponent {
    
      constructor(router: Router) {
        let allParams = mergeRouteParams(router);
        let parentRouteId = allParams['id'][0];
        let childRouteId = allParams['id'][1];
        let otherRandomParam = allParams.otherRandomParam[0];
      }
    
      ...
    }
    
  • 6

    FINAL 中,在 RXJS 的帮助下,您可以组合两个 Map (来自孩子和父母):

    (route) => Observable
        .zip(route.params, route.parent.params)
        .map(data => Object.assign({}, data[0], data[1]))
    

    可能有的其他问题:

    • 使用上面是否真的是一个好主意 - 因为耦合(将子组件与父组合's param' s - 不在api级别 - 隐藏耦合),

    • 对于RXJS而言它是否是正确的方法(它需要硬核RXJS用户反馈;)

  • 2

    您可以使用以下内容在快照上执行此操作,但如果更改,则不会更新 id 属性 .

    此示例还说明了如何通过合并所有参数observable来订阅所有祖先参数更改并查找您感兴趣的参数更改 . 但是,请注意此方法,因为可能有多个祖先具有相同的参数键/名称 .

    import { Component } from '@angular/core';
    import { ActivatedRoute, Params, ActivatedRouteSnapshot } from '@angular/router';
    import { Observable } from 'rxjs/Observable';
    import { Subscription } from 'rxjs/Subscription';
    import 'rxjs/add/observable/merge';
    
    // This traverses the route, following ancestors, looking for the parameter.
    function getParam(route: ActivatedRouteSnapshot, key: string): any {
      if (route != null) {
        let param = route.params[key];
        if (param === undefined) {
          return getParam(route.parent, key);
        } else {
          return param;
        }
      } else {
        return undefined;
      }
    }
    
    @Component({ /* ... */ })
    export class SomeChildComponent {
    
      id: string;
    
      private _parameterSubscription: Subscription;
    
      constructor(private route: ActivatedRoute) {
      }
    
      ngOnInit() {
        // There is no need to do this if you subscribe to parameter changes like below.
        this.id = getParam(this.route.snapshot, 'id');
    
        let paramObservables: Observable<Params>[] =
          this.route.pathFromRoot.map(route => route.params);
    
        this._parametersSubscription =
          Observable.merge(...paramObservables).subscribe((params: Params) => {
            if ('id' in params) {
              // If there are ancestor routes that have used
              // the same parameter name, they will conflict!
              this.id = params['id'];
            }
          });
      }
    
      ngOnDestroy() {
        this._parameterSubscription.unsubscribe();
      }
    }
    

相关问题