Utilitários
O Markup oferece muitas funções utilitárias que ajudam você na modelagem ou no trabalho com o DOM em geral:
- element: permite criar facilmente elementos DOM;
- suspense: permite renderizar conteúdo lentamente;
- repetir: permite manipular listas ou repetir conteúdo;
- quando: permite renderizar conteúdo condicionalmente;
- visível: permite adiar a renderização até que os elementos entrem na janela de visualização;
- is e isNot: permite verificar rapidamente a veracidade dos estados
- and, or, & oneOf: permite verificar rapidamente as condições dos estados
- pick: permite ler profundamente os valores-chave do objeto de estado
Por que você precisa de utilitários?
No Markup, as funções são cidadãos de primeira classe, o que significa que os dados reativos são representados por funções. Para simplificar, você pode criar funções que lidam com lógica específica fora do modelo, para que as coisas pareçam limpas e a lógica seja reutilizável.
Você pode criar funções utilitárias para lidar com coisas como:
- validação;
- transformação de dados;
- renderização de base lógica;
- cache;
- etc;
Você só precisa definir utilitários ao trabalhar com estados. Todo o resto pode permanecer como dados estáticos.
Utilitários são apenas funções renderizadas em template e que precisam ser chamadas sempre que o estado do qual dependem for alterado.
Utilitário personalizado
O poder de trabalhar com estados e modelos surge quando você começa a definir seus próprios utilitários. Vejamos um exemplo rápido:
Temos um cenário comum onde temos um campo de entrada que usa estado e precisamos exibir uma determinada mensagem seja o valor válido ou não.
const [value, updateValue] = state(null)
const handleChange = (event) => {
updateValue(event.target.value)
}
html` <input value="${value}" oninput="${handleChange}" /> `.render(
document.body
)Poderíamos simplesmente ir em frente e adicionar a lógica da mensagem diretamente no modelo:
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)Isso não é ruim, mas a lógica do modelo ocupa espaço e muitas delas dificultam a leitura do modelo.
Percebi que quero marcar o campo em vermelho quando a entrada for inválida e isso depende da mesma lógica que temos na função. Então, posso extrair essa lógica em uma função para reutilização:
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 falseAgora tenho dois utilitários que me dizem se o valor já foi alterado ou se é válido para poder usar em qualquer lugar para renderizar o que quer que seja.
Mas não preciso parar por aí, posso usar a função para representar o que quiser e acabar com algo assim:
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)Você pode ver que temos funções que lidam com lógicas diferentes compondo-as para lógicas ainda mais complexas dependentes do estado.
Essa capacidade de apenas criar e compor funções para lidar com tudo é o que torna a modelagem ainda mais fácil e divertida no Markup.
Podemos aprofundar tudo agrupando tudo em um utilitário reutilizável para lidar com valores de entrada de formulário de qualquer tipo que possamos usar com vários campos de formulário.
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,
}
}E isso me permite criar facilmente manipuladores de campos para qualquer campo da seguinte forma:
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 você pode ver, quando se trata de reatividade, o Markup depende muito e exclusivamente de funções para realizar o trabalho. Essa natureza orientada a funções torna a modelagem simples e de alguma forma familiar para qualquer pessoa. Experimente!
editar este documento