¿Cómo emitir un evento de padre a hijo?

111

Actualmente estoy usando Angular 2. Usualmente usamos @Output() addTab = new EventEmitter<any>();y luego addTab.emit()para emitir un evento al componente principal.

¿Hay alguna forma de hacerlo viceversa, de padres a hijos?

código1
fuente
2
Simplemente cambie el valor de una expresión pasada como Entrada al componente secundario, y el componente secundario lo obtendrá (y será informado por ngOnChanges). También puede emitir un evento utilizando un servicio compartido que tenga un Observable.
JB Nizet

Respuestas:

192

Usando RxJs , puede declarar un Subjecten su componente principal y pasarlo como Observablecomponente secundario, el componente secundario solo necesita suscribirse a esto Observable.

Componente principal

eventsSubject: Subject<void> = new Subject<void>();

emitEventToChild() {
  this.eventsSubject.next();
}

HTML principal

<child [events]="eventsSubject.asObservable()"> </child>    

Componente hijo

private eventsSubscription: Subscription;

@Input() events: Observable<void>;

ngOnInit(){
  this.eventsSubscription = this.events.subscribe(() => doSomething());
}

ngOnDestroy() {
  this.eventsSubscription.unsubscribe();
}
BlueNC
fuente
13
Además, los datos se pueden pasar junto con el evento utilizando este enfoque. Como this.eventsSubject.next({data});en el padre, luego this.events.subscribe(({data}) => doSomething(data));en el hijo.
vince
2
He editado esta gran respuesta para agregar la cancelación de suscripción. +1
Umar Farooq Khawaja
1
Probablemente una pregunta para principiantes aquí: ¿Por qué convertir eventsSubjecta un Observable en lugar de simplemente suscribirse a él como un Sujeto?
Justin Morgan
11
Conversión de eventos Sujeto a Observable, evite que el componente hijo llame a next ().
BlueNC
2
Gracias por esta solución, FUNCIONA, pero aparece un error en la consola: "core.js: 6185 ERROR TypeError: No se puede leer la propiedad 'subscribe' de undefined" El error apunta a events.subscribe () en ngOnInit del componente secundario: "this.eventsSubscription = this.events.subscribe (() => doSomething ());" Estoy usando las versiones: "@ angular / cli": "~ 9.1.0" y "rxjs" : "~ 6.5.4"
Eduardo
72

Hasta donde yo sé, hay 2 formas estándar de hacerlo.

1. @Input

Siempre que los datos en el padre cambian, el niño recibe una notificación sobre esto en el método ngOnChanges. El niño puede actuar en consecuencia. Esta es la forma estándar de interactuar con un niño.

Parent-Component
public inputToChild: Object;

Parent-HTML
<child [data]="inputToChild"> </child>       

Child-Component: @Input() data;

ngOnChanges(changes: { [property: string]: SimpleChange }){
   // Extract changes to the input property by its name
   let change: SimpleChange = changes['data']; 
// Whenever the data in the parent changes, this method gets triggered. You 
// can act on the changes here. You will have both the previous value and the 
// current value here.
}
  1. Concepto de servicio compartido

Creando un servicio y usando un observable en el servicio compartido. El niño se suscribe y siempre que haya un cambio, se notificará al niño. Este también es un método popular. Cuando desee enviar algo diferente a los datos que pasa como entrada, se puede utilizar.

SharedService
subject: Subject<Object>;

Parent-Component
constructor(sharedService: SharedService)
this.sharedService.subject.next(data);

Child-Component
constructor(sharedService: SharedService)
this.sharedService.subject.subscribe((data)=>{

// Whenever the parent emits using the next method, you can receive the data 
in here and act on it.})
prajwal gonsalves
fuente
Probé el primer método y funcionó bien hasta cierto punto y luego recibí un mensaje de error que indicaba que el valor se cambió ahora, antes es falso y ahora es verdadero. No me dio ninguna otra explicación aparte de esto.
código1
2
Dado que se refiere a un ExpressionChangedAfterItHasBeenCheckedError, este error no se lanzará en el modo de producción.
user1337
<child [data]="inputToChild"> </child>probablemente debería ser <child [data]="(inputToChild)"> </child>para obtener cambios
pmc
28

En un componente principal, puede usar @ViewChild () para acceder al método / variable del componente secundario.

@Component({
  selector: 'app-number-parent',
  templateUrl: './number-parent.component.html'
})
export class NumberParentComponent {
    @ViewChild(NumberComponent)
    private numberComponent: NumberComponent;
    increase() {
       this.numberComponent.increaseByOne();
    }
    decrease() {
       this.numberComponent.decreaseByOne();
    }
} 

Actualizar:

Angular 8 en adelante -

@ViewChild(NumberComponent, { static: false })
Hardik Patel
fuente
4
Esto parece más limpio que trabajar con observables. El inconveniente es que el niño debe estar a la vista. Si el niño se carga con enrutamiento por ejemplo, esta fallará como numberComponentserá undefined.
Shy Agam
1
¿Es esta una buena practica? Estoy de acuerdo con el hecho de que es más limpio que los observables, pero dudo de manipular las variables del niño desde el padre. De todos modos, funcionó muy bien para mis necesidades, ¡Gracias!
Takatalvi
1
Este es un buen consejo. Parece que @ViewChild se cambió en Angular 8, o al menos tuve que especificar las opciones de ViewChild. Usé esto en mi caso: @ViewChild (Otro, {estático: falso}) otro componente privado: Otro;
Tore Aurstad
8

Utilice el decorador @Input () en su componente hijo para permitir que el padre se vincule a esta entrada.

En el componente hijo, lo declaras como está:

@Input() myInputName: myType

Para vincular una propiedad de padre a hijo, debe agregar en su plantilla los corchetes de enlace y el nombre de su entrada entre ellos.

Ejemplo:

<my-child-component [myChildInputName]="myParentVar"></my-child-component>

Pero tenga cuidado, los objetos se pasan como referencia, por lo que si el objeto se actualiza en el hijo, la var del padre se actualizará demasiado. Esto podría dar lugar a algún comportamiento no deseado en algún momento. Con los tipos primarios se copia el valor.

Para ir más lejos, lea esto:

Documentos: https://angular.io/docs/ts/latest/cookbook/component-communication.html

Noki
fuente
1

Dentro del padre, puede hacer referencia al hijo usando @ViewChild. Cuando sea necesario (es decir, cuando se disparará el evento), puede ejecutar un método en el hijo del padre utilizando la referencia @ViewChild.

Paul Evans
fuente