Binding en elementos HTML nativos en Polymer

  • Por
Cómo implementar bindeo de dos direcciones, two way binding, sobre elementos HTML nativos, o elementos que no sean de Polymer en general. En templates HTML.

El binding en Polymer es muy amplio, con numerosas alternativas y utilidades que hemos visto ya a lo largo de diversos artículos del Manual de Polymer 2. Sin embargo, debemos avisar que todos los comportamientos que hemos tratado se refieren fundamentalmente a custom elements creados por medio de la librería Polymer. Para los elementos nativos de HTML, o cualquier otro elemento que no haya sido creado mediante Polymer, hay algunos detalles que cambian.

No es cuestión de asustarse, puesto que todo lo aprendido sirve también, solo que hay que aplicar una sintaxis especial al definir el binding en el template, que vamos a explicar en el presente artículo.

Por convención vamos a dejar claros dos conceptos antes de empezar:

  • Elementos Polymer: son todos los custom elements que han sido definidos con la librería Polymer.
  • Elementos "Non-Polymer": son todos los elementos nativos del HTML y custom elements que no hayan sido creados con Polymer específicamente, como los generados únicamente con el estándar Web Components.

Por qué el binding no funciona en los elementos Non-Polymer

Solo a título informativo, los elementos de Polymer hacen internamente y de manera transparente para el desarrollador una notificación de eventos de cambio, que usa la librería para controlar cuándo las propiedades se alteran y poner en marcha los mecanismos de binding a dos direcciones.

Sin embargo, elementos nativos del HTML y Non-Polymer en general no realizan esas notificaciones de cambios, por lo que es imposible que Polymer realice los mecanismos de binding a dos direcciones.

Sintaxis para el binding a dos direcciones en Non-Polymer elements

Para que Polymer ponga en marcha el binding en dos direcciones, sin necesidad de las notificaciones de eventos ante cambios, hay que especificar en el template el binding con una sintaxis especial.

Simplemente se coloca, entre las dos llaves que definen el "two way binding", el nombre de la propiedad bindeada, seguido de "::" y el nombre del evento que debe activar el binding.

Por ejemplo, si queremos estar atentos a los cambios sobre un campo input, aplicando doble binding en una propiedad, debemos escribir algo como esto:

<input value="{{propiedadBindeada::input}}">

En este campo input tenemos bindeado el value (lo que hay escrito) a la "propiedadBindeada". Pero el binding en dos direcciones sólo se producirá ante el evento "input" del campo input. Parece un poco raro, pero en realidad es sencillo.

El evento input de un campo de formulario se dispara cuando cambia el value de ese campo. Por lo tanto, ante cambios en el value del campo input, se producirá el binding en dos direcciones, que es lo que finalmente estábamos necesitando.

El evento "input" también lo dispara por ejemplo un campo "select". De modo que podríamos usar esta sintaxis para bindear a un menú desplegable de formulario.

<select value="{{propiedadBindeada::input}}">
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
</select>

Esto equivaldría a tener bindeado el evento change sobre este campo select, ya que se dispara en similares condiciones.

<select value="{{propiedadBindeada::change}}">
…

Podemos usar perfectamente otros eventos de elementos HTML en general, por ejemplo:

<input type="text" value="{{prop::keyup}}">

Este binding sólo se producirá en el momento que se libera la tecla, durante la escritura en el campo input.

Elementos como un checkbox no disparan el evento input, porque no cambia el value nunca del elemento. En este caso lo lógico sería usar el evento change.

<input type="checkbox" checked="{{propiedadBindeada::change}}">

Ejemplo de uso de doble binding sobre elementos nativos HTML

El ejemplo que vamos a ver ahora es una sencilla calculadora creada en un Web Component Polymer. Usa varios tipos de elementos de formulario para que podamos ver cómo se implementa el doble binding sobre ellos.

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

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

    <div>
      <span>Operador 1:</span> <input type="text" value="{{op1::keyup}}">
    </div>
    <div>
      <span>Operación:</span> 
      <select value="{{operacion::input}}">
        <option value="+">+</option>
        <option value="-">-</option>
        <option value="*">*</option>
        <option value="/">/</option>
      </select>
    </div>
    <div>
      <span>Operador 2:</span> <input type="text" value="{{op2::input}}">
    </div>
    <div>
      <span>Redondear: </span> <input type="checkbox" checked="{{redondear::change}}">
    </div>
    <div>
      <span>Resultado:</span> {{calcularResultado(op1, op2, operacion, redondear)}}
    </div>
  </template>

  <script>
    
    class CalculadoraPolymer extends Polymer.Element {
      static get is() {
        return 'calculadora-polymer';
      }

      static get properties() {
        return {
          op1: {
            type: Number,
            value: 0
          },
          op2: {
            type: Number,
            value: 0
          },
          operacion: {
            type: String,
            value: '+'
          },
          redondear: {
            type: Boolean,
            value: false
          }
        };
      }

      constructor() {
        super();
      }

      calcularResultado(op1, op2, operacion, redondear) {
        op1 = parseFloat(op1);
        op2 = parseFloat(op2);
        if(isNaN(op1) || isNaN(op2)) {
          return 'Los operadores deben ser números';
        }
        if( operacion == '+') {
          return this.redondeoCondicional(op1 + op2, redondear);
        }
        if( operacion == '-') {
          return this.redondeoCondicional(op1 - op2, redondear);          
        }
        if( operacion == '*') {
          return this.redondeoCondicional(op1 * op2, redondear);                    
        }
        if( operacion == '/' && op2 != 0) {
          return this.redondeoCondicional(op1 / op2, redondear);          
        }
        return 'Sin resultado posible'
      }

      redondeoCondicional(valor, redondear) {
        if(!redondear) {
          return valor;
        } else {
          return Math.round(valor * 100) / 100;
        }
      }

    }

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

Es importante sobre todo fijarse en el template de este componente, en el que ves varios elementos de formulario y el binding en dos direcciones.

También es interesante un binding computado, {{calcularResultado(op1, op2, operacion, redondear)}}, que se usa para mostrar el resultado de nuestra calculadora. Este tipo de binding en el que usamos una función en lugar de una propiedad, se vió en el artículo Más sobre binding.

Para usar este elemento, una vez importado, colocamos simplemente esta etiqueta.

<calculadora-polymer></calculadora-polymer>

Con esto hemos visto la mayoría de las cosas relacionadas con el Binding que debemos de conocer sobre la librería Polymer 2. Ahora es tiempo de pasar a otro tema relevante para seguir aprendiendo a desarrollar nuestros propios componentes.

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