Como hacer observers donde usamos comodines, de modo que podemos ejecutar código cuando cambia cualquier propiedad profunda de un objeto.
Lo que vamos a ver en este artículo es novedad en Polymer 2 y nos puede ahorrar mucho código, ya que ahora podemos declarar observers que están al tanto de caminos profundos configurables con comodines.
Son un tipo especial de observers complejos que analizan propiedades profundas, de los cuales ya hemos hablado en el Manual de Polymer 2. La novedad ahora es que, por medio de una única declaración de un observer, podemos controlar los cambios en un conjunto de subpropiedades y no en una única subpropiedad. Para definir este observer podremos usar el carácter * como comodín, de modo que nos afecte no solo a una propiedad, sino a todo el conjunto de subpropiedades.
Declarar un observer con comodín
En un componente podemos tener una propiedad y a su vez esta propiedad tener subpropiedades.
static get properties() {
return {
calificacion: {
type: Object,
value: function() {
return {
matematicas: 5,
lenguaje: 5,
ciencias: 5
};
}
}
};
}
Si queremos observar, en un único observer, todos los cambios en cualquiera de sus subpropiedades, entonces podemos definir un único observer usando el comodín.
static get observers() {
return [
'cambiosCalificacion(calificacion.*)'
];
}
Cada vez que cambie cualquiera de las calificaciones en este componente, se invocará al método "cambiosCalificacion".
Recepción de los datos del cambio ocurrido
Como siempre ocurre en los observadores de Polymer, el método de implementación recibe los datos que se acaban de alterar. Sin embargo, con el uso del comodín, ocurre una cosa y es que no sabemos a priori cuál de las subpropiedades ha cambiado.
Para darnos los datos precisos sobre qué propiedad ha cambiado y el nuevo valor, Polymer nos envía un parámetro en el método del observador. Este parámetro es un objeto que contiene un resumen de los cambios que ha ocurrido en el elemento que estás observando.
Este objeto que recibes contendrá los siguientes datos:
- path: Es el camino de lo que acaba de ser modificado. Te sirve para conocer cuál de las subpropiedades observadas se ha alterado.
- value: Este es el nuevo valor que se ha asignado a la propiedad alterada.
- base: Es el objeto completo que se está observando. Por ejemplo si observamos calificacion.*, lo que nos llegará es el objeto completo "calificacion", con todas sus subpropiedades.
Este podría ser el método de implementación del observer, que recibe el objeto que se acaba de describir.
cambiosCalificacion(changeRecord) {
console.log('Ha cambiado ', changeRecord.path);
console.log('El nuevo valor es ', changeRecord.value);
}
Ya dependiendo de tu código y lo que necesites hacer, puede que te interesen unas propiedades u otras del objeto recibido por parámetro, llamado changeRecord en el código anterior. Puede que incluso no lo necesites para nada si solo te interesa saber que ha cambiado alguna de sus subpropiedades pero te da igual cuál de ellas.
Ejemplo de uso de observers con comodines
Ahora veamos el código de un componente que usa lo que acabamos de aprender. Es un componente que controla las calificaciones de notas de un estudiante. El componente calcula la media de la nota que el estudiante ha obtenido y dice si está aprobado o no. Pero ojo, para que el estudiante esté aprobado debe de tener calificaciones mayores que 5 en todas las asignaturas. Solo con que una de ellas esté suspendida, su calificación global será de suspenso.
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<dom-module id="calificaciones-estudiante">
<template>
<style>
:host {
display: block
}
h1 {
margin: 5px 0;
font-size: 1.1em;
}
</style>
<h1>Matemáticas</h1>
Seleccionar calificación: <select value="{{calificacion.matematicas::change}}">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
<h1>Lenguaje</h1>
Seleccionar calificación: <select value="{{calificacion.lenguaje::change}}">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
<h1>Ciencias naturales</h1>
Seleccionar calificación: <select value="{{calificacion.ciencias::change}}">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
<h1>MEDIA</h1>
Media de las notas: [[media]]
<p hidden$="[[!aprobado]]">Curso aprobado</p>
<p hidden$="[[aprobado]]">Curso suspendido</p>
</template>
<script>
class CalificacionesEstudiante extends Polymer.Element {
static get is() {
return 'calificaciones-estudiante';
}
static get properties() {
return {
media: Number,
aprobado: {
type: Boolean,
value: true
},
calificacion: {
type: Object,
value: function() {
return {
matematicas: 5,
lenguaje: 5,
ciencias: 5
};
}
}
};
}
static get observers() {
return [
'cambiosCalificacion(calificacion.*)'
];
}
cambiosCalificacion(changeRecord) {
let suma = 0;
let materias = 0;
let estaAprobado = true;
for (let i in this.calificacion) {
materias++;
let calificacionActual = parseInt(this.calificacion[i]);
suma += calificacionActual;
if(calificacionActual < 5) {
estaAprobado = false;
}
}
this.media = suma / materias;
this.media = Math.round(this.media * 10) / 10;
this.aprobado = estaAprobado;
}
constructor() {
super();
}
}
window.customElements.define(CalificacionesEstudiante.is, CalificacionesEstudiante);
</script>
</dom-module>
Todas las notas de las asignaturas, las calificaciones, se almacenan en la misma propiedad "calificacion". Esa propiedad del componente tiene a su vez un conjunto de subpropiedades, o propiedades profundas, una para cada asignatura. El bindeo a las subpropiedades se realiza cuando se produce un cambio en el campo SELECT.
En nuestro componente hemos declarado un observer con comodín, para implementar en un único método el comportamiento a producirse cuando cambia la calificación de una asignatura.
En el método que implementa el observador se hace la tarea de recalcular la media de notas del estudiante y si está o no está aprobado de manera global. Si te fijas, aunque estamos recibiendo en este método el parámetro "changeRecord" realmente no me hace falta usarlo para nada. Esto puede ocurrir perfectamente.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...