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

  ...
}

Але тоді, як я можу отримати той самий параметр "username" у цих дочірніх компонентах? Виконуючи той самий трюк, що і вище, не робить цього. Оскільки ці параметри визначаються в ProfileComponent чи щось таке ??

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

Як щодо властивості введення для дітей? Наприклад, у батьківському шаблоні:<child-one-component [username]="username"> ...
Mark Rajcok

Це також спрацює <routerlink [username]="username">...? І чи такий шлях піти тоді @MarkRajcok?
Aico Klein Ovink

Я думаю, ви запитуєте, чи щось подібне <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
Tasnim Reza

Рішення, на яке вказує @jmq, також найкраще для кутового 6, нам не потрібно підписуватися окремо на батьківський ідентифікатор.
навчання ...

Ідеальне рішення! Придбайте, чому мені потрібно підписатися, щоб отримати батьківські параметри? Парам вже є! :: мислення ::
moreirapontocom

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: Невелика зміна від кутової 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'];
  }

  ...
}

Це так слід робити для дитячих маршрутів? Я також стикаюся з цією проблемою, коли дочірні маршрути не можуть бачити маршрути батьківських компонентів. Наприклад route / users /: user_id / posts /: post_id, я не можу отримати user_id з компонента posts .... Мені здається невдалим, що мені потрібно використовувати RouteRegistry.
mharris7190

@ mharris7190 Я оновив свою відповідь. Починаючи з angular beta.9, ви можете отримати поточну інструкцію безпосередньо з компонента Router.
ProGM

Дякуємо за оновлення. Я збираюся перейти з бета.6 до бета.13, тому спробую це після цього.
mharris7190,

3
Невелике редагування цієї відповіді, Використовуйте _router.root.currentInstruction.component.params['id']. Наголос на кореневій системі, оскільки тепер ви отримуєте поточну інструкцію від кореневого маршрутизатора, а ні _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клас від angular2/coreдля отримання маршрутних параметрів батьків. Виявляється, кутовий 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);
}

8
Це більше не працює на RC-маршрутизаторі angular2.
Inn0vative1

6

Я знайшов потворне, але робоче рішення, попросивши батьківський (саме 2-го предка) інжектор і отримавши RouteParams звідси.

Щось на зразок

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

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

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

Велике спасибі за те, що поділилися цим, чи є якийсь запис про помилки або заява команди, що займається роботою, про те, як це буде оброблятися в майбутньому?
Маркус Рімер,

Здається, .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');
  }

  ...
}

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.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html


2

У RC6, маршрутизатор 3.0.0-rc.2 (ймовірно, працює і в RC5), ви можете взяти параметри маршруту з URL-адреси як моментальний знімок, якщо параметри не зміняться, без спостережуваних за допомогою цього одного лайнера:

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

Не забудьте ввести ActivatedRoute наступним чином:

constructor(private route: ActivatedRoute) {};


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класу (другий параметр - індекс, за замовчуванням - нуль).
Jim Buck

0

У FINAL, за допомогою RXJS, ви можете поєднати обидві карти (від дочірньої та батьківської):

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

Інші запитання:

  • Чи дійсно є гарною ідеєю використовувати вище - через зчеплення (зв’язати дочірній компонент з батьківськими параметрами - не на рівні api - приховане зв’язування),
  • Чи правильний підхід щодо терміну RXJS (для цього потрібні жорсткі відгуки користувачів RXJS;)

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

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

Param - 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 має значення param.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.