Winkel 2: Abrufen von RouteParams von der übergeordneten Komponente


79

Wie erhalte ich die RouteParams von einer übergeordneten Komponente?

App.ts::

@Component({
  ...
})

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

export class HomeComponent {
  ...
}

Und dann ParentComponentkann ich in der einfach meinen Benutzernamen param abrufen und die untergeordneten Routen festlegen.

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

  ...
}

Aber wie kann ich dann denselben Parameter 'Benutzername' in diesen untergeordneten Komponenten erhalten? Den gleichen Trick wie oben zu machen, macht es nicht. Weil diese Parameter in der ProfileComponent definiert sind oder so?

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

Wie wäre es mit einer Eingabe-Eigenschaft für die Kinder? ZB in der übergeordneten Vorlage:<child-one-component [username]="username"> ...
Mark Rajcok

Würde das auch funktionieren <routerlink [username]="username">...? Und ist das der richtige Weg dann @MarkRajcok?
Aico Klein Ovink

Ich denke, Sie fragen, ob so etwas <a [router-link]="[ './....', {username: username} ]funktionieren wird. Entschuldigung, ich habe keine Ahnung, ob das funktionieren wird oder nicht. (Ich habe noch nicht viel mit Routing gespielt.)
Mark Rajcok

Es tut mir leid, @MarkRajcok, ich habe es falsch eingegeben. Ich meinte <router-outlet></router-outlet>, sollte ich eine Eingabe dazu machen. Weil die
Kinderrouten

Antworten:


72

AKTUALISIEREN:

Nachdem das Angular2-Finale offiziell veröffentlicht wurde, ist der richtige Weg, dies zu tun:

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:

So habe ich es mit dem Paket "@ angle / router" gemacht: "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();
    }
}

In diesem Beispiel hat die Route das folgende Format: / parent /: id / child /: childid

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

2
Sie müssen dies in ngOnInit (wie gezeigt) aufrufen , nicht im Konstruktor, wie ich es zunächst törichterweise versucht habe.
Cameron

2
Seit Angular 5.2 gibt es einen alternativen Weg, bei dem Sie nicht mehr als parent1 Mal durchlaufen müssen . Siehe stackoverflow.com/a/48511516/4185989 Es lohnt sich jedoch, das subscribe/ unsubscribe-Muster aus dieser Antwort zu berücksichtigen .
jmq

In Angular 6this.activatedRoute.parent.snapshot.params.someParam
Tasnim Reza

Die von @jmq angegebene Lösung ist auch für Winkel 6 die beste. Wir müssen die übergeordnete ID nicht separat abonnieren.
Lernen ...

Perfekte Lösung! Kaufen Sie, warum ich mich anmelden muss, um die übergeordneten Parameter zu erhalten. Der Parameter ist schon da! :: Denken ::
moreirapontocom

10

Sie sollten nicht versuchen, RouteParamsin Ihrem zu verwenden ChildOneComponent.

Verwenden Sie RouteRegistrystattdessen!

@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: Ab dieser Pull-Anfrage (Angular Beta.9): https://github.com/angular/angular/pull/7163

Sie können jetzt ohne auf die aktuelle Anweisung zugreifen recognize(location.path(), []).

Beispiel:

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

Ich habe es noch nicht versucht

Weitere Details hier:

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: Eine kleine Änderung gegenüber Winkel 2.0.0.beta15:

Jetzt currentInstructionist keine Funktion mehr. Außerdem müssen Sie den rootRouter laden . (Danke an @ Lxrd-AJ für die Berichterstattung)

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

Ist das so, wie es für Kinderrouten gemacht werden soll? Ich stoße auch auf dieses Problem, bei dem untergeordnete Routen keine Routeparams übergeordneter Komponenten sehen können. Zum Beispiel die Route / users /: user_id / posts /: post_id, ich kann die user_id nicht von der posts-Komponente abrufen ... Es scheint mir hackig, RouteRegistry verwenden zu müssen.
mharris7190

@ mharris7190 Ich habe meine Antwort aktualisiert. Ab Angular Beta.9 können Sie die aktuelle Anweisung direkt von der Router-Komponente abrufen.
ProGM

Danke für das Update. Ich bin im Begriff, ein Upgrade von Beta.6 auf Beta.13 durchzuführen, also werde ich es ausprobieren, nachdem ich es getan habe.
mharris7190

3
Eine leichte Bearbeitung dieser Antwort, Verwenden _router.root.currentInstruction.component.params['id']. Der Schwerpunkt liegt auf root, da Sie jetzt die aktuelle Anweisung vom Root-Router erhalten und nicht _router. PS: Ich benutzeangular2.0.0-beta.15
Lxrd-AJ

_router.root existiert nicht mehr. (Ich benutze Angular 2.4.7)
Eivind Gussiås Løkseth

7

Wie von Günter Zöchbauer erwähnt, habe ich den Kommentar unter https://github.com/angular/angular/issues/6204#issuecomment-173273143 verwendet , um mein Problem anzugehen. Ich habe die InjectorKlasse von verwendet angular2/core, um die Routeparams des übergeordneten Elements abzurufen. Es stellt sich heraus, dass Winkel 2 keine tief verschachtelten Routen verarbeitet. Vielleicht werden sie das in Zukunft hinzufügen.

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

8
Dies funktioniert auf dem angle2 RC-Router nicht mehr.
Inn0vative1

6

Ich fand eine hässliche, aber funktionierende Lösung, indem ich den Injektor des Elternteils (genau des 2. Vorfahren) anforderte und den RouteParamsvon hier bekam.

Etwas wie

@Component({
  ...
})
export class ChildOneComponent {
  public username: string;

  constructor(injector: Injector) {
    let params = injector.parent.parent.get(RouteParams);

    this.username = params.get('username');
  }
}

Vielen Dank für das Teilen. Gibt es einen Bugtracker-Eintrag oder eine Erklärung des Angular-Teams, wie dies in Zukunft gehandhabt wird?
Marcus Riemer

Scheint, dass .parent auf RC3 entfernt wurde
theFreedomBanana

4

RC5 + @ angle / router ":" 3.0.0-rc.1 LÖSUNG: Es scheint, dass this.router.routerState.queryParamsdies veraltet ist. Sie können die übergeordneten Routenparameter folgendermaßen abrufen:

constructor(private activatedRoute: ActivatedRoute) {
}    

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

2

Sie können die Komponente der übergeordneten Route innerhalb der untergeordneten Komponente vom Injektor übernehmen und dann eine beliebige Komponente von der untergeordneten Komponente abrufen. In deinem Fall so

@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

Das Übergeben der Injector-Instanz an den Konstruktor in der untergeordneten Komponente ist möglicherweise nicht sinnvoll, wenn Sie Komponententests für Ihren Code schreiben möchten.

Der einfachste Weg, dies zu umgehen, besteht darin, eine Serviceklasse in der übergeordneten Komponente zu haben, in der Sie Ihre erforderlichen Parameter speichern.

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

Dann injizieren Sie einfach den gleichen Dienst in untergeordnete Komponenten und greifen auf die Parameter zu.

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

Beachten Sie, dass Sie diesen Service auf Ihre übergeordnete Komponente und ihre untergeordneten Komponenten anwenden sollten, indem Sie "Provider" im übergeordneten Komponentendekorator verwenden.

Ich empfehle diesen Artikel über DI und Bereiche in Angular 2: http://blog.ehowtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html


2

In RC6, Router 3.0.0-rc.2 (funktioniert wahrscheinlich auch in RC5), können Sie Routenparameter aus der URL als Momentaufnahme verwenden, falls sich die Parameter nicht ändern, ohne Observables mit diesem einen Liner:

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

Vergessen Sie nicht, ActivatedRoute wie folgt zu injizieren:

constructor(private route: ActivatedRoute) {};


2

Mit RxJS Observable.combineLatestkönnen wir dem Umgang mit idiomatischen Parametern etwas nahe kommen:

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

1

Am Ende habe ich diese Art von Hack für Angular 2 rc.1 geschrieben

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

Jetzt in der Ansicht sammle ich Parameter in der Ansicht, anstatt zu lesen, RouteParamsich erhalte sie nur über den 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];
  }

  ...
}  

Funktioniert super! Am Ende habe ich diese Methode innerhalb einer MergedRouteParamsKlasse privat gemacht , die die getMethode der Standardklasse implementiert RouteParams(zweiter Parameter ist index, standardmäßig null).
Jim Buck

0

In FINAL können Sie mit wenig Hilfe von RXJS beide Karten (von Kind und Eltern) kombinieren:

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

Andere Fragen, die man haben könnte:

  • Ist es wirklich eine gute Idee, oben zu verwenden - wegen der Kopplung (untergeordnete Komponente mit den Parametern der Eltern koppeln - nicht auf API-Ebene - versteckte Kopplung),
  • Ist es ein angemessener Ansatz in Bezug auf RXJS (es würde Hardcore-Feedback von RXJS-Benutzern erfordern;)

0

Sie können dies für den Snapshot wie folgt tun. Wenn sich dies jedoch ändert, wird Ihre idEigenschaft nicht aktualisiert.

Dieses Beispiel zeigt auch, wie Sie alle Änderungen der Vorfahrenparameter abonnieren und nach der gewünschten suchen können, indem Sie alle Parameter-Observablen zusammenführen. Seien Sie jedoch vorsichtig mit dieser Methode, da es mehrere Vorfahren geben kann, die denselben Parameterschlüssel / Namen haben.

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

Abrufen von RouteParams von der übergeordneten Komponente in Angular 8 -

Ich habe eine Route http: // localhost: 4200 / partner / student-profile / 1234 / info

Elternroute - Schülerprofil

Param - 1234 (student_id)

Kinderroute - Info


Zugriff auf Parameter in der untergeordneten Route (Info) -

Importieren

import { ActivatedRoute, Router, ParamMap } from '@angular/router';

Konstrukteur

constructor(private activatedRoute: ActivatedRoute, private router: Router) { }

Zugriff auf übergeordnete Routenparameter

this.activatedRoute.parent.paramMap.subscribe((params: ParamMap) => this.studentId = (params.get('student_id')));


Jetzt hat unsere Variable studentId den Parameterwert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.