Comment puis-je obtenir les RouteParams à partir 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: string;
constructor(
public params: RouteParams
) {
this.username = params.get('username');
}
...
}
Mais alors, comment puis-je obtenir le même paramètre 'nom d'utilisateur' dans ces composants enfants? Faire le même tour que ci-dessus, ne le fait pas. Parce que ces paramètres sont définis au ProfileComponent ou quelque chose ??
@Component({
...
})
export class ChildOneComponent {
public username: string;
constructor(
public params: RouteParams
) {
this.username = params.get('username');
// returns null
}
...
}
PDATE:
Maintenant que la version finale de Angular2 a été officiellement publiée, procédez comme suit:
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 avec 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, l'itinéraire 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
, à la place!
@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: À partir de cette demande d'extraction (version bêta angulaire.9): https://github.com/angular/angular/pull/ 716
Vous pouvez maintenant accéder à l'instruction en cours sans recognize(location.path(), [])
.
Exemple:
@Component({
...
})
export class ChildOneComponent {
public username: string;
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-09https://angular.io/docs/ ts/latest/api/router/Router-class.html
UPDATE 2: Un petit changement à partir de angular 2.0.0.beta15:
Maintenant, currentInstruction
n'est plus une fonction. De plus, vous devez charger le routeur root
. (merci à @ Lxrd-AJ pour le compte rendu)
@Component({
...
})
export class ChildOneComponent {
public username: string;
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 à l'adresse https://github.com/angular/angular/issues/6204#issuecomment-17327314 pour résoudre mon problème. J'ai utilisé la classe Injector
de 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 imbriquées. Peut-être qu'ils l'ajouteront à 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 laide mais qui fonctionne, en demandant l'injecteur parent (précisément le 2e ancêtre) et en obtenant le RouteParams
à partir d'ici.
Quelque chose comme
@Component({
...
})
export class ChildOneComponent {
public username: string;
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
est obsolète. Vous pouvez obtenir les paramètres de la route parent de cette façon:
constructor(private activatedRoute: ActivatedRoute) {
}
this.activatedRoute.parent.params.subscribe(
(param: any) => {
let userId = param['userId'];
console.log(userId);
});
Passer l'instance d'Injector au constructeur du composant enfant peut ne pas être utile si vous souhaitez écrire des tests unitaires pour votre code.
Le moyen le plus simple de contourner ce problème consiste à avoir une classe de service dans le composant parent, dans laquelle vous enregistrez vos paramètres requis.
@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');
}
}
Ensuite, vous n'injectez que le même service aux composants enfants et accédez aux paramètres.
@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 affecter ce service à votre composant parent et à ses composants enfants à l'aide de "providers" dans le décorateur de composants parent.
Je recommande cet article sur DI et les portées dans Angular 2: http://blog.iblytram.io/angular/2015/08/20/Host-and-visibility-in -angular-2-dependency-injection.html
Avec RxJS's Observable.combineLatest
, nous pouvons obtenir quelque chose de proche de la gestion des paramètres 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: 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'];
});
}
}
Vous pouvez prendre le composant de la route parent à l'intérieur du composant enfant à partir de l'injecteur, puis obtenir tout composant de l'enfant. Dans ton cas comme ça
@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');
}
...
}
Dans RC6, routeur 3.0.0-rc.2 (fonctionne probablement aussi dans RC5), vous pouvez utiliser les paramètres de route de l’URL comme instantané au cas où ces paramètres ne changeraient pas, sans observables avec cette doublure:
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 bidouille pour 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;
}
Maintenant en vue, je collecte des paramètres en vue au lieu de lire RouteParams
je les obtiens simplement via un routeur:
@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];
}
...
}
Vous pouvez le faire sur l'instantané avec les éléments suivants, mais si cela change, votre propriété id
ne sera pas mise à jour.
Cet exemple montre également comment vous pouvez vous abonner à toutes les modifications de paramètre d'ancêtre et rechercher celle qui vous intéresse en fusionnant toutes les observables de paramètre. Cependant, soyez prudent avec cette méthode car plusieurs ancêtres pourraient avoir le même paramètre clé/nom.
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();
}
}
Dans FINAL avec un peu d'aide de RXJS vous pouvez combiner les deux cartes (de l'enfant et parent):
(route) => Observable
.Zip(route.params, route.parent.params)
.map(data => Object.assign({}, data[0], data[1]))
Autres questions que l'on pourrait avoir: