Utilidades
Markup ofrece muchas funciones de utilidad que le ayudarán a crear plantillas o trabajar con el DOM en general:
- elemento: le permite crear fácilmente elementos DOM;
- suspense: le permite renderizar contenido de forma diferida;
- repetir: permite manejar listas o repetir contenido;
- cuando: le permite renderizar contenido condicionalmente;
- visible: le permite diferir la renderización hasta que los elementos entren en la ventana gráfica;
- is and isNot: le permite comprobar rápidamente la veracidad de los estados
- y, o, & oneOf: le permite verificar rápidamente las condiciones de los estados
- elegir: le permite leer en profundidad los valores clave del objeto de estado
¿Por qué necesitas utilidades?
En Markup, las funciones son ciudadanas de primera clase, lo que significa que los datos reactivos se representan con funciones. Para simplificar, puede crear funciones que manejen lógica específica fuera de la plantilla para que todo se vea limpio y la lógica sea reutilizable.
Puede crear funciones de utilidad para manejar cosas como:
- validación;
- transformación de datos;
- representación de base lógica;
- almacenamiento en caché;
- etc.;
Sólo necesita definir utilidades cuando trabaja con estados. Todo lo demás puede permanecer como datos estáticos.
Las utilidades son solo funciones que se representan en una plantilla y que deben llamarse cada vez que cambia el estado del que dependen.
Utilidad personalizada
El poder de trabajar con estados y plantillas surge cuando comienzas a definir tus propias utilidades. Veamos un ejemplo rápido:
Tenemos un escenario común en el que tenemos un campo de entrada que usa estado y necesitamos mostrar un mensaje determinado, ya sea que el valor sea válido o no.
const [value, updateValue] = state(null)
const handleChange = (event) => {
updateValue(event.target.value)
}
html` <input value="${value}" oninput="${handleChange}" /> `.render(
document.body
)Podríamos seguir adelante y agregar la lógica del mensaje directamente en la plantilla:
html`
<input value="${value}" onchange="${handleChange}" />
${() => {
if (/[a-z]{3,}/i.test(value())) {
return ''
}
return 'Value must me at least 3 characters'
}}
`.render(document.body)Esto no está mal, pero la lógica de la plantilla ocupa espacio y muchas de ellas dificultan la lectura de la plantilla.
Me di cuenta de que quiero marcar el campo en rojo cuando la entrada no es válida y eso se basa en la misma lógica que tenemos en la función. Entonces, puedo extraer esa lógica en una función para su reutilización:
const [value, updateValue] = state(null)
const isPristine = () => value() === null
const isValidValue = () => /[a-z]{3,}/i.test(value())
const handleChange = (event) => {
updateValue(event.target.value)
}
html`
<input value="${value}" oninput="${handleChange}" />
${isPristine} ${isValidValue}
`.render(document.body)
// renders: <input> true falseAhora tengo dos utilidades que me dicen si el valor ha cambiado alguna vez o si es válido y puedo usarlo en cualquier lugar para representar lo que sea.
Pero no necesito detenerme ahí, puedo usar funciones para representar cualquier cosa que quiera y terminar con algo como esto:
const [value, updateValue] = state(null)
const isPristine = () => value() === null
const isValidValue = () => /[a-z]{3,}/i.test(value())
const valueCSSColor = () => (or(isPristine, isValidValue)() ? 'inherit' : 'red')
const handleChange = (event) => {
updateValue(event.target.value)
}
html`
<input
value="${value}"
oninput="${handleChange}"
style="color: ${valueCSSColor}"
/>
${when(
or(isPristine, isValidValue),
html`<p>Your name</p>`,
html`<p style="color: ${valueCSSColor}">
Name must be at least 3 characters
</p>`
)}
`.render(document.body)Puede ver que tenemos funciones que manejan diferentes lógicas y las componen para una lógica aún más compleja que depende del estado.
Esta capacidad de simplemente crear y componer funciones para manejar todo es lo que hace que la creación de plantillas sea fácil y divertida en Markup.
Podemos mejorar las cosas envolviendo todo en una utilidad reutilizable para manejar valores de entrada de formulario de cualquier tipo que podamos usar con múltiples campos de formulario.
const formFieldValue = (pattern) => {
const [value, updateValue] = state(null)
const isPristine = () => value() === null
const isValid = () => pattern.test(value())
const isPristineOrValid = or(isPristine, isValid)
const color = () => (isPristineOrValid() ? 'inherit' : 'red')
return {
value,
updateValue,
color,
isPristine,
isValid,
isPristineOrValid,
}
}Y esto me permite crear fácilmente controladores de campos para cualquier campo como este:
const name = formFieldValue(/[a-z]{3,}/i)
const email = formFieldValue(/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i)
const handleNameChange = (event) => {
name.updateValue(event.target.value)
}
const handleEmailChange = (event) => {
email.updateValue(event.target.value)
}
html`
<input
type="text"
value="${name.value}"
oninput="${handleNameChange}"
style="color: ${name.color}"
/>
${when(
name.isPristineOrValid,
html`<p>Your name</p>`,
html`<p style="color: ${name.color}">
Name must be at least 3 characters
</p>`
)}
<input
type="email"
value="${email.value}"
oninput="${handleEmailChange}"
style="color: ${email.color}"
/>
${when(
email.isPristineOrValid,
html`<p>Your email</p>`,
html`<p style="color: ${email.color}">Email is not valid</p>`
)}
`.render(document.body)Como puede ver, cuando se trata de reactividad, Markup depende en gran medida y únicamente de funciones para realizar el trabajo. Esta naturaleza orientada a funciones hace que las plantillas sean simples y de alguna manera familiares para cualquiera. ¡Probar!
editar este documento