Angular 2: obtener routeParams del componente padre


¿Cómo obtengo routeParams de un componente padre?

App.ts:

@Component({
  ...
})

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

export class HomeComponent {
  ...
}

Y luego, en el ParentComponent, puedo obtener fácilmente mi param de nombre de usuario y establecer las rutas secundarias.

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

  ...
}

Pero entonces, ¿cómo puedo obtener este mismo parámetro 'username' en esos componentes secundarios? Hacer el mismo truco que el anterior, no lo hace. ¿Porque esos parámetros están definidos en el componente de perfil o algo así??

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}
Author: Aico Klein Ovink, 2015-12-28

12 answers

ACTUALIZACIÓN:

Ahora que Angular2 final fue lanzado oficialmente, la forma correcta de hacerlo es la siguiente:

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:

Así es como lo hice usando el "@angular/router": "3.0.0-alpha.paquete de 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();
    }
}

En este ejemplo la ruta tiene el siguiente formato:/parent/: id/child/: childid

export const routes: RouterConfig = [
    {
        path: '/parent/:id',
        component: ParentComponent,
        children: [
            { path: '/child/:childid', component: ChildComponent }]
    }
];
 53
Author: Fábio Junqueira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-15 17:46:03

No deberías intentar usar RouteParams en tu ChildOneComponent.

Uso RouteRegistry, ¡en su lugar!

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


  ...
}

ACTUALIZACIÓN: A partir de esta solicitud de extracción (beta angular.9): https://github.com/angular/angular/pull/7163

Ahora puede acceder a la instrucción actual sin recognize(location.path(), []).

Ejemplo:

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}

No lo he probado, todavía

Más detalles aquí:

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

ACTUALIZACIÓN 2: Un pequeño cambio a partir de angular 2.0.0.beta15:

Ahora currentInstruction ya no es una función. Además, debe cargar el enrutador root. (gracias a @Lxrd-AJ por informar)

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}
 9
Author: ProGM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-23 13:16:35

Como mencionó Günter Zöchbauer, usé el comentario en https://github.com/angular/angular/issues/6204#issuecomment-173273143 para abordar mi problema. Usé la clase Injector de angular2/core para obtener los routeparams del padre. Resulta que angular 2 no maneja rutas profundamente anidadas. Tal vez agreguen eso en el futuro.

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);
}
 7
Author: Lordking,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-22 17:25:42

Encontré una solución fea pero funcional, solicitando el inyector padre (precisamente el 2do antepasado), y obteniendo el RouteParams desde aquí.

Algo como

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

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

    this.username = params.get('username');
  }
}
 6
Author: Yohan G.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-06 22:37:05

RC5 + @ angular / router": "3.0.0-rc.1 SOLUCIÓN: Parece que this.router.routerState.queryParams ha sido obsoleto. Puede obtener los parámetros de ruta principales de esta manera:

constructor(private activatedRoute: ActivatedRoute) {
}    

this.activatedRoute.parent.params.subscribe(
  (param: any) => {
    let userId = param['userId'];
    console.log(userId);
  });
 3
Author: Stephen Paul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-26 04:48:04

Puede tomar el componente de la ruta padre dentro del componente hijo de injector y luego obtener cualquier componente hijo. En tu caso como este

@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
Author: Danxil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-15 14:49:41

Pasar la instancia del inyector al constructor en el componente hijo puede no ser bueno si desea escribir pruebas unitarias para su código.

La forma más fácil de solucionar esto es tener una clase de servicio en el componente padre, en la que guarde sus parámetros requeridos.

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

Luego simplemente inyecta el mismo servicio a los componentes secundarios y accede a los parámetros.

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

Tenga en cuenta que debe abarcar dicho servicio a su componente padre y sus componentes hijos utilizando "proveedores" en decorador de componentes padre.

Recomiendo este artículo sobre DI y alcances en Angular 2: http://blog.thoughtram.io/angular/2015/08/20/host-and-visibility-in-angular-2-dependency-injection.html

 2
Author: qdb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-23 12:54:26

En RC6, router 3.0.0-rc.2 (probablemente también funciona en RC5), puede tomar parámetros de ruta de la URL como una instantánea en caso de que los parámetros no cambien, sin observables con este trazador de líneas:

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

No olvide inyectar ActivatedRoute de la siguiente manera:

constructor(private route: ActivatedRoute) {};

 2
Author: mrgoos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-07 12:48:56

Terminé escribiendo este tipo de hack para 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;
}

Ahora a la vista colecto parámetros a la vista en lugar de leer RouteParams Solo los obtengo a través del enrutador:

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

  ...
}  
 1
Author: Mikael Lepistö,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-02 09:11:35

Con RxJS Observable.combineLatest, podemos obtener algo cercano al manejo de params idiomáticos:

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
Author: In-Ho Yi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-19 23:37:00

En FINAL con poca ayuda de RXJS puede combinar ambos mapas (de hijo y padre):

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

Otras preguntas que uno podría tener:

  • ¿Es realmente una buena idea usar lo anterior-debido al acoplamiento (emparejar el componente hijo con el param del padre - no en el nivel de api - acoplamiento oculto),
  • ¿Es el enfoque adecuado en términos de RXJS (requeriría retroalimentación de usuario de RXJS hardcore;)
 0
Author: wendro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-25 20:16:24

Puede hacerlo en la instantánea con lo siguiente, pero si cambia, su propiedad id no se actualizará.

Este ejemplo también muestra cómo puede suscribirse a todos los cambios de parámetros de los antepasados y buscar el que le interesa fusionando todos los observables de parámetros. Sin embargo, tenga cuidado con este método porque podría haber varios antepasados que tienen el mismo parámetro clave/nombre.

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
Author: shattar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-11 19:16:11