Observers en Polymer

  • Por
Explicamos los observers, una de las principales herramientas en Polymer, para el trabajo con los datos de los componentes. Permiten observar una propiedad y hacer acciones cuando cambie.

Polymer contiene un juego muy potente de herramientas para trabajo con propiedades declaradas. De entre todas ellas, hoy vamos a hablar de los “Observers”. Las propiedades de Polymer ya las tratamos en un artículo anterior, por lo que no vamos a entrar en esos detalles. Nos centraremos entonces en cómo observar sus cambios por medio de los mencionados observers.

Solo por aclarar, para quien venga de AngularJS, los observers vienen a ser como los “watcher”, pero algo más sencillos de manejar. Seguramente otras librerías o frameworks ofrecen herramientas semejantes, dado que son muy útiles en proyectos de desarrollo. Básicamente porque nos permiten centralizar en un único sitio todas las cosas que deben ocurrir cuando hay un cambio en un dato. Cambie por el motivo que cambie ese dato (en Polymer será una propiedad), se invocará al correspondiente observer cuando esto ocurra, si es que se ha definido alguno.

Existen dos maneras de trabajar con los observers. Una muy sencilla en la declaración de la propiedad y otra un poco más compleja por medio de un array “observers” donde podemos declarar todos los observadores que queramos. La manera sencilla la podemos usar siempre que lo que queramos observar una única propiedad. La manera compleja la usaremos cuando queramos observar dos o más propiedades a la vez. Todo esto se verá con detalle a continuación, pero antes de comenzar a entrar en harina, es importante ir avisando ya que observar casillas de un array o propiedades de objetos tiene ciertas dificultades adicionales, luego lo comentaremos bien y explicaremos los motivos.

Observer en una propiedad de un componente

En una propiedad de un componente Polymer podemos agregar el atributo “observer”, asignando como valor el nombre de una función. Así estamos creando un observer básico (de los sencillos), que se ejecutará cada vez que dicha propiedad cambie de valor.

Las funciones que asignas como observadores las podrías entender como una especie de manejador de evento asociado al valor de la propiedad. Es solo un símil a nivel didáctico, porque un observer no es un evento, pero podrías decir que es algo parecido a un evento que se dispara cuando cambia un valor, llamando a la función observadora que se haya declarado.

Su uso es muy elemental, como verás a continuación. Vamos a ver primeramente la declaración de una propiedad donde hemos definido un observer.

cuenta: {
  type: Number,
  value: 10,
  observer: "vigilarFinCuenta"
}

Ahora veamos el código de nuestra función observadora, que no es más que un método del propio componente, que recibe como ves dos parámetros.

vigilarFinCuenta: function(valorActual, valorAnterior) {
  console.log(valorActual, valorAnterior);
  if(valorActual == 0){
    console.log(“llegamos al final de la cuenta”)
  }  
}

Cuando cambia la propiedad se envía a la función como parámetro el nuevo valor, así como el valor que anteriormente hubiera en esa propiedad, por si nos hiciera falta para algo.

Nota: Es interesante mencionar que, en el código de la función anterior, this.cuenta sería equivalente al parámetro valorActual. Podremos acceder a uno u otro indistintamente para acceder al valor actual de la propiedad.

El detalle en este tipo de observers es que solo podemos observar una propiedad individual. Si queremos observar cuando una combinación de dos propiedades cambia, entonces tenemos que cambiar la estrategia y declarar observadores de otro modo.

Declaración de observadores mediante el array "observers"

El array de “observers” (fíjate que en este caso está en plural) nos permite observar una o más propiedades. Es el mecanismo sofisticado para definir observadores, que complementa al mecanismo explicado en el punto anterior.

Se debe indicar en el array, también como cadena, cada uno de los nombres de las funciones observadoras, con el juego de parámetros equivalente a los nombres de propiedades que se están observando.

Las funciones observadoras son capaces de recibir los nuevos valores de las propiedades, una vez producido el cambio observado. Pero a diferencia Igual del método anterior de definición de observers (aquellas funciones observadoras que hemos calificado de simples, declaradas en en el objeto “properties”), en este caso no se nos informa del valor anterior de la propiedad.

Este tipo de observer, declarado en el array "observers", tiene otra característica que lo diferencia y es que la funcion observadora no se ejecutará hasta que el valor de ambas propiedades sea distinto de null o undefined. Dicho de otro modo, el observer no empieza a estar operativo hasta que ambas propiedades tengan un valor definido.

Así se define el array de observers.

observers: ['cambiaTask(task)', 'cambiaTaskBoom(task, boom)'],

Luego nos quedaría definir estas funciones observadoras, como métodos en el componente. Es similar a lo que ya hemos conocido,

cambiaTask: function(task) {
  console.log("cambiaTask", task, k);
},
cambiaTaskBoom(task, boom) {
  console.log("cambiaTaskBoom", task, boom);
}

Como te habrás dado cuenta, en el método de definición de observers no tenemos acceso al valor anterior de la propiedad. Solo nos facilitan como parámetro el valor actual.

Casos especiales: Observar objetos o arrays

Un caso especial es el de los observers sobre propiedades con valor de objeto, o de valor array. Y es que, si estamos observando un objeto, sólo se ejecutará el correspondiente observer cuando cambia el objeto completo, o sea, cuando cambia la referencia y apunta a un nuevo objeto. De similar forma al observar arrays solo se nos informará cuando cambia el array entero y no sus casillas individuales.

Esto es básicamente por motivos de rendimiento. Piensa en objetos complejos, con decenas de propiedades, que a su vez pueden ser otros objetos con más propiedades todavía. Si el observar un objeto incluyera observar cada una de sus propiedades, a cualquier nivel de profundidad, requeriría mucha cantidad de procesamiento, afectando al rendimiento. Lo mismo con un array con cientos o miles de casillas, sería poco práctico estar observando los cambios en cada una de ellas. Pero no es necesario preocuparse, porque existe un mecanismo para observar todo esto con el detalle que sea necesario.

Publicaremos en breve un artículo para ver en detalle todos los casos posibles sobre la observación de propiedades de objetos.

Ejemplo de componente con observers

Suponemos que querrás ver un ejemplo de un componente donde se usen los observers. En este ejemplo vamos a mostrar los observers sencillos, que se indican al declarar las propiedades y los observers complejos en el array de observers.

Este componente lo hemos creado simplemente con fines didácticos. Hace una sencilla cuenta hacia atrás y llegado el valor cero, se muestra un mensaje. Tiene un par de detalles interesantes de ver.

<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<dom-module id="cuenta-atras">
  <template>
    <div class="contador" hidden="{{boom}}">{{cuenta}}</div>
    <div class="boom" hidden="{{!boom}}">BOOOOOM!!!</div>
    <p>
      <paper-button on-tap="disparar" hidden="{{boom}}">Dispara</paper-button>
    </p>
  </template>
  <script>
   Polymer({
     is: 'cuenta-atras',
     properties: {
       cuenta: {
         type: Number,
         value: 10,
         observer: "vigilarFinCuenta"
       },
       task: Number,
       boom: {
         type: Boolean,
         value: false
       }
     },

     observers: ['cambiaTask(task)', 'cambiaTaskBoom(task, boom)'],

     cambiaTask: function(task) {
       console.log("cambiaTask", task);
     },
     cambiaTaskBoom(task, boom) {
       console.log("cambiaTaskBoom", task, boom);
     },
     vigilarFinCuenta: function(valorActual, valorAnterior) {
       console.log(valorActual, valorAnterior);
       if(this.cuenta == 0){
         this.cancelAsync(this.task);
         this.boom = true;
       }
     },
     decrementar: function() {
       this.task = this.async(this.decrementar, 1000);
       this.cuenta --;
     },
     ready: function() {
       this.task = this.async(this.decrementar, 1000);
     },
     disparar: function() {
       this.cuenta = 0;
     }
   });
  </script>
</dom-module>

Localiza la declaración de la propiedad "cuenta" y el observer que se ha definido: "vigilarFinCuenta". La función observadora "vigilarFinCuenta" es la que se encarga de, en cada cambio de la propiedad "cuenta" comprobar si se ha llegado al final de la cuenta atrás.

Luego hay un par de métodos helper: this.async() y this.cancelAsync(). Son equivalentes a los conocidos setTimeout() y clearTimeout() de Javascript nativo. Los usamos en este ejemplo para conseguir que cada segundo se decremente la cuenta hacia atrás en 1 unidad.

De momento eso es todo sobre Observer, aunque todavía hay cosas que se nos han quedado en el tintero y que debemos completar en futuros artículos.

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