Angular 2: получение RouteParams из родительского компонента

79

Как мне получить RouteParams из родительского компонента?

App.ts:

@Component({
  ...
})

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

export class HomeComponent {
  ...
}

И затем, в ParentComponent, я могу легко получить свой параметр имени пользователя и установить дочерние маршруты.

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

  ...
}

Но тогда как я могу получить тот же параметр «имя пользователя» в этих дочерних компонентах? Выполнение того же трюка, что и выше, не поможет. Потому что эти параметры определены в ProfileComponent или что-то в этом роде ??

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}
Айко Кляйн Овинк
источник
Как насчет свойства input для детей? Например, в родительском шаблоне:<child-one-component [username]="username"> ...
Марк Райкок
Это тоже сработает <routerlink [username]="username">...? И разве тогда это путь, @MarkRajcok?
Айко Кляйн Овинк
Я думаю, вы спрашиваете <a [router-link]="[ './....', {username: username} ], сработает ли что-то подобное . Извините, я понятия не имею, сработает это или нет. (Я еще не очень много играл с маршрутизацией.)
Марк Райкок
Мне очень жаль, @MarkRajcok, я неправильно набрал ... Я имел в виду <router-outlet></router-outlet>, стоит ли мне что-то добавить. Потому что там будут рендериться дочерние маршруты ..
Айко Кляйн Овинк
2
Может быть полезная информация github.com/angular/angular/issues/6204#issuecomment-173273143
Günter Zöchbauer

Ответы:

72

ОБНОВИТЬ:

Теперь, когда был официально выпущен финал Angular2, правильный способ сделать это:

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

ОРИГИНАЛ:

Вот как я это сделал с помощью пакета «@ 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();
    }
}

В этом примере маршрут имеет следующий формат: / parent /: id / child /: childid

export const routes: RouterConfig = [
    {
        path: '/parent/:id',
        component: ParentComponent,
        children: [
            { path: '/child/:childid', component: ChildComponent }]
    }
];
Фабио Жункейра
источник
2
Вам нужно вызвать это в ngOnInit (как показано), а не в конструкторе, как я по глупости пытался сделать сначала.
Кэмерон
2
Начиная с Angular 5.2 есть альтернативный способ, который не требует обхода более parent1 раза. См. Stackoverflow.com/a/48511516/4185989 Тем не менее, стоит рассмотреть шаблон subscribe/ unsubscribeиз этого ответа.
jmq
In Angular 6this.activatedRoute.parent.snapshot.params.someParam
Тасним Реза
Решение, на которое указывает @jmq, также лучше всего подходит для angular 6, нам не нужно отдельно подписываться на родительский идентификатор.
обучение ...
Отличное решение! Купить, почему мне нужно подписаться, чтобы получить родительские параметры? Параметр уже есть! :: мышление ::
moreirapontocom 09
10

Вы не должны пытаться использовать RouteParamsв своем ChildOneComponent.

Используйте RouteRegistryвместо этого!

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


  ...
}

ОБНОВЛЕНИЕ: Начиная с этого запроса на перенос (angular beta.9): https://github.com/angular/angular/pull/7163

Теперь вы можете получить доступ к текущей инструкции без recognize(location.path(), []).

Пример:

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

Я еще не пробовал

Подробности здесь:

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

ОБНОВЛЕНИЕ 2: Небольшое изменение по сравнению с angular 2.0.0.beta15:

Теперь currentInstructionэто больше не функция. Кроме того, вам необходимо загрузить rootроутер. (спасибо @ Lxrd-AJ за сообщение)

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}
ProGM
источник
Это так, как предполагается, для дочерних маршрутов? Я также сталкиваюсь с этой проблемой, когда дочерние маршруты не могут видеть параметры маршрута родительских компонентов. Например, route / users /: user_id / posts /: post_id, я не могу получить user_id из компонента сообщений .... Мне кажется хакерским использовать RouteRegistry.
mharris7190
@ mharris7190 Я обновил свой ответ. Начиная с angular beta.9, вы можете получить текущую инструкцию прямо из компонента Router.
ProGM 08
Спасибо за обновления. Я собираюсь перейти с бета.6 до бета.13, так что попробую после этого.
mharris7190 08
3
Небольшое изменение этого ответа: Use _router.root.currentInstruction.component.params['id']. Акцент на root, поскольку теперь вы получаете текущую инструкцию от корневого маршрутизатора, а не _router. PS: Я используюangular2.0.0-beta.15
Lxrd-AJ
_router.root больше не существует. (Я использую Angular 2.4.7)
Eivind Gussiås Løkseth
7

Как упоминал Гюнтер Цохбауэр, я использовал комментарий на https://github.com/angular/angular/issues/6204#issuecomment-173273143, чтобы решить свою проблему. Я использовал Injectorкласс from angular2/coreдля получения параметров маршрута родителя. Оказывается, angular 2 не обрабатывает глубоко вложенные маршруты. Может быть, они добавят это в будущем.

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
источник
8
Это больше не работает на маршрутизаторе angular2 RC.
Inn0vative1
6

Я нашел уродливое, но рабочее решение, запросив родительский (а именно второй предок) инжектор и получив RouteParamsотсюда.

Что-то вроде

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

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

    this.username = params.get('username');
  }
}
Йохан Г.
источник
Большое спасибо за то, что поделились этим, есть ли какая-нибудь запись или заявление команды angular о том, как это будет обрабатываться в будущем?
Маркус Ример
Похоже, что .parent был удален на RC3
theFreedomBanana
4

RC5 + @ angular / router ":" 3.0.0-rc.1 РЕШЕНИЕ: Похоже, что this.router.routerState.queryParamsэто устарело. Вы можете получить параметры родительского маршрута следующим образом:

constructor(private activatedRoute: ActivatedRoute) {
}    

this.activatedRoute.parent.params.subscribe(
  (param: any) => {
    let userId = param['userId'];
    console.log(userId);
  });
Стивен Пол
источник
2

Вы можете взять компонент родительского маршрута внутри дочернего компонента из инжектора, а затем получить любой из дочернего компонента. В таком случае

@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
источник
2

Передача экземпляра Injector в конструктор дочернего компонента может оказаться неэффективной, если вы хотите писать модульные тесты для своего кода.

Самый простой способ обойти это - иметь класс обслуживания в родительском компоненте, в котором вы сохраняете необходимые параметры.

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

Затем вы просто вставляете ту же услугу в дочерние компоненты и получаете доступ к параметрам.

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

Обратите внимание, что вы должны ограничить такую ​​службу своим родительским компонентом и его дочерними компонентами, используя «поставщиков» в декораторе родительского компонента.

Я рекомендую эту статью о DI и областях действия в Angular 2: http://blog.oughttram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

qdb
источник
2

В RC6 маршрутизатор 3.0.0-rc.2 (вероятно, также работает в RC5), вы можете взять параметры маршрута из URL-адреса в качестве снимка, если параметры не изменятся, без наблюдаемых с этим одним вкладышем:

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

Не забудьте ввести ActivatedRoute следующим образом:

constructor(private route: ActivatedRoute) {};

mrgoos
источник
2

С помощью RxJS Observable.combineLatestмы можем получить что-то близкое к идиоматической обработке параметров:

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

В итоге я написал такой хак для 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;
}

Теперь в поле зрения я собираю параметры в поле зрения вместо того, чтобы читать, 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];
  }

  ...
}  
Микаэль Лепистё
источник
Работает отлично! В итоге я сделал этот метод закрытым внутри MergedRouteParamsкласса, который реализует getметод стандартного RouteParamsкласса (второй параметр - это индекс, по умолчанию равен нулю).
Джим Бак,
0

В FINAL с небольшой помощью RXJS вы можете комбинировать обе карты ( дочернюю и родительскую):

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

Могут возникнуть и другие вопросы:

  • Действительно ли это хорошая идея использовать выше - из-за связи (дочерний компонент пары с родительскими параметрами - не на уровне api - скрытая связь),
  • Это правильный подход с точки зрения RXJS (для этого потребуются жесткие отзывы пользователей RXJS;)
Wendro
источник
0

Вы можете сделать это на снимке следующим образом, но если он изменится, ваше idсвойство не будет обновлено.

В этом примере также показано, как вы можете подписаться на все изменения параметров-предков и найти тот, который вас интересует, путем объединения всех наблюдаемых параметров. Однако будьте осторожны с этим методом, потому что может быть несколько предков с одним и тем же ключом / именем параметра.

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

Получение RouteParams из родительского компонента в Angular 8 -

У меня есть маршрут http: // localhost: 4200 / partner / student-profile / 1234 / info

Родительский маршрут - профиль студента

Парам - 1234 (student_id)

Детский маршрут - информация


Доступ к параметру в дочернем маршруте (информация) -

импорт

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

Конструктор

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

Доступ к параметрам родительского маршрута

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


Теперь наша переменная studentId имеет значение параметра.

Вайбхав Виджай
источник