El niño escucha el evento principal en Angular 2


81

En los documentos angulares hay un tema sobre la escucha de eventos secundarios de los padres. Esta bien. ¡Pero mi propósito es algo inverso !. En mi aplicación hay un 'admin.component' que contiene la vista de diseño de la página de administración (menú de la barra lateral, barra de tareas, estado, etc.). En este componente principal configuré el sistema de enrutador para cambiar la vista principal entre otras páginas del administrador. El problema es guardar cosas después del cambio, el usuario hace clic en el botón Guardar en la barra de tareas (que se coloca en admin.component) y el componente secundario debe escuchar ese evento de clic para guardar personal.


2
Parece que la mejor práctica para hacer esto es usar un servicio y un envío observable del evento.
zpul

Su pregunta no es muy diferente de esta: stackoverflow.com/questions/35560860/…
freethebees

@freethebees Quizás la solución sea la misma, pero la forma del problema es diferente y mi intención es encontrar el mejor enfoque para esta situación.
Hamed Hamedi

No vamos a configurar un servicio para un solo evento pasado a un componente secundario.
Ben Racicot

Respuestas:


98

Creo que este documento podría serle útil:

De hecho, podría aprovechar un observable / sujeto que el padre proporciona a sus hijos. Algo como eso:

@Component({
  (...)
  template: `
    <child [parentSubject]="parentSubject"></child>
  `,
  directives: [ ChildComponent ]
})
export class ParentComponent {
  parentSubject:Subject<any> = new Subject();

  notifyChildren() {
    this.parentSubject.next('some value');
  }
}

El componente hijo puede simplemente suscribirse a este tema:

@Component({
  (...)
})
export class ChildComponent {
  @Input()
  parentSubject:Subject<any>;

  ngOnInit() {
    this.parentSubject.subscribe(event => {
      // called when the notifyChildren method is
      // called in the parent component
    });
  }

  ngOnDestroy() {
    // needed if child gets re-created (eg on some model changes)
    // note that subsequent subscriptions on the same subject will fail
    // so the parent has to re-create parentSubject on changes
    this.parentSubject.unsubscribe();
  }

}

De lo contrario, podría aprovechar un servicio compartido que contenga un tema de este tipo de manera similar ...


La devolución de llamada en mi componente secundario no se ejecutará a menos que el componente principal transmita el siguiente valor desde ngInit. ¿Por qué es ese el caso?
cjsimon

4
¿Hay alguna ventaja de utilizar este método sobre ViewChild?
sunnyiitkgp

Aunque parece más profesional, después de darle algunas horas para experimentar en mi situación, no pude salir de problemas con UnsubscribeErrors al agregar / eliminar elementos secundarios (y, por lo tanto, cancelar la suscripción al tema). Entonces, por medio de TimeBoxing, busqué la respuesta mucho más sencilla de @StephenPaul. De todos modos, gracias por la respuesta y probablemente no entiendo la biblioteca rxjs lo suficientemente bien en este momento como para comprenderla completamente y hacer los ajustes correctos (adicionales) a su ejemplo para que funcione sin problemas.
Bernoulli IT

115

Por el bien de la posteridad, pensé en mencionar la solución más convencional a esto : simplemente obtenga una referencia a ViewChild y luego llame a uno de sus métodos directamente.

@Component({
  selector: 'app-child'
})
export class ChildComponent {

  notifyMe() {
    console.log('Event Fired');
  }
}

@Component({
  selector: 'app-parent',
  template: `<app-child #child></app-child>`
})
export class ParentComponent {

  @ViewChild('child')
  private child: ChildComponent;

  ngOnInit() {
    this.child.notifyMe();
  }
}

3
Se estrella tan pronto como entro en el componente de los padres ... Cannot read property 'notifyMe' of undefined.
amable usuario

1
Fui por esta solución muy sencilla en comparación con la solución de sentimiento de alguna manera más profesional de @ThierryTemplier (vea mi comentario allí).
Bernoulli IT

2
@BernoulliIT tienes un buen punto. Supongo que al final del día logran aproximadamente lo mismo. Mi solución usa menos código. Su solución significa que el componente principal no tiene que inyectar el componente secundario para enviarle mensajes. RXJS es genial, pero a menudo veo un uso excesivo de él dentro de los proyectos de Angular. La gente piensa que es esta "bala de plata" o algo así. De hecho, puede hacer que las cosas sean mucho más complicadas de lo necesario. Mira su comentario dentro de su ngOnDestroy()método. Eso es demasiado oscuro en lo que a mí respecta. Eso es solo un error esperando suceder en mi humilde opinión.
Stephen Paul

1
"Mira su comentario dentro de su método ngOnDestroy ()". Creo que eso es exactamente lo que estaba "sufriendo" al intentar ese enfoque, luego me quedé atascado y no tuve mucho tiempo para investigar más (para un proyecto comercial en el que trabajo). Aunque "dolió un poco" mi corazón profesional;)
Bernoulli IT

1
Gracias @StephenPaul, para mí el método Parent Calls a Viewchild fue bueno, ¡pero tu respuesta me ayudó a alcanzar mi destino!
MKJ

13

Un enfoque más básico podría ser posible aquí si entiendo la pregunta correctamente. Supuestos -

  • OP tiene un botón de guardar en el componente principal
  • Los datos que deben guardarse están en los componentes secundarios
  • Se puede acceder a todos los demás datos que el componente secundario pueda necesitar desde services

En el componente principal

<button type="button" (click)="prop1=!prop1">Save Button</button>
<app-child-component [setProp]='prop1'></app-child-component>

Y en el niño ...

prop1:boolean;
  @Input()
  set setProp(p: boolean) {
    // -- perform save function here
}

Esto simplemente envía el clic del botón al componente secundario. Desde allí, el componente hijo puede guardar los datos de forma independiente.

EDITAR: si los datos de la plantilla principal también deben pasarse junto con el clic del botón, eso también es posible con este enfoque. Avíseme si ese es el caso y actualizaré los ejemplos de código.


0

Para aquellos que estan recibiendo Cannot read property 'notifyMe' of undefined

Intente llamar al método dentro en ngAfterViewInit()lugar dengOnInit()

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.