Observar propiedades de objetos y arrays en Polymer

  • Por
Cómo observar el cambio en propiedades de objetos, así como en casillas de un array en propiedades de componentes Polymer.

Si queremos observar una propiedad de un objeto, no basta con observar el objeto completo, porque Polymer no nos informará que ha cambiado, si lo que solo ha cambiado es su propiedad. En lo que respecta a los arrays es un poco diferente, podemos observarlos pero solo nos informarán si se cambian por mecanismos propios de Polymer que enseguida explicaremos.

El motivo es básicamente no afectar al rendimiento de aplicaciones o componentes realizados en Polymer. Ya lo explicamos en el pasado artículo dedicado a los observers en Polymer, por lo que no vamos a entrar en más detalles.

Observar propiedades de objetos

Para observar las propiedades de objetos en Polymer necesitamos usar el mecanismo sofisticado del array "observers".

Crearemos funciones observadoras, indicando como parámetro qué propiedad o conjunto de propiedades queremos observar. Fíjate en el siguiente código.

observers: ['cambioAgotadas(contadores.agotadas)', 'cambioDisparadas(contadores.disparadas)']

Las funciones observadoras ahora están vigilando cambios en propiedades de objetos.

Nota: Como alternativa podríamos haber definido una única función observadora, que vigilase cambios en ambas propiedades del objeto "contadores".

observers: ['cambios(contadores.agotadas, contadores.disparadas)']

Igual que antes, las funciones observadoras recibirán el nuevo valor asignado:

cambioAgotadas: function(valor){
  console.log("cambio Agotadas, ahora vale: ", valor);
}

Método set() para modificar propiedades

Pero ojo, para que se informe de los cambios en estas propiedades debemos realizarlos obligatoriamente con métodos "set". Esto aplica a cualquier modificación de una propiedad desde nuestros propios Javascript, porque si esa propiedad se modifica por medio de mecanismos de binding siempre se notifica (si es que hay un notify con valor true, como veremos más adelante).

Nota: Mucha atención en este punto, porque no usar el correspondiente método set() cuando queremos cambiar propiedades que deseamos observar es una fuente de errores muy habitual en Polymer.

this.set("contadores.agotadas", 5);

El método this.set() es de Polymer. Realmente forma parte del prototipo básico del que heredan todos los componentes de Polymer, por lo que estará disponible siempre que lo necesites en el componente. Recibe una cadena con la propiedad a modificar y el nuevo valor que se pretende asignar.

Insistimos, esta es una condición indispensable para que tus observer se disparen. Además, si tienes bindeadas las propiedades a un template y quieres que ese valor nuevo de la propiedad también se transmita a todos los lugares donde esté bindeado, deberás usar this.set().

Otro caso especial es que quieras observar todas las propiedades de un objeto y no quieras especificarlas una a una. Entonces puedes usar el comodín asterisco *.

observers: ['mensajeDestinatarioCambiado(mensaje.destinatario.*)']

A la hora de definir la función observadora la propiedad que haya cambiado se recibe de una manera especial, ya que lo que recibimos en este caso es un objeto con dos propiedades, la cadena con la ruta a la propiedad que cambió y luego el valor nuevo que se asignó.

'mensajeDestinatarioCambiado: function(cambios) {
  console.log('Atributo cambiado: ' + cambios.path);
  console.log('Nuevo valor: ' + cambios.value);
}

Observar cambios en arrays

Los cambios en los arrays también se tienen que realizar mediante funciones especiales de Polymer para que se notifique a los observadores. Si usas las funciones nativas de Javascript, o manipulas directamente el array sin usar función alguna, no se notificará a nadie.

De momento solo vamos a enumerar las funciones de mutación de arrays: push, pop, shift, unshift, y splice. Todas dependen del prototipo básico de todo componente Polymer, así que las invocaremos a partir de "this".

De momento veamos cómo realizar un observer que se asocia a un array:

observers: ['cambiosArrayProductos(productos.splices)'],

Como ves, el array que estaríamos observando es "productos". Le agregamos ".splices" para decirle a Polymer que lo que queremos es observar cambios en sus casillas.

Ahora en la función observadora podremos consultar qué elementos se han modificado, borrado o añadido, por medio del parámetro que se le envía. Ese atributo es un registro de los cambios en el array.

cambiosArrayProductos: function(registroCambios) {
	//en registroCambios tengo las modificaciones del array observadas
}

Ese objeto que realiza el registro de los cambios es un poco complejo de usar, con respecto a lo que estamos acostumbrados en otros observers. Preferimos detallarlo en ejemplos de futuros artículos. Mientras tanto puedes consultar también la documentación de Polymer.

Ejemplo de observación de propiedades de objetos

Ahora tenemos un ejemplo de componente que realiza un seguimiento con observers de propiedades de un objeto.

Se basa en el componente que hicimos en el artículo anterior de una cuenta atrás. Ahora tendremos varios contadores hacia atrás y queremos saber cuántos han llegado al final y cuántos no. En realidad este componente lo hacemos solo por jugar un poco, tampoco tiene mucha más utilidad que la de enseñar código.

Vamos a tener dos propiedades de objetos que se muestran en el template:

<span>Disparadas: {{contadores.disparadas}}</span>
<span>Agotadas: {{contadores.agotadas}}</span>

Esas propiedades se modifican desde dos métodos. Es importante ver como se usa el método set(), que es esencial para que ocurran las notificaciones a los observer.

agotada: function() {
  //this.contadores.agotadas ++ //eso no notificaría a nadie
  this.set("contadores.agotadas", this.contadores.agotadas + 1);
},
disparada: function() {
 this.set("contadores.disparadas", this.contadores.disparadas + 1);
},

Luego tenemos declarados los observer correspondientes:

observers: ['cambioAgotadas(contadores.agotadas)', 'cambioDisparadas(contadores.disparadas)', 'cambioCualquiera(contadores.*)'],

Y los métodos observadores que se encargan de realizar acciones cuando cambien los valores de las propiedades:

cambioAgotadas: function(valor){
  console.log("cambio Agotadas, ahora vale: ", valor);
},
cambioDisparadas: function(){
  console.log("cambio Disparadas");
},
cambioCualquiera(cambios){
  console.log("En cambio cualquiera", cambios.path, cambios.value);
}

Para ordenar las ideas, te dejamos el código completo de este ejemplo de componente:

<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="cuenta-atras-eventos.html">
<dom-module id="agotadas-disparadas">
  <template>
    <style>
      article{
        width: 45%;
        margin: 1%;
        padding: 10px;
        border: 1px solid #ddd;
        display: block;
        float: left;
      }
      .marcador{
        margin: 10px;
        text-align: center;
        font-family: sans-serif;
      }
      span{
        padding: 10px;
        background-color: #ddd;
      }
    </style>
    <div class="marcador">
      <span>Disparadas: {{contadores.disparadas}}</span>
      <span>Agotadas: {{contadores.agotadas}}</span>
    </div>
    <div class="elementos">
      <template is="dom-repeat" items="{{elementos}}">
        <article><cuenta-atras-eventos on-cuenta-atras-agotada="agotada" on-cuenta-atras-disparada="disparada" cuenta="{{item}}"></cuenta-atras-eventos></article>
      </template>
    </div>
  </template>
  <script>
   Polymer({
     is: 'agotadas-disparadas',
     properties: {
       elementos: {
         type: Array,
         value: function() {
           return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
         }
       },
       contadores: {
         type: Object,
         value: function() {
           return {
             agotadas: 0,
             disparadas: 0
           }
         }
       }
     },
     observers: ['cambioAgotadas(contadores.agotadas)', 'cambioDisparadas(contadores.disparadas)', 'cambioCualquiera(contadores.*)'],
     agotada: function() {
        //this.contadores.agotadas ++ //eso no notificaría a nadie
        this.set("contadores.agotadas", this.contadores.agotadas + 1);
     },
     disparada: function() {
       this.set("contadores.disparadas", this.contadores.disparadas + 1);
     },
     cambioAgotadas: function(valor){
       console.log("cambio Agotadas, ahora vale: ", valor);
     },
     cambioDisparadas: function(){
       console.log("cambio Disparadas");
     },
     cambioCualquiera(cambios){
       console.log("En cambio cualquiera", cambios.path, cambios.value);
     }
   });
  </script>
</dom-module>

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

Comentarios

Alberto

10/5/2016
Este artículo está genial
El tema tratado es de los q causan mayor confusión y errores en los desarrolladores.
Gracias

ipep

30/3/2017
quienes son...
Hola,
Estoy poniendome al día con Polymer gracias a tu manual y en esta línea me pierdo
<cuenta-atras-eventos on-cuenta-atras-agotada="agotada" on-cuenta-atras-disparada="disparada" cuenta="{{item}}"></cuenta-atras-eventos>
¿quienes son cuenta-atras-eventos, on-cuenta-atras-agotada y on-cuenta-atras-disparada?
gracias