polymer: 8. data binding

Introducción al data binding de polymer y el flag notify

Vincent Van Gogh

archivado en: JavaScript / 1 marzo, 2016 / taller:

El concepto de doble binding le resultará familiar a quien haya trabajado con angular, un frame en el que se asocian propiedades de la vista con propiedades del controlador para que cualquier cambio en un lado u otro se refleje también en su pareja de forma automática. En polymer existe algo parecido, pero entre la parte del template y la del script y entre componentes.

En código que ahora veremos con más detalle, así por ejemplo bindearíamos una propiedad entre las dos partes de un componente definiéndola en la vista, en la template, entre un par de llaves.

<dom-module id="host-component">

<template>

<div>{{foo}}</div>

<p>

<button on-tap="setProp">Cambiar propiedad</button>

</p>

</template>

<script>

Polymer({

is: 'host-component',

properties: {

foo: {

type: String,

value: 'Uka Lele'

}

},

setProp: function() {

this.foo = "Haga Basin";

}

});

</script>

</dom-module>

Y así desde un componente padre que contiene otro hijo...

<dom-module id="host-component">

<template>

<child-component propiedad-bindeada="Hey hey"></child-component>

</template>

<script>

Polymer({

is: "host-component"

});

</script>

</dom-module>

Al igual que en el primer caso, la propiedad definida como atributo en el tag del componente hijo se «bindea» directamente en este componente mediante el par de llaves {{}}.

<dom-module id="child-component">

<template>

<div>{{propiedadBindeada}}</div>

</template>

<script>

Polymer({

is: "child-component",

properties: {

propiedadBindeada: String

}

});

</script>

</dom-module>

En este ejemplo la propiedad bindeada es una cadena de texto, pero, claro está, también puede ser un atributo del tag, en cuyo caso el comportamiento difiere un poco como veremos ahora.

Dos caminos

El binding entre componentes puede ser de dos maneras: unidireccional, que solo va desde el padre al hijo, o bidireccional, en el que también desde el hijo se puede modificar el valor del padre.

Para definir un binding unidireccional se usan dos corchetes ([[]]) y, en caso contrario, dos llaves ({{}}). Además, en este caso se deben dar tres requisitos:

  1. En el elemento hijo, la propiedad bindeada debe tener definido el flag notify como true.
  2. Además, la propiedad hija no debe tener como true el flag readOnly.
  3. La propiedad bindeada no debe ser una cadena de texto en el elemento hijo, sino un atributo.

En código, el camino para comunicarse desde el elemento contenedor, el padre, con el hijo es sencillo. Tan solo tenemos que escribir los atributos o cadenas entre llaves. Por ejemplo, dado este componente padre:

<dom-module id="host-component">

<template>

<child-component propiedad-bindeada="{{bar}}"></child-component>

<p><button on-tap="setProp">Cambiar propiedad</button></p>

</template>

<script>

Polymer({

is: 'host-component',

properties: {

bar: {

type: String,

value: 'Awesome!'

}

},

setProp: function() {

this.bar = "Cambiada!";

}

});

</script>

</dom-module>

Se establece una comunicación directa, unidireccional, con el hijo con la propiedad definida entre llaves.

<dom-module id="child-component">

<template>

<div>{{propiedadBindeada}}</div>

</template>

<script>

Polymer({

is: "child-component",

properties: {

propiedadBindeada: String

}

});

</script>

</dom-module>

wc-binding-00

Y como la propiedad está bindeada, si la cambiamos en el padre, se cambia en el hijo.

wc-binding-01

De abajo arriba

El lío se produce cuando el camino es a la inversa, desde el hijo al padre, pues como hemos visto hay que cumplir varios requisitos. Para entenderlo mejor, vamos a preparar un componente hijo que sea un input y que al escribir se cambie dinámicamente un valor en el componente contenedor.

Entonces, si todo el mondongo estuviera en el mismo componente, no habría problema de bindeo, pues bastaría con hacer algo así:

<dom-module id="child-component">

<template>

<input id="myInput" type="text" on-keypress="setProp">

<p>{{bubbleProp}}</p>

</template>

<script>

Polymer({

is: "child-component",

setProp: function() {

this.bubbleProp = this.$.myInput.value;

}

});

</script>

</dom-module>

wc-binding-02

Pero si nos llevamos la parte que muestra el cambio del input arriba, al contenedor padre...

...

<template>

<child-component bubble-prop="{{bubbleProp}}"></child-component>

<p>{{bubbleProp}}</p>

</template>

...

En el hijo tenemos que añadir el notify:

properties: {

bubbleProp: {

type: String,

notify: true

}

},

De hecho, cuando definimos el flag notify como true, se genera un evento de forma automática que podemos cazar con cualquier listener, pero este es un tema que nos desviaría ahora de la cuestión, por lo que le dedicaré he dedicado una entrada específica.

Bindear objetos

Internamente, el doble bindeo funciona como una especie de escucha. Si hubiera que engancharla en cada una de las propiedades que puede tener un objeto, que pueden ser centenares, multiplicadas a su vez por todas las instancias del componente, el coste en rendimiento sería alto, así que el equipo de polymer decidió manejar el bindeo de objetos de una manera especial.

Para entender cómo funciona vamos a preparar un componente con los datos de un dinosaurio. Algo así:

<dom-module id="host-component">

<template>

<h5>Ficha dinosaurio</h5>

<p>Género: <span>{{dino.genero}}</span><p>

<p>Dieta: <span>{{dino.dieta}}</span><p>

<button on-tap="setDino">Cambiar dinosaurio</button>

</template>

<script>

Polymer({

is: 'host-component',

properties: {

dino: {

type: Object,

value: {

genero:'Triceratops',

dieta:'herbívoro'

}

}

},

setDino: function() {

// aquí haremos algo

}

});

</script>

</dom-module>

wc-binding-03

Entonces, si intentásemos cambiar directamente una subpropiedad del objeto, por ejemplo el género, no funcionaría, ya que no estaría «bajo escucha».

setDino: function() {

/* Thats not working baby */

this.dino.genero = 'Brontosaurus';

}

Para que el bindeo funcione tenemos tres opciones. Una es cambiar todo el objeto, ya que este sí que está siendo observado.

this.dino =  {genero:'Brontosaurus', dieta:'herbívoro más o menos'}

Otra es usar un método que le incluye polymer a los componentes denominado set():

this.set('dino.genero', 'Brontosaurus');

Y la tercera sería declarar lo que denominan path, esto es, el camino hacia la subpropiedad, con el método notifyPath().

this.notifyPath('dino.genero', 'Brontosaurus');

Con los arrays sucede un jaleo aun más complicado, por lo que les dedicaré la próxima entrada de este taller de forma específica y termino este ladrillako hablando de los atributos bindeados.

Binding en atributos nativos

Como es sabido, los elementos de html tienen una montonera de atributos naturales, de fábrica, como class, id, value, width y un largo etcétera, cuyo valor puede puede ser una propiedad bindeada como si estuviéramos bindeando cualquier otra cosa. Aunque hay una sintaxis más enrevesada que asocia la propiedad al evento, para bindear los atributos basta con incluir la propiedad entre el par de llaves.

<input type="text" value="{{foo}}">

<button on-tap="setFoo">Cambiar foo</button>

...

properties: {

foo: {

type: String,

value: "Haga Basin"

}

},

setFoo: function() {

this.foo = "Awesome!";

},

La única excepción a esto son los atributos style, href, class, for y data-*, a los que por alguna razón que desconozco hay que añadir el símbolo $ antes del igual:

<div class$={{foo}} data-algo$={{bar}}></div>

Bueno, pues de momento lo dejo aquí. En la próxima entrada retomo el tema del bindeo y la problemática de los arrays.

|| Tags: ,

valoración de los lectores sobre polymer: 8. data binding

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 5 sobre 5 (2 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Aportar un comentario


*