Utilitários

O Markup oferece muitas funções utilitárias que ajudam você na modelagem ou no trabalho com o DOM em geral:

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:

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.

javascript
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:

javascript
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:

javascript
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 false

Agora 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:

javascript
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.

javascript
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:

javascript
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