web-dev-qa-db-fra.com

Angular 2: obtenir des RouteParams à partir du composant parent

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
  }

  ...
}
69
Aico Klein Ovink

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 }]
    }
];
59
Fábio Junqueira

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

  ...
}
9
ProGM

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

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');
  }
}
6
Yohan G.

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);
  });
4
Stephen Paul

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

2
qdb

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'];
        });
  }
}
2
In-Ho Yi

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

  ...
}
2
Danxil

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

2
mrgoos

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

  ...
}  
1
Mikael Lepistö

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

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:

  • Est-ce vraiment une bonne idée d'utiliser ci-dessus - à cause du couplage (couple composant enfant avec paramètre du parent - pas au niveau de l'api - couplage caché),
  • Est-ce une approche appropriée en termes de RXJS (cela nécessiterait un retour d’expert hardcore de la part des utilisateurs de RXJS;)
0
wendro