Angular 2: getting RouteParams from parent compone

2019-06-15 11:06发布

How do I get the RouteParams from a parent component?

App.ts:

@Component({
  ...
})

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

export class HomeComponent {
  ...
}

And then, in the ParentComponent, I can easily get my username param and set the child routes.

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

  ...
}

But then, how can I get this same 'username' parameter in those child components? Doing the same trick as above, doesn't do it. Because those params are defined at the ProfileComponent or something??

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

12条回答
我欲成王,谁敢阻挡
2楼-- · 2019-06-15 11:25

You shouldn't try to use RouteParams in your ChildOneComponent.

Use RouteRegistry, instead!

@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: As from this pull request (angular beta.9): https://github.com/angular/angular/pull/7163

You can now access to the current instruction without recognize(location.path(), []).

Example:

@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

Further details here:

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: A small change as from angular 2.0.0.beta15:

Now currentInstruction is not a function anymore. Moreover, you have to load the root router. (thanks to @Lxrd-AJ for reporting)

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

  constructor(_router: Router) {
    let instruction = _router.root.currentInstruction;
    this.username = instruction.component.params['username'];
  }

  ...
}
查看更多
一纸荒年 Trace。
3楼-- · 2019-06-15 11:25

Passing Injector instance to constructor in child component may not be good if you want to write unit tests for your code.

The easiest way to work around this is to have a service class in the parent component, in which you save your required params.

@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');
    }
}

Then you just inject the same service to child components and access the params.

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

Note that you should scope such service to your parent component and its child components using "providers" in parent component decorator.

I recommend this article about DI and scopes in Angular 2: http://blog.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

查看更多
放荡不羁爱自由
4楼-- · 2019-06-15 11:30

RC5 + @angular/router": "3.0.0-rc.1 SOLUTION: It seems that this.router.routerState.queryParams has been deprecated. You can get the parent route params this way:

constructor(private activatedRoute: ActivatedRoute) {
}    

this.activatedRoute.parent.params.subscribe(
  (param: any) => {
    let userId = param['userId'];
    console.log(userId);
  });
查看更多
在下西门庆
5楼-- · 2019-06-15 11:33

I ended up writing this kind of hack for 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;
}

Now in view I collect parameters in view instead of reading RouteParams I just get them through router:

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

  ...
}  
查看更多
倾城 Initia
6楼-- · 2019-06-15 11:40

In RC6, router 3.0.0-rc.2 (probably works in RC5 as well), you can take route params from the URL as a snapshot in case that params won't change, without observables with this one liner:

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

Don't forget to inject ActivatedRoute as follows:

constructor(private route: ActivatedRoute) {};

查看更多
淡お忘
7楼-- · 2019-06-15 11:42

As mentioned by Günter Zöchbauer, I used the comment at https://github.com/angular/angular/issues/6204#issuecomment-173273143 to address my problem. I used the Injector class from angular2/core to fetch the routeparams of the parent. Turns out angular 2 does not handle deeply nested routes. Maybe they'll add that in the future.

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);
}
查看更多
登录 后发表回答