Hacer componentes con Polymer 2 que soportan binding a dos direcciones, por medio de la configuración de las propiedades con notify a true.
En este artículo del Manual de Polymer 2 vamos a abordar de un tipo de propiedades directamente vinculadas al bindeo de datos, la configuración notify, que permite que los componentes notifiquen cambios a los padres mediante el sistema de binding.
Recuerda que el sistema de binding es un enlace, que permite especificar que cierto dato se compartirá por diversos elementos o componentes, algo que se especifica en un template por medio de la notación [[]] o {{}} (binding de una o de dos direcciones). Dijimos que los datos bindeados viajan siempre de los padres a los hijos, hacia abajo. Pero además opcionalmente también podríamos conseguir que viajasen desde los hijos hacia los padres, hacia arriba, usando el binding de dos direcciones.
No obstante, lo dicho anteriormente no siempre es posible. Es decir, para poder producirse el binding a dos direcciones, el componente que pase los datos al padre debe de haberse construido para que lo permita, usando la configuración notify en las propiedades necesarias.
Configuración notify en las propiedades
Al declarar una propiedad, una de las cosas que podemos indicar es si esa propiedad notificará cambios a los padres. Para ello usamos la configuración "notify".
static get properties() {
return {
datoNotificable: {
type: String,
notify: true
},
};
}
La propiedad declarada "datoNotificable" se ha configurado con "notify" a true, por lo que ya está preparada para enviar cambios a los padres que puedan usar este componente.
Ten en cuenta que, por defecto, las propiedades de los componentes tienen el valor notify a false, por lo que no escalarían los cambios hacia los padres de manera predeterminada.
Cuándo usar notify
Notify es una configuración que no se debería usar, a no ser que realmente sea necesaria. Si el componente no necesita comunicarse con el padre para nada, entonces no tiene sentido usar notify. Solo en el caso que el componente necesite comunicar con el padre tendría sentido configurar alguna de sus propiedades con notify a true, pero aún así no siempre será la mejor opción.
Imagina un componente que es un banner por el que rotan varias imágenes, o unos textos que parpadean alternando el mensaje que se visualiza. Ese banner lo colocas en la página, para adornar y quizás no necesita decirle nada al padre. Va cambiando y punto. Si se hace clic sobre el banner se podría accionar un enlace, usando una simple etiqueta A, pero no se necesita avisar al padre de nada en concreto. Entonces, obviamente, no tendrá sentido usar notify.
Ahora piensa en un componente algo diferente, por ejemplo un campo de formulario para escribir una clave, que te indica la fortaleza de esa clave. Ese campo de formulario lo puedes hacer mediante un componente que podría llamarse "clave-segura". Al usar ese componente comúnmente se colocará dentro de un formulario, que puede tener otra serie de campos creando una jerarquía de elementos como podría ser la que tienes en la imagen:
En este esquema tenemos un componente, que es un formulario de contacto completo. Ese componente tiene a su vez varios hijos, que podrían ser campos INPUT (el elemento caja de texto nativo del HTML), campos paper-input (una caja de texto con el estilo de Material Design) o aquel componente que nos referíamos antes que permite escribir una clave y te informa sobre su fortaleza.
Todos los componentes hijos de formulario-contacto tiene sentido que comuniquen el estado a su padre. En el componente formulario-contacto nos interesa saber qué datos se han escrito en todos los campos de formulario y por ello necesita que los componentes hijos notifiquen a los padres la propiedad donde se almacene aquello que hay escrito. Para ello:
- El campo INPUT, por ser nativo del HTML y no un componente de Polymer, tiene un tratamiento del binding que es un poco más avanzado y que veremos en otro artículo.
- El campo paper-input está ya construido y forma parte del catálogo de componentes del equipo de Polymer. En este caso el componente ya está construido para que permita un doble binding. Lo puedes comprobar si te fijas en la documentación del elemento, en su propiedad "value", donde indican que es una propiedad configurada con notify.
El campo clave-segura, que es un campo que bien podríamos haber desarrollado nosotros, necesitaremos crearlo de modo que el valor de la clave sea notificado al padre. Para ello tendremos que configurar la propiedad en cuestión con el valor notify a true.
La declaración de propiedades del componente clave-segura podría ser como esta:
static get properties() {
return {
clave: {
type: String,
value: '',
notify: true
},
fuerza: {
type: Number,
value: 0,
computed: 'calcularFuerzaClave(clave)'
}
};
}
Habilitar el bindeo a dos direcciones en el template
Esta parte ya la hemos realizado en otras ocasiones por lo que no habrá dudas. Sin embargo, para poder tener el binding a dos direcciones necesitamos especificarlo en el template:
<paper-input label="Escribe tu nombre" value="{{nombre}}"></paper-input>
<clave-segura clave="{{clave}}"></clave-segura>
Aunque pueda parecer obvio, tenemos que señalar:
- Si en el template colocamos bindeo con los corchetes, por ejemplo clave="[[clave]]", aunque el componente tenga la propiedad declarada como notify, no se producirá el envío del dato del hijo al padre, puesto que la configuración con los corchetes manda, indicando binding de una sola dirección.
- Si nuestro componente no tiene una propiedad configurada como notify, por mucho que en el template coloquemos el bindeo con las dos llaves {{}}, el enlace no será de dos direcciones. Como hemos señalado, no habrá envío de datos del hijo al padre si el componente no está preparado para ello con la configuración notify.
Ejemplo de componente que permite binding con notify
Como ya es costumbre, finalizamos el artículo con un nuevo ejemplo de componente realizado con Polymer 2, en el que mostramos cómo configurar con notify la propiedad necesaria.
Este componente serviría para hacer una valoración de cualquier cosa. El componente crea una lista de posibles valoraciones, que podemos seleccionar haciendo clic. Dependiendo de la valoración seleccionada, notifica hacia el padre el valor pertinente.
Vamos con el código del componente.
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<dom-module id="dw-valorar">
<template>
<style>
:host {
display: block
}
ul {
margin: 0;
padding: 0;
}
li {
list-style: none;
margin-bottom: 5px;
}
.marcado {
font-size: 110%;
font-weight: bold;
color: orange;
}
.desmarcado {
color: #999;
}
</style>
<ul>
<li on-click="marcarBueno" id="opcionbueno" class$="[[buenoClass]]"> Bueno</li>
<li on-click="marcarRegular" id="opcionregular" class$="[[regularClass]]"> Regular</li>
<li on-click="marcarMalo" id="opcionmalo" class$="[[maloClass]]"> Malo</li>
</ul>
</template>
<script>
class DwValorar extends Polymer.Element {
static get is() {
return 'dw-valorar';
}
static get properties() {
return {
valoracion: {
type: String,
notify: true,
observer: 'valoracionObserver'
},
buenoClass: {
type: String,
value: 'desmarcado'
},
regularClass: {
type: String,
value: 'desmarcado'
},
maloClass: {
type: String,
value: 'desmarcado'
}
};
}
constructor() {
super();
}
valoracionObserver(valoracion) {
console.log('valoracionObserver', valoracion);
this.buenoClass = (valoracion == 'bueno')? 'marcado' : 'desmarcado';
this.regularClass = (valoracion == 'regular')? 'marcado' : 'desmarcado';
this.maloClass = (valoracion == 'malo')? 'marcado' : 'desmarcado';
}
marcarBueno() {
this.valoracion = 'bueno';
}
marcarRegular() {
this.valoracion = 'regular';
}
marcarMalo() {
this.valoracion = 'malo';
}
}
window.customElements.define(DwValorar.is, DwValorar);
</script>
</dom-module>
Para la realización del componente hemos usado simples textos, en los que es posible hacer clic para marcar una valoración. Cada vez que se hace clic en una de las posibles valoraciones se invoca un evento para marcar la correspondiente valoración en la propiedad configurada como notify.
Cada vez que cambia la valoración, por medio de un observer, se ejecuta el método valoracionObserver(), que se encarga de adaptar el estilo de la opción marcada, para resaltarlo sobre los demás.
Para mostrar visualmente cuál es la valoración seleccionada hemos usado una clase de CSS. Para saber qué clase mostrar en cada componente he usado una propiedad y bindeado esa propiedad al atributo class.
El componente es autónomo, lo podrías usar en cualquier otro componente donde lo necesites, y mediante un binding de dos direcciones podrás estar al tanto de la valoración que se haya marcado.
<dw-valorar valoracion="{{resultadoValoracion}}"></dw-valorar>
Estéticamente este componente no es nada espectacular, pero hace su función. Pone con una clase CSS (un estilo) especial el elemento de valoración que se ha marcado, para representarlo visualmente. Quedaría más o menos como se puede ver en la imagen siguiente:
Seguro que tú puedes mejorar este componente, por ejemplo representando los mensajes de valoración con iconos de caritas sonrientes o tristes. Al usar el componente podrás utilizar el valor de la valoración, que recibimos por el binding de dos direcciones.
<dw-valorar valoracion="{{valoracion}}"></dw-valorar>
Tenemos esta valoración: [[valoracion]]
De momento eso es todo. Como práctica te sugerimos eliminar la configuración "notify" sobre la propiedad "valoracion", para comprobar cómo el padre no se enterará de los cambios, ya que no serían así notificados.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...