Framework Javascript Alpinejs

Alpinejs é um framework Javascript para adicionar comportamento diretamente nas marcaçõoes HTML. Ele se apresenta como sendo o jQuery para a web morderna. E realmente é uma ferramenta muito interessante. Tendo uma baixa curva de aprendizagem é muito bom para a criação de pequenos componentes dentro de uma página.

Para começar devemos incluí-lo em nosso documento.

<script defer src="https://unpkg.com/alpinejs@3.9.0/dist/cdn.min.js"></script>

Também podemos utilizar via npm:

npm install alpinejs
//script.js

import Alpine from 'alpinejs'
 
window.Alpine = Alpine
 
Alpine.start()

O Alpinejs tem como core o state, que são os dados(objeto) javascript que ele observa por mudanças. Definimos o state através da directiva x-data, dentro de uma marcação HTML. Dentro desta marcação podemos acessar e modificar o state do componente.

<div x-data="{count: 0}">
    <button x-text="count" @click="count++"></button>
</div>

No exemplo acima definimos nosso state como um objeto javascript com o atributo count valendo 0. Dentro desta marcação criamos um elemento button e nele incluimos uma directiva do alpinejs chamada x-text, que recebe um valor(tipicamente do state) e o exibe como o valor de texto do elemento. Também utilizamos uma outra directiva do alpinejs o @click que executa o conteúdo passado para ela quando o elemento onde ela esta sofre um evento de click, no caso somamos 1 no atributo do state do componente. Lembrando que o alpinejs fica observando mudanças em seu state, então ao alterar o atributo o texto do button automaticamente será modificado na exibição. Veja o exemplo.

Para componentes mais complexos que não é viável ser colocado diretamente na marcação podemos passar uma função que retorna um objeto para a directiva x-data.

<script>
    window.contador = function(){
        return{
            count: 0,
            incrementar: function(){
                this.count++
            }
        }
    }
</script>

<div x-data="contador()">
    <button x-text="count" @click="incrementar()"></button>
</div>

Directivas do Alpinejs

O Alpinejs oferece diversas directivas para alterar o DOM em um documento.

x-text e x-html

Já vimos a directiva x-text que serve para exibir o valor como valor de texto de uma tag. Ele aceita como valor qualquer expressão javascript.

<div x-data="{title: 'Um titulo'}">
    <h1 x-text="title"></h1>
    <div x-text="1+2"></div>
    <div x-text="title+' da página'"></div>
</div>

No exemplo acima será exibido como um titulo ‘Um titulo’ que é o valor do atributo title, depois dentro do primeiro div o valor 3, que é o resultado da expressão 1+2 e no segundo div o texto ‘Um titulo da página’ que é o resultado da concatenação do atributo title com a string ‘ da página’.

A directiva x-html funciona de forma semelhante, com a diferença que ela altera o innerHTML do elemento.

<div x-data="{tag: '<h1>teste</h1>'}">
    <div x-text="tag"></div>
    <div x-html="tag"></div>
</div>

No exemplo acima o div com o x-text irá escapar as tags html já no div com x-html ele irá interpretar as tags normalmente sendo exibido a palavra teste como um título de nível 1.

x-show e x-if

A directiva x-show é utilizada para mostrar ou ocultar um elemento conforme algum teste. É normalmente utilizado para criar modals, dropdowns, etc.

<div x-data="{open: false}">
    <button @click="open = !open">Expandir</button>
    <div x-show="open">
        Conteúdo a ser exibido ....
    </div>
</div>

Quando o valor de open é true ele irá mostar o div ‘Conteúdo a ser exibido ….’, caso seja false o div ficará oculto. Por baixo dos panos o alpinejs utiliza a propriedade CSS display: none; para ocultar o elemento. Veja um exemplo.

A directiva x-if serve para os mesmos fins que o x-show, mas ao contrário de x-show que simplesmente oculta via CSS, o x-if remove o elemento do DOM. Por este motivo que devemos inseri-lo através de uma marcação template em vez de diretamente no elemento desejado.

<div x-data="{open: false}">
    <button @click="open = !open">Expandir</button>
    <template x-if="open">
        <div>Conteúdo a ser exibido ....</div>
    </template>
</div>

Veja o exemplo.

x-model

A directiva x-model é utilizada para ligar um campo de formulário com um dado do state.

<form x-data="{nome: '',sobrenome:''}">
    <div>
        <label for="nome">Nome: </label>
        <input type="text" name="nome" x-model="nome">
    </div>
    <div>
        <label for="sobrenome">Sobrenome: </label>
        <input type="text" name="sobrenome" x-model="sobrenome">
    </div>

    <div>
        <div>Saída: </div>
        <div>Nome: <span x-text="nome"></span></div>
        <div>Sobrenome: <span x-text="sobrenome"></span></div>
    </div>
</form>

No exemplo acima temos no state os valores nome e sobrenome que ligamos aos campos de formulário input através da directiva x-model. Assim quando o valor do input for alterado também será alterado o valor no atributo associado no state. Veja o exemplo.

Esta ligação é feita nas duas vias se o atributo no state for alterado o valor do campo input também será.

<div x-data="{texto: ''}">
    <div>
        <input type="text" name="texto" x-model="texto">
    </div>
    <button @click="texto +='A'">A</button>
    <button @click="texto +='B'">B</button>
    <button @click="texto +='C'">C</button>
</div>

Acima vemos um campo input ligado ao atributo texto e 3 botões cada um com um evento de click que ao ser executado altera do valor do atributo concatenando uma letra a ele e em consequência o valor do input também. Veja o exemplo.

x-for

Podemos criar elementos no DOM iterando em um lista, para isto utilizamos a directiva x-for. Ela é utilizada dentro de uma marcação template que só deve ter um elemento com filho.

<div x-data="{nomes: ['Rodrigo','Angela','Carlos','Adalberto']}">
    <ul>
        <template x-for="nome in nomes">
            <li x-text="nome"></li>
        </template>
    </ul>
</div>

Veja o exemplo.

x-bind

A directiva x-bind permite que o valor de algum atributo do elemento HTML seja o valor de alguma expressão javascript. Para isto colocamos a directiva x-bind antes do nome do atributo que queremos utilizar, por exemplo: x-bind:placeholder="textoPlaceholder".

Podemos utilizar uma versão curta do x-bind que consiste em somente colocar os : antes do atributo.

<div x-data="{disable: true}">
    <form>
        <input type="text" name="nome" :disabled="disable">
    </form>
    <button @click="disable= false">Habilitar</button>
    <button @click="disable= true">Desabilitar</button>
</div>

No exemplo acima criamos um elemento input com seu atributo disabled ligado com o valor do atributo disable do state. Usamos os botões para mudar o atributo do state disable e em resposta o campo input terá seu atributo alterado habilitado e desabilitando o campo para edição. Veja o exemplo.

A directiva x-bind é muito utilizada para alterar as classes CSS de elementos conforme o state do componente.

<style>
    .red{
        border: 1px solid #F00;
    }
</style>
<div x-data="{nome: ''}">
    <form>
        <input type="text" x-model="nome" :class="nome == '' ? 'red': '' ">
    </form>
    
</div>

Utilizamos a directiva x-bind no atributo class fazendo uso do operador ternário onde se o state nome for vazio então o class terá valor red senão o class ficará vazio. Veja o exemplo.

A directiva x-bind também permite informar em seu valor um objeto javascript onde o nome do atributo é uma classe CSS que queremos utilizar e seu valor é um booleano, quando o valor do atributo é verdadeiro o alpinejs aplica a classe no elemento, se for falso não aplica.

<style>
    .background{
        background: #F00;
    }
    .border{
        border: 2px solid #888;
    }
    .rounded{
        border-radius: 30px;
    }
</style>

<div x-data="{backgroundData: false, borderData: true, roundedData: false}">
    <div style="width:200px;height:200px" :class="{background: backgroundData, border: borderData, rounded: roundedData}">
    </div>
    <div>
        <input type="checkbox" x-model="backgroundData" id="backgroundData">
        <label for="backgroundData">Background</label>
    </div>
    <div>
        <input type="checkbox" x-model="borderData" id="borderData">
        <label for="borderData">Border</label>
    </div>
    <div>
        <input type="checkbox" x-model="roundedData" id="roundedData">
        <label for="roundedData">Bordas Redondas</label>
    </div>
</div>

No exemplo acima criamos um div de 200×200 e associamos as classes CSS: background, border e rounded através do x-bind com os valores do seu state que são booleanos, então ao mudar o valor destes atributos no state(fazemos isso através dos inputs) as classes são aplicas ou não no elemento. Veja o exemplo.

Também temos um formato para utilizar o x-bind com o atributo style, podemos passar para :style um objeto javascript onde os nomes das propriedas do objeto e seus valores são as propriedades e valores do CSS.

<div x-data="{}">
    <div :style="{ color: 'red', display: 'flex' }"></div>
</div>

<!-- Vai renderizar: -->
<div style="color: red; display: flex;" ...>

Eventos

O alpinejs utiliza a directiva x-on: para definir um evento, após os dois pontos colocamos o evento que desejamos manipular, por exemplo, x-on:click, x-on:change, x-on:submit, x-on:keyup, x-on:keydown. Também podemos utilizar uma versão mais curta(inclusive é que utilizamos anteriormente) que consiste em utilizar um @ antes da directiva, por exemplo, @click, @change, @submit, @keyup .

Dentro do valor da directiva de evento inserimos o código que queremos que seja executado quando o evento ocorrer, como se fosse o conteúdo da função passada para o listener. Podemos acessar o objeto do evento através da variável $event.

<div x-data="{nomes: []}">
    <select @change="nomes.push($event.target.value)">
        <option value="Ana">Ana</option>
        <option value="Maria">Maria</option>
        <option value="João">João</option>
        <option value="Fabricio">Fabricio</option>
    </select>
    <ul>
        <template x-for="nome in nomes">
            <li x-text="nome"></li>
        </template>
    </ul>
</div>

No exemplo acima definimos que no evento change iremos pegar do objeto do evento o elemento que sofreu o evento $event.target e pegar o seu value, e depois adicionar ao array nomes do state. Veja o exemplo.

Modificadores de Eventos

O alpinejs fornece diversos modificadores de eventos para customizar seu comportarmento. Entre eles temos.

.prevent: é o equivalente a chamar .preventDefault() dentro de um listener de evento.

<div x-data="{}">
    <form action="/teste" @submit.prevent="alert('não enviado')">
        <input type="text" name="nome">
        <input type="submit" value="Enviar">
    </form>
</div>

Veja o exemplo.

.outside: torna o evento de click para fora do elemento, ou seja, é disparado quando acontece um click fora do elemento que ele esta.

<div x-data="{alvo: ''}">
    <div 
        @click="alvo='click dentro'" 
        @click.outside="alvo='click fora'" 
        x-text="alvo"
        style="width: 200px; height: 200px; background: #CCC"
        ></div>
</div>

Veja o exemplo.

.window: utilizando o modificador .window irá registrar o evento para todo objeto window em vez de somente para o próprio elemento.

<div x-data="{texto: ''}">
    <div @keyup.window="texto += $event.key" x-text="texto"></div>
</div>

No exemplo acima irá registra o evento keyup para o window capturando os eventos de teclado de toda a página. Veja o exemplo.

Os eventos de keyup e keydown possuiem modificadores próprios para facilitar a detecção de algumas teclas especificas.

.shiftTecla Shift
.enterTecla Enter
.spaceTecla de Espaço
.ctrlTecla Control
.cmdTecla Cmd
.metaCmd no Mac, Control no Windows
.altTecla Alt
.up .down .left .rightTeclas de seta para cima, baixo esquerda e direita
.escapeTecka Esc.
.tabTecla Tab
.caps-lockCaps Lock

x-ref

A directiva x-ref é utilizada para acessar elemento do DOM de forma mais fácil sem precisar utilizar os getElementById  ou querySelector. Podemos recupear o elemento utilizando a variável $ref.<valor_da_directiva>

<div x-data="{}">
    <button @click="$refs.texto.remove()">Remover texto</button>

    <span x-ref="texto">Um texto</span>
</div>

Veja o exemplo.

x-init

A directiva x-init é utilizada para executar algo na inicialização do componente, como fazer uma busca em um API por exemplo. Caso o objeto do state tiver um método init ele irá ser executado.

<div x-data="{}" x-init="alert('Inicializando o componente!')">

</div>

<div x-data="{
    init() {
        alert('Inicializando o componente!')
    }
}">

</div>

Veja o exemplo.

$watch

Usamos o método $watch para observar um atributo do state e realizar alguma ação quando ele for modificado. Passamos de parâmetro o nome do atributo que queremos observar e a função a ser executada quando este for alterado.

<script>
    function component(){
        return {
            texto: '',
            init: function(){
                let _this = this;
                this.$watch('texto', function(){
                    if( _this.texto == "Boteco Digital" ){
                        alert("Obrigado por digitar 'Boteco Digital'")
                    }
                })
            }
        };
    }
</script>


<p>Digite "Boteco Digital"</p>
<div x-data="component()">
    <input type="text" x-model="texto"> 
</div>

No exemplo acima como o código ficaria meio grande para ser colocado inline foi criado uma função que retorna o objeto do state. Nela temos o atributo texto que será associado com o input. Também criamos o método init() que será chamado na inicialização do componente, nele chamamos o método $watch(), para observar alterações no atributo texto, quando ela ocorrer a função passada para o $watch será executada comparando o texto com o valor esperado e mostrando um alert se for verdadeiro.

Veja o exemplo.

$dispatch

O método $dispatch é utilizado para enviar um evento, este evento pode ser tratado utilizando a directiva x-on: ou @ .

<div x-data="{}" @notificacao="alert('Evento disparado!!!')">
    <button @click="$dispatch('notificacao')">
        Notificar
    </button>
</div>

Veja o exemplo.

Podemos enviar um evento para um outro componente utilizando o modificar de evento .window que faz a directiva de evento observar os eventos de todo o navegador.

O método $dispatch também permite que seja enviado um objeto junto com o evento, isso é feito passando um objeto javascript como segundo argumento do método, e estará disponível no handler do evento através de $event.detail.

<div x-data="{}">
    <button @click="$dispatch('notificacao',{msg: 'Um evento aconteceu!!!'})">
        Notificar
    </button>
</div>

<div x-data="{mensagem: ''}"  @notificacao.window="mensagem=$event.detail.msg">
    <div x-text="mensagem"></div>
</div>

Veja o exemplo.

Bom pessoal, espero que tenham gostado desta introdução, e como sempre mais detalhes de uma olhada na documentação,

T++