A qué llamamos propiedades computadas en Lit. Para qué nos pueden servir, con diversos ejemplos de cálculos de valores a partir de propiedades del componente, los cuales se harán de manera reactiva.
En este artículo vamos a seguir trabajando con propiedades de los componentes Lit, mostrando sus valores en los templates de una manera distinta a como fueron entregadas por los usuarios del componente. Para ello vamos a explorar un concepto que llamamos "propiedades computadas", que no existe como tal en Lit, pero que podemos implementar fácilmente si lo necesitamos mediante código Javascript, ya sea con expresiones o métodos del componente.
Es algo que realmente se usa mucho en el desarrollo de componentes y que veremos en numerosos ejemplos a partir de ahora.
Qué denominamos propiedades computadas
Las propiedades computadas son aquellas que se derivan de realizar un cálculo a partir del valor original de una propiedad. De esta manera, al usar la propiedad para mostrarla en la vista del componente, en lugar de usar el valor original usaremos el computado.
Creo que se aclarará mejor si mencionamos unos ejemplos típicos para los que nos pueden servir las propiedades computadas:
- Quiero mostrar el nombre completo, que es la suma del nombre y los apellidos.
- Quieres mostrar el total de una factura, que es la suma de los valores de todos los productos incluidos en ella.
- Quieres mostrar la hora local, según el huso horario del usuario, a partir de una hora que está localizada en una región específica. Por ejemplo, tengo la hora de Madrid pero quiero mostrar la hora que corresponde para un usuario que está en Canarias.
En el día a día encontrarás muy útil el uso de esta técnica para hacer transformaciones en una propiedad antes de su presentación, o realizar cálculos con varias propiedades a la vez y presentar el cálculo en la vista del componente.
Sin embargo, hay que aclarar que Lit no tiene el concepto de propiedad computada, sino que en realidad el cálculo lo harás tú por medio de un método, por ejemplo. Por lo que esto que vamos a explicar en el artículo es más bien una técnica que algo que la librería ofrezca específicamente.
Los que nos iniciamos en Web Components con la librería Polymer sí que teníamos el concepto de propiedades computadas, como una de las funcionalidades de la librería. Esta funcionalidad se eliminó en Lit, dado que el propio código Javascript la puede solucionar sin tener que desarrollarse una función específica en la librería. Como hemos mencionado, Lit pretende mantenerse lo más pegado al código Javascript nativo, para que la experiencia de desarrollo sea lo más similar al estándar Javascript, por ello implementan solamente las características que no podemos usar directamente con Javsascript "plano" y Web Components en particular.
Cómo mostrar una propiedad computada en un template
Para nuestro ejemplo vamos a crear un componente que muestra el número de años de un usuario a partir de su fecha de nacimiento. Por ejemplo, este usuario nació en 20/03/2000 y queremos mostrar su edad en el momento actual. Claro que dependiendo de la fecha del momento en el que se realice el cálculo, la edad será distinta, por lo que habrá que usar una transformación, que implementaremos por medio de esta técnica para realización de propiedades computadas.
La manera de usar una propiedad computada en un template es simplemente invocar un método que se encargará de realizar el cálculo y devolver el valor que corresponda.
Veamos este método render:
render() {
return html`<span>${this.calculateAge(this.birthday)}</span>`;
}
Apreciarás que, en vez de mostrar directamente la propiedad this.birthday
del componente (que tendrá la fecha de nacimiento), estamos invocando un método que se llama calculateAge()
que será encargado de hacer ese cálculo.
El método calculateAge()
simplemente tiene que devolver el valor resultado del cálculo, que podría quedar más o menos así:
// Inspirado en https://stackoverflow.com/questions/4060004/calculate-age-given-the-birth-date-in-the-format-yyyymmdd
calculateAge(date) {
if(!date) {
return '-';
}
var birthday = +new Date(date);
if(isNaN(birthday)) {
return 'Formato de fecha incorrecto';
}
return ~~((Date.now() - birthday) / (31557600000));
}
Como puedes ver, este método realiza el cálculo y además comprueba si el componente ha recibido realmente una fecha de nacimiento y si esa fecha tenía un formato correcto.
Hemos usado una respuesta de stackoverflow para implementar el cálculo de fecha que parece muy compacta y de elevado rendimiento. No obstante, si prefieres otro algoritmo más sencillo y entendible puedes encontrarlo en el artículo: calcular la edad a partir de la fecha de nacimiento en Javascript.
Gracias a la librería Lit, cada vez que cambie el valor de la propiedad this.birthday, se invocará de nuevo al método this.calculateAge() y se actualizará el valor en el template de manera reactiva.
Para usar este componente necesitamos pasarle la fecha en un formato como este:
<dw-age birthday="2011-02-21"></dw-age>
Si le pasamos algo que no es una fecha, entonces te indicará que el formato no es correcto.
<dw-age birthday="20"></dw-age>
<dw-age birthday="esto no es una fecha"></dw-age>
Vamos a ver ahora el código del componente completo.
import { LitElement, html, css } from 'lit';
export class DwAge extends LitElement {
static get properties() {
return {
birthday: { type: String },
};
}
render() {
return html`<span>${this.calculateAge(this.birthday)}</span>`;
}
// Inspirado en https://stackoverflow.com/questions/4060004/calculate-age-given-the-birth-date-in-the-format-yyyymmdd
calculateAge(date) {
if(!date) {
return '-';
}
var birthday = +new Date(date);
if(isNaN(birthday)) {
return 'Formato de fecha incorrecto';
}
return ~~((Date.now() - birthday) / (31557600000));
}
}
customElements.define('dw-age', DwAge);
Cálculos con más de una propiedad a la vez
Igual que hemos usado un parámetro para el cálculo de la edad, podemos usar más de un parámetro si es que el cálculo lo requiere.
Por ejemplo, podríamos realizar un cálculo de un valor de venta de un producto, aplicando el IVA. Para este cálculo necesitamos dos valores:
- El valor del producto antes de impuestos
- El porcentaje de IVA (impuestos) que se le debe aplicar al producto
Para mostrar este caso con código vamos a mejorar nuestro componente de cálculo de la edad. Para ello vamos a incorporar la posibilidad de que los usuarios indiquen el formato de entrada de la fecha, para que permita formatos de fecha como dd/mm/yyyy o yyyy-mm-dd, o cualquier cosa que sea necesaria.
Para conseguir este comportamiento tendremos ahora dos propiedades en el componente, una para recibir la fecha y otra para recibir el formato.
static get properties() {
return {
birthday: { type: String },
format: { type: String },
};
}
Además vamos a asignar un formato predeterminado para nuestra fecha, que será el usado en el caso que el usuario no indique un formato determinado para la conversión. Esto lo hacemos en el constructor de la clase del componente.
constructor() {
super();
this.format = 'yyyy-mm-dd';
}
Ahora, para usar la propiedad computada necesitaremos enviar dos datos al método que se encargará de realizar el cálculo:
render() {
return html`<span>${this.calculateAge(this.birthday, this.format)}</span>`;
}
Estos dos datos provienen de dos propiedades distintas, por lo tanto, el método se invocará cada vez que una de esas propiedades cambie. Por supuesto, será reactivo y cada cambio hará que se calcule de nuevo el valor de la propiedad computada y por tanto actualizará también el template.
Por último vamos a ver el método completo que hace el cálculo de la fecha actual, aplicando el formato suministrado.
// Inspirado en https://stackoverflow.com/questions/2945113/how-to-create-a-new-date-in-javascript-from-a-non-standard-date-format
calculateAge(date, format) {
if(!date) {
return '-';
}
var parts = date.match(/(\d+)/g),
i = 0, fmt = {};
if(!parts || parts.length != 3) {
return 'Formato de fecha incorrecto';
}
format.replace(/(yyyy|dd|mm)/g, function(part) { fmt[part] = i++; });
var birthday = +new Date(parts[fmt['yyyy']], parts[fmt['mm']]-1, parts[fmt['dd']]);
if(isNaN(birthday)) {
return 'Formato de fecha incorrecto';
}
return ~~((Date.now() - birthday) / (31557600000));
}
De nuevo, hemos usado otra respuesta de stackoverflow para componer la fecha y realizar el cálculo. Para lo que nos interesa en el artículo no es tan importante el algoritmo que uses para ese cálculo, sino que al final devolvamos el valor calculado con un return, de modo que ese valor se muestre en el template.
Con estas modificaciones, el componente de cálculo de la edad nos ha quedado de la siguiente manera.
import { LitElement, html, css } from 'lit';
export class DwAge extends LitElement {
static get properties() {
return {
birthday: { type: String },
format: { type: String },
};
}
constructor() {
super();
this.format = 'yyyy-mm-dd';
}
render() {
return html`<span>${this.calculateAge(this.birthday, this.format)}</span>`;
}
// Inspirado en https://stackoverflow.com/questions/2945113/how-to-create-a-new-date-in-javascript-from-a-non-standard-date-format
calculateAge(date, format) {
if(!date) {
return '-';
}
var parts = date.match(/(\d+)/g),
i = 0, fmt = {};
if(!parts || parts.length != 3) {
return 'Formato de fecha incorrecto';
}
format.replace(/(yyyy|dd|mm)/g, function(part) { fmt[part] = i++; });
var birthday = +new Date(parts[fmt['yyyy']], parts[fmt['mm']]-1, parts[fmt['dd']]);
if(isNaN(birthday)) {
return 'Formato de fecha incorrecto';
}
return ~~((Date.now() - birthday) / (31557600000));
}
}
customElements.define('dw-age', DwAge);
Ahora lo podemos usar con varios formatos. Si no indicamos formato, tomará el que se ha creado de manera predeterminada. Pero además podemos suministrar uno personalizado para las fechas.
<dw-age birthday="2002-06-08"></dw-age>
<dw-age birthday="01/12/1975" format="dd/mm/yyyy"></dw-age>
Esperamos que se haya podido entender el componente y se haya apreciado la utilidad de las propiedades computadas. Realmente no es que sean propiedades, sino cálculos que se realizan en función de los valores de una o más propiedades, para mostrar los resultados del cálculo en vez de las propiedades en "crudo".
Si quieres ver el código hasta este punto del proyecto del manual de Lit, puedes acceder a este enlace de GitHub.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...