Угловой 2: Получение RouteParams от родительского компонента
как я могу получить RouteParams от родительского компонента?
App.ts:
@Component({
...
})
@RouteConfig([
{path: '/', component: HomeComponent, as: 'Home'},
{path: '/:username/...', component: ParentComponent, as: 'Parent'}
])
export class HomeComponent {
...
}
и затем в ParentComponent, Я могу легко получить свое имя пользователя param и установить дочерние маршруты.
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
}
...
}
12 ответов:
обновление:
Теперь, когда Angular2 final был официально выпущен, правильный способ сделать это заключается в следующем:
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 }] } ];
вы не должны пытаться использовать
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']); }) } ... }обновление: как из этого запроса тяги (угловая бета.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']; } ... }
как упоминал Гюнтер Цехбауэр, я использовал комментарий в https://github.com/angular/angular/issues/6204#issuecomment-173273143 чтобы решить мою проблему. Я использовал
Injectorклассangular2/coreчтобы получить routeparams родителя. Оказывается, 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); }
Я нашел уродливое, но рабочее решение, запросив Родительский (именно 2-й предок) инжектор и получив
RouteParamsотсюда.что-то вроде
@Component({ ... }) export class ChildOneComponent { public username: string; constructor(injector: Injector) { let params = injector.parent.parent.get(RouteParams); this.username = params.get('username'); } }
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); });
вы можете взять компонент родительского маршрута внутри дочернего компонента из инжектора, а затем получить любой из дочернего компонента. В вашем случае, как это
@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'); } ... }
передача экземпляра инжектора конструктору в дочернем компоненте может быть не очень хорошей, если вы хотите написать модульные тесты для своего кода.
@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
в RC6, маршрутизатор 3.0.0-rc.2 (вероятно, работает и в RC5), вы можете взять параметры маршрута из URL как снимок в случае, если параметры не изменятся, без наблюдаемых с этим одним лайнером:
this.route.snapshot.parent.params['username'];Не забудьте ввести ActivatedRoute следующим образом:
constructor(private route: ActivatedRoute) {};
Я закончил тем, что написал этот вид взлома для 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]; } ... }
С помощью 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']; }); } }
In финал С помощью RXJS вы можете объединить обе карты (от ребенка и родителя):
(route) => Observable .zip(route.params, route.parent.params) .map(data => Object.assign({}, data[0], data[1]))другие вопросы, которые могут возникнуть:
- действительно ли это хорошая идея использовать выше-из - за связи (пара дочерних компонентов с родительским param - не на уровне api-скрытая связь),
- это правильный подход с точки зрения RXJS (это потребует хардкорной обратной связи с пользователем RXJS;)
вы можете сделать это на снимке со следующим, но если он изменится, ваш
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(); } }
Comments