Desarrollo JavaScript y Formación - Carlos Azaustre

Carlos Azaustre

3 formas de comunicar componentes en AngularJS 1.x

27 enero, 2016

5 minutos de lectura

💻 Desarrollo

¿Ves alguna errata o quieres modificar algo? Haz una Pull Request

Este es un post invitado escrito por Emilio Grande , autor del blog JSJutsu el cuál te invito a leer.

Emilio hace un buen resumen sobre las 3 principales maneras que existen en AngularJS 1.x para comunicar componentes (o directivas hasta la versión 1.4).

Introducción

Dentro de las necesidades más habituales en una aplicación AngularJS está la de comunicar diferentes componentes.

En AngularJS podemos hacer esto de diferentes formas, y conviene conocerlas para saber cuál aplicar en cada caso.

Una buena aplicación JavaScript está dividida en componentes y es importante saber cómo relacionarlos de la forma más limpia posible.

Vamos a ver cómo comunicar los componentes rojo y azul del siguiente ejemplo:

<div ng-app="myApp">
  <div class="controllerOne" ng-controller="controllerOne as c1">
    <!-- Componente Rojo -->
    <div class="controllerTwo" ng-controller="controllerTwo as c2">
      <input type="text" />
      <button>Send Event</button>
    </div>
    <!-- /Componente Rojo -->

    <!-- Componente Azul -->
    <div class="controllerThree" ng-controller="controllerThree as c3">
      <div>Nothing here...</div>
    </div>
    <!-- /Componente Azul -->
  </div>
</div>

Aplicación Base

En este caso los componentes están encapsulados en controladores, pero podrían ser directivas perfectamente.

Forma 1: a través del scope

La forma más sencilla y más utilizada para comunicar componentes es usando el scope.

Consiste en utilizar el scope común a los componentes más próximo.

En AngularJS 1x, cada componente tiene su propio scope. El scope hereda en forma de árbol desde el scope raíz de la aplicación, que es $rootScope.

En concreto, en este ejemplo es así:

Herencia $scope

Por tanto, en este caso podemos comunicar C2 (Rojo) y C3 (Azul), a través del componente C1. Veamos cómo:

<div ng-app="myApp">
  <div class="controllerOne" ng-controller="controllerOne as c1">
    <!-- Componente Rojo -->
    <div class="controllerTwo" ng-controller="controllerTwo as c2">
      <input type="text" ng-model="c1.message" />
      <button ng-click="c1.sendEvent()">Send Event</button>
    </div>
    <!-- /Componente Rojo -->

    <!-- Componente Azul -->
    <div class="controllerThree" ng-controller="controllerThree as c3">
      <div>{{ c1.data }}</div>
    </div>
    <!-- /Componente Azul -->
  </div>
</div>
controller("controllerOne", function () {
  var vm = this;
  vm.data = "Nothing here...";
  vm.sendEvent = function () {
    vm.data = vm.message;
  };
});

Lo que hemos hecho aquí es simplemente utilizar el scope del componente padre (C1), al que tienen acceso los componentes hijos (C2 y C3).

Comunicando con $scope

Ventajas e inconvenientes

Usar el scope es lo más simple y lo que menos código requiere.

El problema es que en una aplicación grande los scopes compartidos pueden rápidamente volverse inmanejables y establecer un grado de acoplamiento muy alto, lo cual no es deseable.

Forma 2: a través de eventos

AngularJS 1.x proporciona un sistema de eventos muy útil al que se accede mediante los métodos $broadcast, $on y $emit.

Lo que haremos aquí es: emitir eventos hacia abajo en el árbol con $broadcast.

Veamos cómo.

<div ng-app="myApp">
  <div class="controllerOne" ng-controller="controllerOne as c1">
    <!-- Componente Rojo -->
    <div class="controllerTwo" ng-controller="controllerTwo as c2">
      <input type="text" ng-model="c2.message" />
      <button ng-click="c2.sendEvent()">Send Event</button>
    </div>
    <!-- /Componente Rojo -->

    <!-- Componente Azul -->
    <div class="controllerThree" ng-controller="controllerThree as c3">
      <div>{{ c3.data }}</div>
    </div>
    <!-- /Componente Azul -->
  </div>
</div>
.controller('controllerTwo', function($scope){
  var vm = this;
  vm.sendEvent = function() {
    $scope.$parent.$broadcast('msg', vm.message);
  };
})
.controller('controllerThree', function($scope){
  var vm = this;
  vm.data = 'Nothing here...';
  $scope.$on('msg', function(evt, msg){
    vm.data = msg;
  });
});

En este caso el componente C2 emite un evento de tipo 'msg', a través del componente C1. El componente C3 está escuchando los eventos 'msg', y al recibirlos ejecuta su función de callback.

Comunicando con eventos

Ventajas e inconvenientes

Los eventos son útiles para comunicar eventos específicos de componentes padres a hijos.

En caso de tener que comunicar componentes a otro nivel y tener que recurrir a emit o scopes padres (como en el ejemplo), o incluso al $rootScope, una vez más en una aplicación grande podemos tener eventos que circulen por toda la aplicación. Esto no nos interesa.

Forma 3: a través de un servicio

La forma más elaborada de comunicar componentes, y a la vez la más mantenible, es mediante servicios intermedios requeridos por ambos componentes.

Veamos el ejemplo.

<div ng-app="myApp">
  <div class="controllerOne" ng-controller="controllerOne as c1">
    <!-- Componente Rojo -->
    <div class="controllerTwo" ng-controller="controllerTwo as c2">
      <input type="text" ng-model="c2.message" />
      <button ng-click="c2.sendEvent()">Send Event</button>
    </div>
    !-- /Componente Rojo --> !-- Componente Azul -->
    <div class="controllerThree" ng-controller="controllerThree as c3">
      <div>{{ c3.data() }}</div>
    </div>
    !-- /Componente Azul -->
  </div>
</div>
.factory('utilityService', function() {
  return {
    message: 'Nothing here...',
    getMessage: function() {
      return this.message;
    },
    setMessage: function(msg) {
      this.message = msg;
    }
  };
})

.controller('controllerTwo', function(utilityService){
  var vm = this;
  vm.sendEvent = function() {
    utilityService.setMessage(vm.message);
  };
})

.controller('controllerThree', function(utilityService){
  var vm = this;
  vm.data = function(){
    return utilityService.getMessage();
  }
});

Con esta última forma, hemos creado un servicio que inyectamos en ambos componentes, y es el que se encarga de gestionar la información a compartir.

Comunicando con servicios

Ventajas e inconvenientes

Los servicios son la mejor manera de comunicar las diferentes piezas de nuestro puzzle y pueden ser requeridos en cualquier sitio que se precise de la aplicación.

La desventaja, que requieren de más código para prepararlos y en situaciones simples puede ser "matar moscas a cañonazos".

Conclusión

AngularJS es muy flexible y nos permite compartir y comunicar las diferentes piezas de nuestra aplicación de forma organizada y controlable.

Como sabes por éste blog, Angular 2 y sus componentes cambian la forma de trabajar totalmente, pero la idea es la misma: partir la aplicación en componentes.

Además a Angular 1 todavía le queda mucho recorrido por delante :)