ANGULAR: Comunicación de componentes

Uno de los detalles más importantes cuando trabajamos con un framework como Angular es conseguir que los diferentes componentes que forman la aplicación sean pequeños y reutilizables. Cuando un componente empieza a tener demasiadas líneas de código o a encargarse de varias operaciones con poca relación entre sí debemos tratar de reducirlo y separar su comportamiento en otros componentes más pequeños.

Sin embargo, esto no siempre es sencillo, porque pasamos de tener todos los datos y referencias almacenados en un único componente, que nos permite trabajar con ellos como nos plazca, a dividirlos en varios componentes que pueden tener dificultades para comunicarse. Esto es precisamente lo que vamos a ver en este artículo, diferentes maneras que tienen nuestros componentes para interactuar entre ellos.

Qué vamos a ver

Vamos a trabajar en un ejemplo sencillo para ver las siguientes opciones que tenemos con Angular:

  • Enviar datos desde el componente padre al hijo
  • Modificar datos del padre desde el hijo mediante un evento
  • Invocar a una función del hijo desde el padre

Para ello el ejemplo va a consistir en lo siguiente: el componente padre contiene un número al que llamaremos ‘total’. El componente hijo consistirá en un campo de tipo numérico que permitirá sumar o restar su contenido al total. Además, el padre indicará a cada uno de los componentes hijos si la operación que ellos realizan será una suma o una resta. Por último, para ver cómo invocar a una función del hijo desde el padre, el padre podrá reiniciar el valor almacenado en el hijo mediante un método reset.

Vamos a ir viéndolo poco a poco para que quede más claro.

Enviar datos desde el componente padre al hijo

En primer lugar creamos nuestro componente hijo. Este va a consistir en un input numérico que nos permitirá indicar el valor, y un botón que será el que más adelante lance la acción para que se sume o se reste al total. Como queremos que el mismo componente sirva tanto para sumar como para restar, permitiremos que reciba un parámetro sum que le indique si va a realizar una suma o no. Este el archivo de nuestro componente:

Únicamente definimos en él dos propiedades. En primer lugar, el valor que queremos que reciba del padre, que indicará si es una suma, o de lo contrario, es una resta cuando el valor sea false. Por defecto tendrá el valor true. Presta atención en que antes de definir la propiedad hemos usado el decorador @Input() de Angular. Esto es importante porque es lo que indica a Angular que esa propiedad debe recogerla de los datos que  proporcione el componente padre al invocar a este componente desde la plantilla.

La otra propiedad simplemente la usaremos para almacenar el valor del campo numérico, ya que más adelante necesitaremos trabajar con él.

La plantilla de este componente es la siguiente:

Está compuesta por el campo numérico, al que bindeamos el valor que hemos definido previamente en la plantilla, y además posee un botón, que muestra un texto en función de si realizará una suma o una resta.

Recuerda que para usar la propiedad [(ngModel)] debes añadir antes FormsModule al array imports de tu módulo principal.

A continuación pasamos al componente padre. Este en principio simplemente definirá el valor del contador. En su plantilla, incluirá dos veces al componente hijo que hemos creado antes, uno para sumar y otro para restar.

El primer componente que añade no tiene indicado si es una suma o no, pero como su valor por defecto era true, actuará como suma. El segundo si se define como una resta, ya que se le indica que no es una suma y nuestro ejemplo solo tiene esas dos opciones.

Y con esto ya hemos visto el primer paso más sencillo, enviar datos desde el padre al hijo.

Modificar datos del padre desde el hijo mediante un evento

Ahora pasamos al siguiente paso, queremos que nuestros componentes hijos sean capaces de actuar sobre el valor que almacena el componente padre, ya que ese es su propósito.

Vamos al componente hijo y añadimos una nueva propiedad.

Fíjate en que ahora el decorador que utilizamos es @Output(). Con ello indicamos a Angular que esta propiedad va a emitir en vez de recibir. Como es un valor numérico, añadimos <number> para tiparlo.

A continuación, creamos la función que se encargará de emitir el valor del campo:

Esta función simplemente establece como positivo o negativo el valor que teníamos almacenado en función del campo sum, y después lo emite utilizando el EventEmitter que habíamos definido.

Este componente ya es capaz de hacer su parte, pero ahora el componente padre debe hacer la suya. Para ello, primero va a definir la función que realmente modifique el campo total que almacena el valor del contador:

Tan simple como que únicamente recoge el valor que se le envía y lo añade al total. Ahora solo falta un paso: el padre debe indicar al hijo que esta es la función que debe invocar enviándole el valor necesario. Volvemos a la plantilla del padre y bindeamos el manejador del evento:

A la izquierda, el valor de la propiedad en el hijo, a la derecha, la función que ejecutará el padre y que recibe el evento que se ha producido. Con esto ya tenemos funcionando nuestro ejemplo, podemos introducir diferentes valores y sumarlos o restarlos al total.

Invocar a una función del hijo desde el padre

Ya solo nos falta un paso. El hijo tendrá una función reset() para poner el valor de su <input> a cero, pero sin embargo su propia plantilla no tiene un botón que invoque a esta función. Añadiremos en el padre botones que nos permitan invocar a esta función en cada componente.

La función reset del hijo es tan simple como esta:

Y para invocarla, en el padre crearemos un botón para cada componente, quedando con este resultado:

Como se puede observar, en capa componente que hace de operador, hemos añadido una referencia mediante #. Gracias a ella, podemos llamar a su método reset() desde el botón que lo acompaña.

Y esto es todo. Hemos visto como pasar datos como propiedades del nuevo componente y cómo invocar a una función del hijo desde el padre y viceversa, para que podamos comunicar nuestros diferentes componentes en Angular. Puedes ver el ejemplo en funcionamiento aquí: ejemplo

Si tienes alguna duda o sugerencia, no dudes en utilizar los comentarios, estaré encantado de responderte!