Observers de arrays Polymer 2

  • Por
Cómo observar manipulaciones de arrays que cambien la cantidad de casillas, en Polymer 2, de modo que podamos realizar acciones cada vez que es insertada o borrada una casilla.

Los observers en Polymer 2 tienen enormes posibilidades y nos permiten realizar todo tipo de acciones cuando pasan cosas sobre las propiedades de los componentes. Hemos conocido en el Manual de Polymer 2 diversos tipos de observers y ahora le toca el turno a los que nos permiten estar al tanto sobre cambios en los arrays.

Cada vez que se inserta una casilla en un array, o que se elimina, podemos ejecutar un método y tener acceso a aquella información que ha cambiado. No son tan fáciles de manejar como otros observers ya comentados, aviso, pero con un poco de práctica los podrás dominar sin mayores problemas. Esperamos ayudarte a lo largo de este artículo.

Además de los propios observers, en el presente texto comenzaremos hablando de un tema también muy importante, como son las funciones que existen en Polymer 2 para trabajar con los arrays, que se deben usar para que las cosas funcionen como deben y se mantengan al día los mecanismos de binding.

Funciones de manipulación de arrays

El lenguaje Javascript contiene métodos específicos para realizar acciones sobre arrays, como añadir un elemento en el array o eliminar una sección del array. Esos métodos nativos de Javascript funcionan en Polymer 2 (y Polymer en general), ya que todo en el fondo es Javascript, pero tienen un problema: no desencadenan los mecanismos de binding ni tampoco producen la ejecución de los observers.

Por ejemplo, imagina que tenemos una propiedad llamada "valores" en un componente. Esta propiedad que contiene un array cualquiera. Dado que es un array Javascript, podríamos invocar al método nativo push() sobre ese array.

this.valores.push('nuevo elemento a insertar al final del array');

Esto, aunque es correcto y modifica el array perfectamente, generalmente no producirá el resultado esperado, puesto que Polymer 2 no se entera que has editado el array introduciendo una nueva propiedad.

En cambio, lo correcto es hacer uso del método push() propio de los componentes de Polymer 2, que recibe el camino donde se encuentra el array y el valor que se quiere insertar allí.

this.push('valores', 'nuevo elemento a insertar al final del array');
Nota: Si ya tienes conocimientos de Polymer 1, esto funciona exactamente igual.

Al final, el efecto sobre el array que tenemos en esa propiedad es el mismo, tanto con push() de Javascript nativo, como con el método push() de Polymer. Pero la segunda opción será preferible, puesto que producirá los comportamientos deseados en el componente.

Como podemos ver en la documentación de Polymer 2, existen varias funciones para mutación de arrays, como pop(), push(), shift(), unshift() y splices().

Declarar un observer de mutaciones en casillas de array

Para declarar un observador que vigila las casillas del array debemos de usar la sintaxis especial de los observers complejos. Aunque en vez de dar simplemente el nombre del array, vamos a indicar que queremos vigilar sus casillas.

static get observers() {
  return [
    'valorInsertadoEliminado(valores.splices)'
  ]
}

Aquí estamos definiendo un observer sobre las casillas del array. El array está en la propiedad del componente llamada "valores", pero no queremos observar el array como un todo, sino sus cambios en el número de casillas, para lo que indicamos "valores.splices" como parámetro del observador.

Nota: Este modo de observar arrays se presta a confusión. Para intentar solventar posibles dudas quiero enumerar las cosas que podrías observar en un array:

1) Observar el array como un todo. Esto sería por ejemplo observar si la referencia a un array ha cambiado. Es decir, si una variable que contenía un valor de array (recuerda que los arrays en las variables se dan por referencia, siendo como un puntero a la posición de la memoria donde está el array). En este caso tendrías que observar el array como cualquier otra propiedad de tipo numérico o tipo cadena y el observer se ejecutará cuando la variable pasa a apuntar a otro array, es decir, cuando cambia la referencia.

2) Observar la cantidad de casillas. Por ejemplo, detectar cuando un array se le agrega un elemento, o se le retira un elemento, o varios. Esto lo harás con los observers que hemos relatado en este mismo artículo. Y lo podrás conocer siempre que se usen las mencionadas funciones de manipulación de arrays.

3) Observar un cambio de valor en alguna casilla. Esta situación se podría dar si por ejemplo la casilla 1 cambia de un valor a otro. Es decir, si el valor de una casilla del array cambia. Para observar este tipo de cambios, de manera granular en cada casilla del array, hay que usar otro tipo de observers que no hemos visto todavía, que permiten mediante caminos y una especie de comodines, especificar que quieres observar cualquier propiedad de un objeto o cualquier valor de casilla en un array. En el próximo artículo de este manual es justamente de lo que vamos a hablar.

Definir el método observador para mutaciones en el array

La potencia de Polymer 2 se encuentra a la hora de definir el método declarado como observador. Este método se ejecutará ante cualquier cambio en las casillas del array, pero además Polymer le entregará como parámetro un dato crucial, que es el detalle de la mutación que se ha producido.

Así pues, si se produjo alguna mutación, en el método observer vamos a recibir un parámetro. Ese parámetro contendrá un objeto con una propiedad llamada "indexSplices". Splices podríamos traducirlo por "empalmes", pero también lo podemos entender como las secciones del array que han sido transformadas. Como pueden haberse producido transformaciones en varias casillas a la vez, dentro de indexSplices encontraremos un array, con cada una de las transformaciones. Podemos verlo si simplemente volcamos a la consola el contenido de los splices.

valorInsertadoEliminado(secciones) {
  console.log('En el observer', secciones)
}

Cada uno de los empalmes que han mutado es a su vez un objeto, que nos ofrece todas las propiedades necesarias para disponer de toda la información que nos permita entender qué es lo que cambió en el array. Los datos de cada splice son estos:

  • index: Posición donde empieza el empalme
  • removed: un array de los items que se han eliminado del array
  • addedCount: el número de elementos que se han insertado en la posición índice
  • object: El array en cuestión, una referencia al propio array, no es su valor, sino el array real que hay en esa propiedad
  • type: una cadena literal que siempre vale "splice".

Así pues, tendremos algo como esto:

Nota: Como decía, es verdad que puede parecer un poco complejo, pero lo interesante es que ahí encontrarás todos los datos que necesitas para saber qué ha ocurrido con las casillas del array. Obviamente no tienes por qué recibir valores representativos a todos esos datos en el objeto. Por ejemplo, si se han insertado casillas no recibirás ninguna información en el array removed.

En este código podemos encontrar un ejemplo de uso del parámetro splices un poco más relevante.

valorInsertadoEliminado(secciones) {
  if(secciones && secciones.indexSplices) {
    let explicaTransformacion = `El empalme comienza en: ${secciones.indexSplices[0].index}`;
    explicaTransformacion += `\nSe han agregado ${secciones.indexSplices[0].addedCount} casillas`;
    console.log(explicaTransformacion);
  }
}

Prueba a ver qué es lo que pasaría al insertar un valor, o insertar tres valores a la vez. Agregar un valor ya lo hemos visto:

this.push('valores', 'nuevo valor a insertar');

Agregar 3 valores al mismo tiempo se realiza simplemente indicando los otros valores como siguientes parámetros de push.

this.push('valores', 'nuevo valor a insertar', 'otro valor', 'más cosas a insertar');

Ejemplo de componente para testear observers sobre mutación de arreglos

Acabamos con un código de componente que no sirve para nada en concreto, solo para testear esta funcionalidad de observers en arrays que se ha explicado.

<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/paper-input/paper-input.html">

<dom-module id="test-splices">
  <template>
    <style>
      :host {
        display: block
      }
    </style>

    <paper-input value="{{nuevoValor}}" label="Escribe un valor numérico"></paper-input>
    <paper-button raised on-click="anadirValor">Añadir valor</paper-button>
    <br>
    <paper-button raised on-click="anadirTres">Insertar 3 casillas a la vez</paper-button>
    

  </template>

  <script>
    class TestSplices extends Polymer.Element {
      static get is() {
        return 'test-splices';
      }

      static get properties() {
        return {
          nuevoValor: String,
          valores: {
            type: Array,
            value: function() { return []; }
          }
        };
      }

      static get observers() {
        return [
          'valorInsertadoEliminado(valores.splices)'
        ]
      }

      anadirValor() {
        let valor = parseInt(this.nuevoValor);
        if(! isNaN(valor)) {
          console.log('agrego valor', valor)
          this.push('valores', valor);
        }
        console.log(this.valores);
      }

      anadirTres() {
        this.push('valores', 1, 2, 3);        
      }

      valorInsertadoEliminado(secciones) {
        if(secciones && secciones.indexSplices) {
          let explicaTransformacion = `El empalme comienza en: ${secciones.indexSplices[0].index}`;
          explicaTransformacion += `\nSe han agregado ${secciones.indexSplices[0].addedCount} casillas`;
          console.log(explicaTransformacion);
        }
      }

      constructor() {
        super();
      }

    }

    window.customElements.define(TestSplices.is, TestSplices);
  </script>
</dom-module>

Espero que a modo de demostración este componente te aclare algunas cosas sobre los observers en las casillas de los arrays. El componente hace en realidad pocas cosas, pero suficiente para ir haciéndose una idea sobre cómo operar con los datos que nos proporciona Polymer 2 para observar los arreglos. Lo cierto es que Polymer te ofrece mucha información cuando se altera la cantidad de casillas del array, pero dependiendo de tu programa y sus necesidades tendrás que usar algunos de esos datos, generalmente no todos.