Observers complejos, de dos o más propiedades, en Polymer 2

  • Por
Introducción de los observers complejos en Polymer, declarados en la propiedad observers, que nos permiten observar de una sola vez valores en dos o más propiedades.

En el artículo anterior del Manual de Polymer 2 estudiamos los observers simples en Polymer 2, que nos permiten observar los cambios en una propiedad. Pero en la práctica no siempre es suficiente este tipo de observadores, puesto que muchos procesos dependen de más de una propiedad al mismo tiempo.

Para casos más avanzados en Polymer (tanto en la versión 1.x como en la 2.x) existen los observadores complejos, que nos permiten declararlos de una manera diferente y más versátil, que vamos a estudiar en el presente texto. Aunque ya vienen existiendo en Polymer 1.x, los observers complejos han sufrido cambios interesantes que tendrán que aprender también los que ya conocían esta librería.

Declaración observers

Los observers complejos en Polymer requieren una declaración específica, independiente de la declaración de propiedades. Esta declaración de observers complejos permite definir de manera centralizada todos los observers utilizados en un componente, pero además habilita diversas posibilidades adicionales para los métodos observadores.

Esta declaración es mucho más versátil, permitiendo observar cambios en una propiedad, pero también en un conjunto de propiedades, en atributos profundos de un objeto o en las casillas de elementos de un array. De manera similar a otras declaraciones en clases de componentes de Polymer 2, para la especificación de los observers complejos usaremos también una propiedad estática, definida por un getter de la clase del componente. No debería representar ningún problema, pues es algo que has hecho ya múltiples veces para la definición de propiedades.

En este artículo vamos a conocer la sintaxis esencial de la declaración de observers, realizando ejemplos de observar dos o más propiedades a la vez. En futuras entregas veremos otros ejemplos que nos permitirán observar arrays o bien atributos internos de los objetos, a cualquier profundidad, por medio de caminos (paths).

Los observers se indican por mediación de un array, en el que se declaran mediante una cadena todos los métodos observadores y sus propiedades, un observer por posición del array.

static get observers() {
  return [
    observer1(propiedad1, propiedad2)',
    observer2(propiedad3, propiedad4, propiedad5)',
    observerX(propiedadX)
  ]
}

En este caso hemos definido 3 observers, en los que se controla el valor de distintos juegos de propiedades. Puedes observar al mismo tiempo cualquier número de propiedades del componente:

Nota: Incluso puedes observar una única propiedad, como en el método "observerX" (aunque en este caso también podrías usar observers simples, siempre que esa propiedad no sea un array del que queremos controlar sus ítem o una propiedad profunda de un objeto).

Para cada observador se declara el conjunto de propiedades que se están vigilando, por medio de los parámetros del método observador. Luego se tienen que declarar los métodos que hacen la función de observadores, por ejemplo:

observer1(propiedad1, propiedad2) {
  //código del observador
}

Estos métodos se invocarán automáticamente cada vez que cambie una de las propiedades observadas. Como te puedes imaginar, en el método observador recibimos los valores actuales de las propiedades. No solo las propiedades que acaban de cambiar, sino el conjunto de propiedades observadas.

Ejemplo de uso de observers complejos

En el siguiente ejemplo podemos ver un componente en el que declaramos un observer complejo, por medio de la declaración "observers". Nuestro ejemplo servirá para ilustrar el caso de vigilar el valor de dos propiedades al mismo tiempo.

El componente es un supuesto elemento que nos ofrece un dato sobre la "probabilidad de lluvia". Para calcular la probabilidad de lluvia hemos supuesto que necesitamos dos valores: la humedad y la presión atmosférica. Como esa probabilidad cambia cuando cualquiera de esos dos valores se altere, tenemos que usar la declaración de observers necesariamente.

Nota: No me he dedicado a estudiar meteorología, por lo que mis suposiciones para calcular la probabilidad de lluvia son solo fruto de mi propia imaginación. En definitiva, nos da igual cómo se calcule la probabilidad de lluvia, lo que nos interesa es ver un observer de dos propiedades a la vez.

La interfaz de mi componente se puede ver en la siguiente imagen:

Existen unos controles para cambiar los valores de presión atmosférica y de humedad, para que se pueda comprobar que, cada vez que cambian, se invoca el método observador.

La declaración de la propiedad observers, con el array de métodos observadores, es la siguiente:

static get observers() {
  return [
    'calcularProbabilidad(presionAtmosferica, humedad)'
  ]
}

Ahora veamos el método observador:

calcularProbabilidad(presionAtmosferica, humedad) {
  console.log('Estoy en CalcularProbabilidad', presionAtmosferica, humedad);
  if(presionAtmosferica < 1 || humedad < 30) {
    this.probabilidadLluvia = 'Baja';
  } else if(presionAtmosferica < 2 || humedad < 50) {
    this.probabilidadLluvia = 'Media';
  } else if(presionAtmosferica < 3 || humedad < 70) {
    this.probabilidadLluvia = 'Alta';
  } else {
    this.probabilidadLluvia = 'Muy alta';
  }
}

El algoritmo usado es indiferente. Lo que tienes que ver es que el observador recibe los dos valores observados y se usan dentro del método para calcular el dato que necesitábamos. Obviamente, dentro de este método podríamos acceder a cualquier otra propiedad del componente, para obtener los valores actuales o alterarlos.

El código completo del componente lo puedes ver a continuación.

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


<dom-module id="probabilidad-lluvia">
  <template>
    <style>
      :host {
        display: flex;
        align-items: center;
        font-family: sans-serif;
        font-size: 0.9em;
        border: 1px solid #ddd;
        padding: 10px; 
        justify-content: space-around;
      }
      span {
        font-weight: bold;
        display: block;
      }
      div {
        text-align: center;
        margin: 10px;
      }
      .probabilidad {
        color: #003399;
        font-size: 1.2em;
      }
      h2 {
        font-size: 1.1em;
        margin: 0 0 10px 0;
      }
    </style>

    <div>
      <span>Presión atmosférica</span>
      [[presionAtmosferica]] 
      <footer>
        <button on-click="aumentarPresion">+</button>
        <button on-click="disminuirPresion">-</button>
      </footer>
    </div>
    <div>
      <span>Humedad</span>
      [[humedad]]%
      <footer>
          <button on-click="aumentarHumedad">+</button>
          <button on-click="disminuirHumedad">-</button>
        </footer>
    </div>
    <div class="probabilidad">
      <h2>Probabilidad de lluvia</h2>
      [[probabilidadLluvia]]
    </div>

  </template>

  <script>

    class ProbabilidadLluvia extends Polymer.Element {
      static get is() {
        return 'probabilidad-lluvia';
      }

      static get properties() {
        return {
          presionAtmosferica: Number,
          humedad: Number,
          probabilidadLluvia: String
        };
      }

      static get observers() {
        return [
          'calcularProbabilidad(presionAtmosferica, humedad)'
        ]
      }

      constructor() {
        super();
      }

      calcularProbabilidad(presionAtmosferica, humedad) {
        console.log('Estoy en CalcularProbabilidad', presionAtmosferica, humedad);
        if(presionAtmosferica < 1 || humedad < 30) {
          this.probabilidadLluvia = 'Baja';
        } else if(presionAtmosferica < 2 || humedad < 50) {
          this.probabilidadLluvia = 'Media';
        } else if(presionAtmosferica < 3 || humedad < 70) {
          this.probabilidadLluvia = 'Alta';
        } else {
          this.probabilidadLluvia = 'Muy alta';
        }
      }

      aumentarPresion() {
        this.presionAtmosferica++;
      }
      aumentarHumedad() {
        this.humedad += 10;
      }
      disminuirPresion() {
        this.presionAtmosferica--;
      }
      disminuirHumedad() {
        this.humedad -= 10;
      }
    }

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

Como práctica te sugerimos comprobar qué pasa a la hora de usar este componente, si hacemos algo como esto:

<probabilidad-lluvia presion-atmosferica="2.9" humedad="50"></probabilidad-lluvia>

Verás que, nada más inicializarse el componente, se llamará al método observador, con los valores de las propiedades definidas.

Si por contra, no se definieran todas las propiedades, pero al menos una de ellas sí:

<probabilidad-lluvia presion-atmosferica="1.5"></probabilidad-lluvia>

Se invocaría también al método observador, con el valor definido en una propiedad y el valor "undefined" en la otra.

Obtener el valor anterior de las propiedades

Una limitación de los observers complejos, en relación a los observers simples sobre una única propiedad, es que somos incapaces de recibir los valores anteriores de las propiedades.

Recuerda que en los observers simples, que se vigilaba únicamente una propiedad, recibimos como parámetro tanto el valor actual de la propiedad como el valor anterior. Esto es algo que no permiten los observers complejos, pues no hay manera de que nos provean de ese valor anterior.

Si estás haciendo un componente y para un caso concreto necesitas los valores actual y anterior de una propiedad, la única solución automática sería apoyarse en observers simples.

Nota: Si necesitas observar dos propiedades al mismo tiempo y además conseguir sus valores anteriores, el problema de usar observers sencillos serían las dependencias del observador. Pues realmente solo estarías observando una propiedad y no dos al mismo tiempo. Recuerda que, por norma, en los métodos de los observers deberíamos limitarnos a confiar solamente en las propiedades observadas y no otras propiedades del componente. Por tanto, en un observador sencillo, necesario para ser capaces de acceder al valor anterior, no deberíamos consultar otras propiedades. En el caso que lo hagas deberías tener en cuenta:
  • Las propiedades que no sean dependencias podrían no estar inicializadas
  • Podría darse el caso que cambien las propiedades que no son dependencias y que el observador no se invoque, porque solo se invocará si cambian sus dependencias.

Conclusión sobre los observers complejos a varias propiedades

Ahora ya sabes implementar observers complejos. Este conocimiento te servirá para innumerables situaciones y junto con los observers simples que conociste en el artículo anterior ya tienes una buena cantidad de información sobre cómo funcionan los observaores de Polymer 2, una de las fundamentales herramientas que nos proporcionan para la implementación de componentes.

Sin embargo, aún nos queda por aprender algunos otros usos también bastante utilizados en observers, como observar modificaciones de casillas de arrays y de propiedades profundas de objetos. En este apartado Polymer 2 ofrece algunas importantes novedades, pero esto ya es un tema que dejamos para los próximos capítulos del Manual de Polymer 2.

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