Angular 2: pobieranie RouteParams z komponentu nadrzędnego

79

Jak uzyskać RouteParams ze składnika nadrzędnego?

App.ts:

@Component({
  ...
})

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

export class HomeComponent {
  ...
}

Następnie ParentComponentmogę łatwo uzyskać parametr mojej nazwy użytkownika i ustawić trasy podrzędne.

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

  ...
}

Ale jak mogę uzyskać ten sam parametr „nazwa użytkownika” w tych komponentach podrzędnych? Robienie tego samego triku co powyżej, nie robi tego. Ponieważ te parametry są zdefiniowane w ProfileComponent czy coś?

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}
Aico Klein Ovink
źródło
A co z właściwością wejściową dzieci? Np. W szablonie nadrzędnym:<child-one-component [username]="username"> ...
Mark Rajcok
Czy to też zadziała <routerlink [username]="username">...? I czy to jest droga do @MarkRajcok?
Aico Klein Ovink
Myślę, że pytasz, czy coś takiego <a [router-link]="[ './....', {username: username} ]zadziała. Przepraszam, nie mam pojęcia, czy to zadziała, czy nie. (Nie grałem jeszcze zbyt wiele w routing.)
Mark Rajcok
Przepraszam @MarkRajcok, wpisałem go źle ... Miałem na myśli <router-outlet></router-outlet>, czy powinienem umieścić na tym wejście. Ponieważ trasy podrzędne zostaną tam wyrenderowane ...
Aico Klein Ovink
2
Mogą być przydatne informacje github.com/angular/angular/issues/6204#issuecomment-173273143
Günter Zöchbauer

Odpowiedzi:

72

AKTUALIZACJA:

Teraz, gdy finał Angular2 został oficjalnie wydany, prawidłowy sposób na zrobienie tego jest następujący:

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

ORYGINALNY:

Oto jak to zrobiłem, używając pakietu „@ 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();
    }
}

W tym przykładzie trasa ma następujący format: / parent /: id / child /: childid

export const routes: RouterConfig = [
    {
        path: '/parent/:id',
        component: ParentComponent,
        children: [
            { path: '/child/:childid', component: ChildComponent }]
    }
];
Fábio Junqueira
źródło
2
Musisz wywołać to w ngOnInit (jak pokazano), a nie w konstruktorze, jak na początku głupio próbowałem to zrobić.
Cameron,
2
Istnieje alternatywny sposób od Angular 5.2, który nie wymaga przejścia parent1+ razy. Zobacz stackoverflow.com/a/48511516/4185989 Jednak nadal warto rozważyć wzorzec subscribe/ unsubscribez tej odpowiedzi.
jmq
W Angular 6this.activatedRoute.parent.snapshot.params.someParam
Tasnim Reza,
Rozwiązanie wskazywane przez @jmq jest również najlepsze dla Angulara 6, nie musimy osobno subskrybować identyfikatora rodzica.
nauka ...
Idealne rozwiązanie! Kup, dlaczego muszę subskrybować, aby uzyskać parametry rodzica? Param już tam jest! :: myślenie ::
moreirapontocom
10

Nie powinieneś próbować używać RouteParamsw swoim ChildOneComponent.

RouteRegistryZamiast tego użyj !

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


  ...
}

AKTUALIZACJA: od tego żądania ściągnięcia (kątowa beta.9): https://github.com/angular/angular/pull/7163

Możesz teraz uzyskać dostęp do aktualnej instrukcji bez recognize(location.path(), []).

Przykład:

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

Jeszcze tego nie próbowałem

Więcej szczegółów tutaj:

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

AKTUALIZACJA 2: Niewielka zmiana od kątowej 2.0.0.beta15:

Teraz currentInstructionnie jest już funkcją. Co więcej, musisz załadować rootrouter. (podziękowania dla @ Lxrd-AJ za zgłoszenie)

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}
ProGM
źródło
Czy tak powinno być w przypadku tras podrzędnych? Mam również problem z tym problemem, w którym trasy podrzędne nie widzą parametrów routingu komponentów nadrzędnych. Na przykład route / users /: user_id / posts /: post_id, nie mogę pobrać user_id z komponentu postów .... Wydaje mi się, że korzystanie z RouteRegistry nie jest łatwe.
mharris7190
@ mharris7190 Zaktualizowałem moją odpowiedź. Począwszy od angular beta.9 możesz pobrać bieżące instrukcje bezpośrednio z komponentu routera.
ProGM
Dziękuję za aktualizację. Mam zamiar zaktualizować wersję beta.6 do beta.13, więc wypróbuję ją później.
mharris7190
3
Niewielka edycja tej odpowiedzi, użyj _router.root.currentInstruction.component.params['id']. Nacisk na root, gdy otrzymujesz bieżącą instrukcję z routera głównego, a nie _router. PS: używamangular2.0.0-beta.15
Lxrd-AJ
_router.root już nie istnieje. (Używam Angular 2.4.7)
Eivind Gussiås Løkseth
7

Jak wspomniał Günter Zöchbauer, użyłem komentarza pod adresem https://github.com/angular/angular/issues/6204#issuecomment-173273143 aby rozwiązać mój problem. Użyłem Injectorklasy from, angular2/coreaby pobrać parametry routingu rodzica. Okazuje się, że kątowy 2 nie obsługuje głęboko zagnieżdżonych tras. Może dodadzą to w przyszłości.

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);
}
Lordking
źródło
8
To już nie działa na routerze angular2 RC.
Inn0vative 1
6

Znalazłem brzydkie, ale działające rozwiązanie, prosząc rodzica (dokładnie drugiego przodka) o wstrzykiwacz i otrzymując RouteParams stąd.

Coś jak

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

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

    this.username = params.get('username');
  }
}
Yohan G.
źródło
Wielkie dzięki za udostępnienie tego, czy jest jakikolwiek wpis bugtrackera lub oświadczenie zespołu Angular na temat tego, jak będzie to traktowane w przyszłości?
Marcus Riemer
Wygląda na to, że plik .parent został usunięty z RC3
theFreedomBanana.
4

RC5 + @ angular / router ":" 3.0.0-rc.1 ROZWIĄZANIE: Wygląda na to, że this.router.routerState.queryParamsjest przestarzałe. Możesz uzyskać parametry trasy nadrzędnej w ten sposób:

constructor(private activatedRoute: ActivatedRoute) {
}    

this.activatedRoute.parent.params.subscribe(
  (param: any) => {
    let userId = param['userId'];
    console.log(userId);
  });
Stephen Paul
źródło
2

Możesz pobrać komponent trasy nadrzędnej wewnątrz komponentu podrzędnego z wtryskiwacza, a następnie pobrać dowolny z komponentu podrzędnego. W takim przypadku jak ten

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

  ...
}
Danxil
źródło
2

Przekazywanie wystąpienia Injector do konstruktora w komponencie podrzędnym może nie być dobre, jeśli chcesz napisać testy jednostkowe dla swojego kodu.

Najłatwiejszym sposobem obejścia tego jest posiadanie klasy usługi w komponencie nadrzędnym, w której zapisujesz wymagane parametry.

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

Następnie po prostu wstrzykujesz tę samą usługę do komponentów podrzędnych i uzyskujesz dostęp do parametrów.

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

Należy zauważyć, że zakres takiej usługi powinien obejmować komponent nadrzędny i jego komponenty potomne przy użyciu „dostawców” w dekoratorze komponentów nadrzędnych.

Polecam ten artykuł o DI i zakresach w Angular 2: http://blog.hardtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

qdb
źródło
2

W RC6, router 3.0.0-rc.2 (prawdopodobnie działa również w RC5), możesz pobrać parametry trasy z adresu URL jako migawkę na wypadek, gdyby parametry się nie zmieniły, bez obserwałów z tą jedną linijką:

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

Nie zapomnij wstrzyknąć ActivatedRoute w następujący sposób:

constructor(private route: ActivatedRoute) {};

mrgoos
źródło
2

Dzięki RxJS Observable.combineLatestmożemy uzyskać coś zbliżonego do idiomatycznej obsługi parametrów:

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'];
        });
  }
}
In-Ho Yi
źródło
1

Skończyło się na tym, że napisałem tego rodzaju hack do 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;
}

Teraz na widoku zbieram parametry na widoku zamiast czytać RouteParams, po prostu przesyłam je przez 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];
  }

  ...
}  
Mikael Lepistö
źródło
Działa świetnie! Skończyło się na tym, że uczyniłem tę metodę prywatną wewnątrz MergedRouteParamsklasy, która implementuje getmetodę RouteParamsklasy standardowej (drugi parametr to indeks, domyślnie zero).
Jim Buck
0

W FINAL z małą pomocą RXJS można połączyć obie mapy (od dziecka i rodzica):

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

Inne pytania, które można mieć:

  • Czy to naprawdę dobry pomysł - ze względu na sprzężenie (para komponentu potomnego z parametrami rodzica - nie na poziomie api - ukryte sprzężenie),
  • Czy jest to właściwe podejście do RXJS (wymagałoby to od użytkowników hardcore RXJS;)
wendro
źródło
0

Możesz to zrobić na migawce w następujący sposób, ale jeśli się zmieni, Twoja idwłaściwość nie zostanie zaktualizowana.

Ten przykład pokazuje również, jak można zasubskrybować wszystkie zmiany parametrów przodków i wyszukać tę, która Cię interesuje, łącząc wszystkie obserwowalne parametrów. Należy jednak zachować ostrożność w przypadku tej metody, ponieważ może istnieć wielu przodków, które mają ten sam klucz / nazwę parametru.

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();
  }
}
shattar
źródło
0

Pobieranie parametrów RouteParams z komponentu nadrzędnego w Angular 8 -

Mam trasę http: // localhost: 4200 / partner / student-profile / 1234 / info

Trasa nadrzędna - profil ucznia

Param - 1234 (student_id)

Trasa dziecięca - informacje


Dostęp do parametrów w trasie podrzędnej (informacje) -

Import

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

Konstruktor

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

Dostęp do parametrów trasy nadrzędnej

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


Teraz nasza zmienna studentId ma wartość param.

Vaibhav Vijay
źródło