Angular 2: получение RouteParams из родительского компонента
Как получить 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
}
...
}
Ответы
Ответ 1
UPDATE:
Теперь, когда окончательный 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();
}
}
ORIGINAL:
Вот как я это сделал, используя "@ 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
Вы не должны пытаться использовать 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.b15:
Теперь 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'];
}
...
}
Ответ 3
Как упоминал Гюнтер Зёчбауэр, я использовал комментарий https://github.com/angular/angular/issues/6204#issuecomment-173273143 для решения моей проблемы. Я использовал класс Injector
из 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);
}
Ответ 4
Я нашел уродливое, но рабочее решение, запросив инжектор родителя (точно второго предка) и получив RouteParams
отсюда.
Что-то вроде
@Component({
...
})
export class ChildOneComponent {
public username: string;
constructor(injector: Injector) {
let params = injector.parent.parent.get(RouteParams);
this.username = params.get('username');
}
}
Ответ 5
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);
});
Ответ 6
Вы можете взять компонент родительского маршрута внутри дочернего компонента из инжектора, а затем получить любой из дочернего компонента. В этом случае
@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');
}
...
}
Ответ 7
Передача экземпляра 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
Ответ 8
В конце концов я написал этот вид взлома для 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];
}
...
}
Ответ 9
В RC6, маршрутизатор 3.0.0-rc.2 (возможно, работает и в RC5), вы можете перенести параметры маршрута из URL-адреса в виде моментального снимка в случае, если параметры не изменятся, без наблюдаемых с этим одним вкладышем:
this.route.snapshot.parent.params['username'];
Не забудьте ввести ActivatedRoute следующим образом:
constructor(private route: ActivatedRoute) {};
Ответ 10
С 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'];
});
}
}
Ответ 11
В FINAL с небольшой помощью RXJS вы можете комбинировать обе карты (от дочерних и родительских):
(route) => Observable
.zip(route.params, route.parent.params)
.map(data => Object.assign({}, data[0], data[1]))
Другие вопросы, которые могут возникнуть:
- Это действительно хорошая идея использовать выше - из-за связи (пара дочернего компонента с родительским параметром - не на уровне api - скрытая связь),
- Это правильный подход в терминах RXJS (для этого потребуется обратная связь с пользователем Hardcore RXJS;)
Ответ 12
Вы можете сделать это на снимке со следующим, но если он изменится, ваше свойство 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();
}
}