Angular 2: obtener RouteParams del componente principal

79

¿Cómo obtengo los RouteParams de un componente principal?

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 parámetro de nombre de usuario y configurar 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 de 'nombre de usuario' en esos componentes secundarios? Hacer el mismo truco anterior, no lo hace. ¿Porque esos parámetros están definidos en ProfileComponent o algo así?

@Component({
  ...
})

export class ChildOneComponent {

  public username: string;

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

  ...
}
Aico Klein Ovink
fuente
¿Qué tal una propiedad de entrada en los niños? Por ejemplo, en la plantilla principal:<child-one-component [username]="username"> ...
Mark Rajcok
¿Eso también funcionaría <routerlink [username]="username">...? ¿Y es ese el camino a seguir entonces @MarkRajcok?
Aico Klein Ovink
Creo que estás preguntando si algo como <a [router-link]="[ './....', {username: username} ]funcionará. Lo siento, no tengo ni idea de si funcionará o no. (No he jugado mucho con las rutas todavía)
Mark Rajcok
Lo siento @MarkRajcok, lo escribí mal ... Quiero decir <router-outlet></router-outlet>, ¿debo poner una entrada en eso? Porque las rutas secundarias se renderizarán allí ..
Aico Klein Ovink
2
Puede haber información útil github.com/angular/angular/issues/6204#issuecomment-173273143
Günter Zöchbauer

Respuestas:

72

ACTUALIZAR:

Ahora que Angular2 final se lanzó 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 paquete "@ 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();
    }
}

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 }]
    }
];
Fábio Junqueira
fuente
2
Debe llamar a esto en ngOnInit (como se muestra) no en el constructor como traté de hacer tontamente al principio.
Cameron
2
Hay una forma alternativa desde Angular 5.2 que no requiere que recorras más de parent1 vez. Consulte stackoverflow.com/a/48511516/4185989 Sin embargo, aún vale la pena considerar el patrón subscribe/ unsubscribede esta respuesta.
jmq
En Angular 6this.activatedRoute.parent.snapshot.params.someParam
Tasnim Reza
La solución señalada por @jmq también es la mejor para angular 6, no es necesario suscribirnos por separado para la identificación principal.
aprendizaje ...
¡Solución perfecta! Compre, ¿por qué necesito suscribirme para obtener los parámetros principales? ¡El parámetro ya está ahí! :: pensando ::
moreirapontocom
10

No debe intentar usar RouteParamsen su ChildOneComponent.

¡Úsalo en su RouteRegistrylugar!

@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 (angular beta.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'];
  }

  ...
}

Aún no lo he probado

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 currentInstructionya no es una función. Además, debes cargar el rootenrutador. (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'];
  }

  ...
}
ProGM
fuente
¿Es así como se supone que debe hacerse para las rutas secundarias? También me encuentro con este problema en el que las rutas secundarias no pueden ver los parámetros de ruta de los componentes principales. Por ejemplo, route / users /: user_id / posts /: post_id, no puedo obtener el user_id del componente de publicaciones ... Me parece hacky tener que usar RouteRegistry.
mharris7190
@ mharris7190 Actualicé mi respuesta. A partir de angular beta.9 puede obtener la instrucción actual directamente desde el componente Router.
ProGM
Gracias por la actualización. Estoy a punto de actualizar de beta.6 a beta.13, así que lo probaré después de hacerlo.
mharris7190
3
Una pequeña modificación a esta respuesta, Use _router.root.currentInstruction.component.params['id']. Énfasis en la raíz, ya que ahora obtiene la instrucción actual del enrutador raíz y no _router. PD: estoy usandoangular2.0.0-beta.15
Lxrd-AJ
_router.root ya no existe. (Yo uso Angular 2.4.7)
Eivind Gussiås Løkseth
7

Como lo mencionó Günter Zöchbauer, utilicé el comentario en https://github.com/angular/angular/issues/6204#issuecomment-173273143 para abordar mi problema. Usé la Injectorclase de angular2/corepara recuperar los parámetros de ruta 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);
}
Señor rey
fuente
8
Esto ya no funciona en el enrutador angular2 RC.
Inn0vative1
6

Encontré una solución fea pero funcional, solicitando el inyector principal (precisamente el segundo ancestro) y obteniendo el RouteParamsde 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');
  }
}
Yohan G.
fuente
Muchas gracias por compartir esto, ¿hay alguna entrada de seguimiento de errores o declaración del equipo angular sobre cómo se manejará esto en el futuro?
Marcus Riemer
Parece que .parent se ha eliminado en RC3
theFreedomBanana
4

RC5 + @ angular / enrutador ":" 3.0.0-rc.1 SOLUCIÓN: Parece que this.router.routerState.queryParamsha quedado en desuso. Puede obtener los parámetros de la ruta principal de esta manera:

constructor(private activatedRoute: ActivatedRoute) {
}    

this.activatedRoute.parent.params.subscribe(
  (param: any) => {
    let userId = param['userId'];
    console.log(userId);
  });
Stephen Paul
fuente
2

Puede tomar el componente de la ruta principal dentro del componente secundario del inyector y luego obtener cualquiera del componente secundario. 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');
  }

  ...
}
Danxil
fuente
2

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 evitar esto es tener una clase de servicio en el componente principal, en la que guarda los parámetros necesarios.

@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 limitar dicho servicio a su componente principal y sus componentes secundarios utilizando "proveedores" en el decorador de componentes principales.

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

qdb
fuente
2

En RC6, enrutador 3.0.0-rc.2 (probablemente también funcione en RC5), puede tomar los 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) {};

mrgoos
fuente
2

Con RxJS Observable.combineLatest, podemos obtener algo parecido al manejo de parámetros 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'];
        });
  }
}
In-Ho Yi
fuente
1

Terminé escribiendo este tipo de truco 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, recopilo los parámetros a la vista en lugar de leerlos 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];
  }

  ...
}  
Mikael Lepistö
fuente
¡Funciona genial! Terminé haciendo que este método sea privado dentro de una MergedRouteParamsclase que implementa el getmétodo de la RouteParamsclase estándar (el segundo parámetro es el índice, el valor predeterminado es cero).
Jim Buck
0

En FINAL, con poca ayuda de RXJS , puede combinar ambos mapas (del niño y del 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 usarlo arriba, debido al acoplamiento (emparejar el componente secundario con los parámetros de los padres, no a nivel de api, acoplamiento oculto),
  • ¿Es un enfoque adecuado en términos de RXJS (requeriría comentarios de usuarios incondicionales de RXJS?)
wendro
fuente
0

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

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

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

Obtener RouteParams del componente principal en Angular 8 -

Tengo una ruta http: // localhost: 4200 / partner / student-profile / 1234 / info

Ruta principal - perfil del alumno

Param - 1234 (estudiante_id)

Ruta del niño - info


Accediendo al parámetro en la ruta secundaria (info) -

Importar

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

Constructor

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

Acceder a los parámetros de la ruta principal

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


Ahora, nuestra variable studentId tiene el valor param.

Vaibhav Vijay
fuente