Angular 2: obtenir RouteParams à partir du composant parent

Comment puis-je obtenir les RouteParams d’un composant parent?

App.ts :

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

Et puis, dans ParentComponent , je peux facilement obtenir mon nom d’utilisateur param et définir les itinéraires enfants.

Parent.ts :

 @Component({ ... }) @RouteConfig([ { path: '/child-1', component: ChildOneComponent, as: 'ChildOne' }, { path: '/child-2', component: ChildTwoComponent, as: 'ChildTwo' } ]) export class ParentComponent { public username: ssortingng; constructor( public params: RouteParams ) { this.username = params.get('username'); } ... } 

Mais alors, comment puis-je obtenir ce même paramètre «username» dans ces composants enfants? Faire le même tour que ci-dessus ne le fait pas. Parce que ces parameters sont définis dans ProfileComponent ou quelque chose?

 @Component({ ... }) export class ChildOneComponent { public username: ssortingng; constructor( public params: RouteParams ) { this.username = params.get('username'); // returns null } ... } 

METTRE À JOUR:

Maintenant que la version finale d’Angular2 a été officiellement publiée, la manière correcte de procéder est la suivante:

 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:

Voici comment je l’ai fait en utilisant le package “@ 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(); } } 

Dans cet exemple, la route a le format suivant: / parent /: id / child /: childid

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

Vous ne devriez pas essayer d’utiliser RouteParams dans votre ChildOneComponent .

Utilisez RouteRegistry !

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

MISE À JOUR: À partir de cette requête d’extraction (version bêta.9 angular): https://github.com/angular/angular/pull/7163

Vous pouvez maintenant accéder à l’instruction en cours sans recognize(location.path(), []) .

Exemple:

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

Je ne l’ai pas encore essayé

Plus de détails ici:

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

MISE À JOUR 2: Un petit changement à partir de 2.0.0.beta15 angular:

Maintenant, currentInstruction n’est plus une fonction. De plus, vous devez charger le routeur root . (merci à @ Lxrd-AJ pour les rapports)

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

Comme mentionné par Günter Zöchbauer, j’ai utilisé le commentaire sur https://github.com/angular/angular/issues/6204#issuecomment-173273143 pour résoudre mon problème. J’ai utilisé la classe Injector d’ angular2/core pour récupérer les routeparams du parent. Il s’avère que angular 2 ne gère pas les routes profondément nestedes. Peut-être qu’ils vont append cela à l’avenir.

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

J’ai trouvé une solution moche mais fonctionnelle, en demandant l’injecteur parent (précisément le 2ème ancêtre), et en obtenant le RouteParams d’ici.

Quelque chose comme

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

RC5 + @ angular / router “:” 3.0.0-rc.1 SOLUTION: Il semble que this.router.routerState.queryParams soit obsolète. Vous pouvez obtenir les parameters de route parent de cette façon:

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

Vous pouvez prendre le composant de la route parente à l’intérieur du composant enfant à partir de l’injecteur, puis obtenir le composant enfant. Dans ton cas comme ça

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

Passer une instance d’injecteur au constructeur dans un composant enfant peut ne pas être bon si vous voulez écrire des tests unitaires pour votre code.

Le moyen le plus simple de contourner ce problème est d’avoir une classe de service dans le composant parent, dans laquelle vous enregistrez vos parameters requirejs.

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

Ensuite, il vous suffit d’injecter le même service aux composants enfants et d’accéder aux parameters.

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

Notez que vous devez étendre ce service à votre composant parent et à ses composants enfants à l’aide de “providers” dans le décorateur de composant parent.

Je recommande cet article sur DI et les étendues dans Angular 2: http://blog.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

Dans RC6, le routeur 3.0.0-rc.2 (fonctionne probablement aussi dans RC5), vous pouvez prendre des parameters de route à partir de l’URL en tant qu’instantané au cas où les parameters ne changeraient pas, sans observable avec cette ligne:

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

N’oubliez pas d’injecter ActivatedRoute comme suit:

constructor(private route: ActivatedRoute) {};

J’ai fini par écrire ce genre de piratage pour Angular 2 rc.1

 import { Router } from '@angular/router-deprecated'; import * as _ from 'lodash'; interface ParameterObject { [key: ssortingng]: any[]; }; /** * Traverse route.parent links until root router and check each level * currentInstruction and group parameters to single object. * * eg * { * 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; } 

Maintenant, dans la vue, je collecte les parameters en vue au lieu de lire 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]; } ... } 

Avec Observable.combineLatest de RxJS, nous pouvons obtenir quelque chose proche de la gestion des parameters idiomatiques:

 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: ssortingng; id: ssortingng; 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']; }); } } 

Dans FINAL avec peu d’aide de RXJS, vous pouvez combiner les deux cartes (de l’enfant et du parent):

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

Autres questions que l’on pourrait avoir:

  • Est-ce vraiment une bonne idée d’utiliser ci-dessus – à cause du couplage (composant enfant couple avec param de parent – pas au niveau api – couplage caché),
  • S’agit-il d’une approche appropriée en termes de RXJS (cela nécessiterait un retour d’expérience de la part de RXJS);

Vous pouvez le faire sur l’instantané avec les éléments suivants, mais s’il change, votre propriété id ne sera pas mise à jour.

Cet exemple montre également comment vous pouvez vous abonner à tous les changements de parameters ancêtres et rechercher celui qui vous intéresse en fusionnant tous les parameters observables. Cependant, soyez prudent avec cette méthode car il pourrait y avoir plusieurs ancêtres qui ont la même clé / nom de paramètre.

 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: ssortingng): 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: ssortingng; 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[] = 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(); } }