Emisión de eventos personalizados con @Output en Angular

  • Por
Explicamos la comunicación de cambios de los hijos hacia los padres por medio de eventos personalizados, generados con propiedades @Output.

En el pasado artículo del Manual de Angular conocimos las propiedades @Input, que permitían comunicar datos desde el componente padre hacia el componente hijo.

También comentamos que existe la posibilidad de implementar la otra dirección en la comunicación entre componentes: desde los hijos hacia los padres. Ésta se realiza mediante la emisión de un evento personalizado, pero no llegamos a explicar cómo se implementaba.

Así que vamos a poner manos a la obra para conocer las propiedades @Output, que nos permitirán la emisión de esos eventos personalizados, con los que avisar a los padres de cualquier situación que ocurra en los hijos y que deban conocer.

Para aclarar las ideas, resumimos el esquema de trabajo de la comunicación de hijos a padres, en el que están involucrados dos componentes:

  • El componente hijo será el encargado de escalar el evento hacia el padre, para avisarle de un suceso. Al avisarle, el hijo podrá comunicar un dato que el padre deba conocer, relacionado lógicamente con ese suceso.
  • El componente padre será capaz de capturar el evento personalizado emitido por el hijo y recuperar aquel dato que fue enviado.

Implementar el evento personalizado en el componente hijo

Nuestro trabajo en la comunicación de hijos a padres comienza en el componente hijo. El proceso de trabajo no es trivial y su realización implica el uso de varios actores. Para explicarlo comenzaremos presentando las partes implicadas en el proceso.

Clase EventEmitter

La clase de Angular que implementa objetos capaces de emitir un evento se llama "EventEmitter". Pertenece al "core" de Angular, por lo que necesitamos asegurarnos de importarla debidamente.

import { Component, OnInit, EventEmitter } from '@angular/core';

Para poder emitir eventos personalizados necesitaremos crear una propiedad en el componente, donde instanciar un objeto de esta clase (new EventEmitter). Solo que este proceso tiene un detalle que lo puede complicar un poco, al menos al principio, ya que hace uso de los "generics" de TypeScript.

Básicamente, el genérico nos sirve para decirle a TypeScript el tipo del dato que nuestro evento personalizado escalará hacia el padre en su comunicación. Este es el código que podríamos utilizar para crear nuestra propiedad emisora de eventos, haciendo uso del generics.

propagar = new EventEmitter<string>();

Así le estamos diciendo que nuestro emisor de eventos será capaz de emitir datos de tipo "string". Obviamente, este tipo de dato dependerá del componente y podrá ser no solamente un tipo primitivo, sino una clase o una interfaz.

Nota: Si no entiendes mucho esta parte, te recomendamos leer este artículo con mayor información de los genéricos de TypeScript.

El nombre de la propiedad, "propagar" en este caso, es importante, porque a la hora de capturar el evento en el padre tendremos que usarlo tal cual.

Decorador @Output

La propiedad de tipo "EventEmitter", necesaria para emitir el evento personalizado, debe ser decorada con @Output. Esto le dice al framework que va a existir una vía de comunicación desde el hijo al padre.

Para usar ese decorador tenemos que importarlo también. Igual que EventEmitter, la declaración de la función decoradora "Output" está dentro de "@angular/core". Nuestro import, quedará por tanto así.

import { Component, OnInit, Output, EventEmitter } from '@angular/core';

Para usar el decorador colocaremos la llamada con los paréntesis vacíos, lo que será suficiente en la mayoría de los casos. El código de la declaración de la propiedad, usando el correspondiente decorador @Output() es el siguiente:

@Output()
propagar = new EventEmitter<string>();

Método emit()

Cuando queramos disparar el evento personalizado invocaremos el método emit() del objeto EventEmitter.

Este método recibe como parámetro aquel dato que queramos hacer llegar al padre. Lógicamente, el tipo del dato que enviemos hacia el padre debe concordar con el que hayamos declarado en el genérico al instanciar la el objeto EventEmitter.

this.propagar.emit('Este dato viajará hacia el padre');

Ejemplo de componente que envía eventos al padre

Con lo que sabemos ahora ya somos capaces de enfrentarnos a nuestro primer ejemplo de componente con propiedades @Output.

Vamos a realizar un sencillo componente que tiene un campo de texto y un botón. Al pulsar el botón emitirá un evento hacia el padre, pasando la cadena escrita en el campo de texto.

Comenzaré mostrando el template del componente, que es la parte que nos ayudará a entender el comportamiento del componente.

<p>
  <input type="text" [(ngModel)]="mensaje">
  <button (click)="onPropagar()">Propagar</button>
</p>

Como puedes ver, tenemos el campo de texto, cuyo texto escrito está bindeado a la propiedad "mensaje".

Luego tenemos un botón que al hacer clic sobre él, ejecutará el método "onPropagar()".

Código TypeScript del componente

A continuación encontramos el código TypeScript del componente en cuestión. Vamos a verlo todo de una vez y luego explicaremos algunos detalles, aunque con los conceptos ya tratados anteriormente debería estar más o menos claro.

import { Component, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'dw-propagador',
  templateUrl: './propagador.component.html',
  styleUrls: ['./propagador.component.css']
})
export class PropagadorComponent implements OnInit {

  mensaje: string;

  @Output()
  propagar = new EventEmitter<string>();

  constructor() { }

  ngOnInit() {
  }

  onPropagar() {
    this.propagar.emit(this.mensaje);
  }
}

Debes fijarte en los siguientes puntos.

  • Hacemos el import de "Output" y "EventEmitter".
  • Creamos la propiedad "mensaje", de tipo string. Esta propiedad contiene el texto que vamos a enviar hacia el padre cuando se dispare el evento personalizado.
  • Creamos la propiedad "propagar". Esta es la más importante del presente ejemplo, pues es el emisor del evento. Observa que usamos el decorador @Output y que se declara como un objeto de la clase "EventEmitter". Además estamos indicando en el genérico de TypeScript que el tipo del dato que escalará hacia el padre es un string.
  • Tenemos el método onPropagar(), que se invoca al hacer clic en el botón. Ese método se encarga de emitir el evento personalizado, con this.propagar.emit(), que viajará hacia el padre. En la emisión de ese evento será entregado al padre el dato enviado por parámetro a emit(), que es el valor de la propiedad, de tipo string, llamada "mensaje".

Con esto hemos acabado nuestro trabajo con el componente hijo. Ahora nos queda usar ese componente y recibir los eventos.

Recibir eventos personalizados en el componente padre

Ahora nos vamos a centrar en el componente padre. Es el que recibirá los eventos emitidos por el hijo. Es una operativa bastante sencilla, ya que los eventos personalizados se reciben igual que los eventos que genera el navegador.

Al usar el componente hijo debemos especificar el correspondiente manejador de evento en el template del padre. El nombre del evento personalizado será el mismo con el que se definió anteriormente la propiedad emisora de evento en el hijo.

<dw-propagador
  (propagar)="procesaPropagar($event)"
></dw-propagador>

Como recordarás, los manejadores de eventos se declaran con el nombre del evento entre paréntesis. Como decíamos, el nombre del evento corresponde con el nombre de la propiedad en el componente hijo.

El manejador del evento se indica entre las comillas dobles. Ese manejador "procesaPropagar" es un método en el componente padre. Es interesante aquí el uso del parámetro "$event", que contiene el dato que está viajando del hijo al padre.

Ahora podemos ver el código de "procesaPropagar", que realizará las acciones necesarias para procesar el evento.

procesaPropagar(mensaje) {
  console.log(mensaje);
}

En nuestro caso solo estamos mostrando en la consola el dato "mensaje" que contiene la cadena enviada del hijo al padre.

Eso es todo! hemos completado el proceso de comunicación de hijos a padres, realizado mediante la emisión de eventos personalizados en Angular. Quizás pueda parecer un poco complicado en un principio, pero a fuerza de repetir lo encontrarás bastante elemental.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir