Saltearse al contenido

Alpine

AlpineJS es una biblioteca JavaScript ligera que facilita la adición de interactividad del lado del cliente a sus páginas web. Fue construida originalmente para complementar herramientas como Livewire donde una utilidad más centrada en JavaScript es útil para salpicar interactividad alrededor de su aplicación.

Livewire viene con Alpine de fábrica, por lo que no hay necesidad de instalarlo en su proyecto por separado.

El mejor lugar para aprender a utilizar AlpineJS es la documentación de Alpine.

Un componente Alpine básico

Para sentar las bases del resto de esta documentación, he aquí uno de los ejemplos más sencillos e informativos de un componente Alpine. Un pequeño «contador» que muestra un número en la página y permite al usuario incrementar ese número pulsando un botón:

<!-- Declarar un objeto JavaScript de datos... -->
<div x-data="{ count: 0 }">
<!-- Renderizar el valor "count" actual dentro de un elemento... -->
<h2 x-text="count"></h2>
<!-- Incrementa el valor de «count» en «1» cuando se produce un evento de click... -->
<button x-on:click="count++">+</button>
</div>

El componente Alpine anterior puede ser utilizado dentro de cualquier componente Livewire en su aplicación sin ningún problema. Livewire se encarga de mantener el estado de Alpine a través de las actualizaciones del componente Livewire. En esencia, usted debe sentirse libre de usar componentes Alpine dentro de Livewire como si estuviera usando Alpine en cualquier otro contexto que no sea Livewire.

Uso de Alpine dentro de Livewire

Exploremos un ejemplo más real del uso de un componente Alpine dentro de un componente Livewire.

Abajo se muestra un simple componente Livewire mostrando los detalles de un modelo de post desde la base de datos. Por defecto, sólo se muestra el título del post:

<div>
<h1>{{ $post->title }}</h1>
<div x-data="{ expanded: false }">
<button type="button" x-on:click="expanded = ! expanded">
<span x-show="! expanded">Show post content...</span>
<span x-show="expanded">Hide post content...</span>
</button>
<div x-show="expanded">
{{ $post->content }}
</div>
</div>
</div>

Utilizando Alpine, podemos ocultar el contenido del post hasta que el usuario pulse el botón “Show post content…”. En ese momento, la propiedad expanded de Alpine se establecerá en true y el contenido se mostrará en la página porque x-show="expanded" se utiliza para dar a Alpine el control sobre la visibilidad del contenido del post.

Este es un ejemplo de donde Alpine brilla: agregando interactividad en su aplicación sin disparar viajes de ida y vuelta al servidor Livewire.

Controlando Livewire desde Alpine usando $wire

Una de las características más poderosas disponibles para usted como desarrollador de Livewire es $wire. El objeto $wire es un objeto mágico disponible para todos sus componentes Alpine que son usados dentro de Livewire.

Puede pensar en $wire como una puerta de entrada de JavaScript a PHP. Le permite acceder y modificar propiedades de componentes Livewire, llamar a métodos de componentes Livewire, y hacer mucho más; todo desde dentro de AlpineJS.

Acceso a las propiedades de Livewire

He aquí un ejemplo de una sencilla utilidad de «recuento de caracteres» en un formulario para crear un post. Esto mostrará instantáneamente al usuario cuántos caracteres hay dentro del contenido de su entrada mientras escribe:

<form wire:submit="save">
<!-- ... -->
<input wire:model="content" type="text">
<small>
Character count: <span x-text="$wire.content.length"></span>
</small>
<button type="submit">Save</button>
</form>

Como puede ver x-text en el ejemplo anterior se está utilizando para permitir a Alpine controlar el contenido de texto del elemento <span>. x-text acepta cualquier expresión JavaScript dentro de él y reacciona automáticamente cuando se actualiza cualquier dependencia. Debido a que estamos utilizando $wire.content para acceder al valor de $content, Alpine actualizará automáticamente el contenido del texto cada vez que $wire.content sea actualizado desde Livewire; en este caso por wire:model="content".

Mutando propiedades Livewire

A continuación se muestra un ejemplo de uso de $wire dentro de Alpine para borrar el campo “title” de un formulario de creación de un post.

<form wire:submit="save">
<input wire:model="title" type="text">
<button type="button" x-on:click="$wire.title = ''">Clear</button>
<!-- ... -->
<button type="submit">Save</button>
</form>

Mientras un usuario está rellenando el formulario Livewire anterior, puede pulsar “Clear” y el campo del título se borrará sin enviar una solicitud de red desde Livewire. La interacción será “instantánea”.

He aquí una breve explicación de lo que ocurre para que esto suceda:

  • x-on:click indica a Alpine que espere a que se haga clic en el elemento botón.
  • Cuando se hace clic, Alpine ejecuta la expresión JS proporcionada: $wire.title = ''
  • Debido a que $wire es un objeto mágico que representa el componente Livewire, todas las propiedades de su componente pueden ser accedidas o mutadas directamente desde JavaScript
  • $wire.title = '' establece el valor de $title en su componente Livewire a una cadena vacía
  • Cualquier utilidad Livewire como wire:model reaccionará instantáneamente a este cambio, todo sin enviar un viaje de ida y vuelta al servidor
  • En la siguiente petición de red Livewire, la propiedad $title se actualizará a una cadena vacía en el backend

Llamada a métodos Livewire

Alpine también puede llamar fácilmente a cualquier método/acción de Livewire simplemente llamándolos directamente en $wire.

Este es un ejemplo del uso de Alpine para escuchar un evento «blur» en una entrada y disparar un guardado de formulario. El evento «blur» es enviado por el navegador cuando un usuario presiona «tab» para quitar el foco del elemento actual y enfocarse en el siguiente en la página:

<form wire:submit="save">
<input wire:model="title" type="text" x-on:blur="$wire.save()">
<!-- ... -->
<button type="submit">Save</button>
</form>

Típicamente, usted sólo usaría wire:model.blur="title" en esta situación, sin embargo, es útil para propósitos de demostración cómo usted puede lograr esto usando Alpine.

Pasando parámetros

También puede pasar parámetros a los métodos Livewire simplemente pasándolos a la llamada al método $wire.

Considere un componente con un método deletePost() como este:

public function deletePost($postId)
{
$post = Post::find($postId);
// Autorizar usuario puede borrar...
auth()->user()->can('update', $post);
$post->delete();
}

Ahora, puede pasar un parámetro $postId al método deletePost() de Alpine de la siguiente manera:

<button type="button" x-on:click="$wire.deletePost(1)">

En general, algo como un $postId se generaría en Blade. He aquí un ejemplo de uso de Blade para determinar qué $postId pasa Alpine a deletePost():

@foreach ($posts as $post)
<button type="button" x-on:click="$wire.deletePost({{ $post->id }})">
Delete "{{ $post->title }}"
</button>
@endforeach

Si hay tres entradas en la página, la plantilla Blade de arriba mostrará algo como lo siguiente en el navegador:

<button type="button" x-on:click="$wire.deletePost(1)">
Delete "The power of walking"
</button>
<button type="button" x-on:click="$wire.deletePost(2)">
Delete "How to record a song"
</button>
<button type="button" x-on:click="$wire.deletePost(3)">
Delete "Teach what you learn"
</button>

Como puedes ver, hemos utilizado Blade para renderizar diferentes IDs de post en las expresiones x-on:click de Alpine.

Parámetros Blade “gotchas”

Esta es una técnica extremadamente poderosa, pero puede ser confusa al leer tus plantillas Blade. Puede ser difícil saber qué partes son Blade y qué partes son Alpine a primera vista. Por lo tanto, es útil inspeccionar el HTML renderizado en la página para asegurarse de que lo que espera que se renderice es correcto.

He aquí un ejemplo que suele confundir a la gente:

Digamos que, en lugar de un ID, su modelo Post utiliza UUIDs para los índices (los IDs son números enteros, y los UUIDs son largas cadenas de caracteres).

Si renderizamos lo siguiente igual que hicimos con un ID habrá un problema:

<!-- Advertencia: este es un ejemplo de código problemático... -->
<button type="button" x-on:click="$wire.deletePost({{ $post->uuid }})"></button>

La plantilla Blade anterior mostrará lo siguiente en su HTML:

<!-- Advertencia: este es un ejemplo de código problemático... -->
<button
type="button"
x-on:click="$wire.deletePost(93c7b04c-c9a4-4524-aa7d-39196011b81a)"
></button>

¿Nota la falta de comillas alrededor de la cadena UUID? Cuando Alpine evalúe esta expresión, JavaScript arrojará un error: «Uncaught SyntaxError: Invalid or unexpected token».

Para solucionar esto, tenemos que añadir comillas alrededor de la expresión Blade de la siguiente manera:

<button
type="button"
x-on:click="$wire.deletePost('{{ $post->uuid }}')"
></button>

Ahora la plantilla anterior se mostrará correctamente y todo funcionará como se espera:

<button
type="button"
x-on:click="$wire.deletePost('93c7b04c-c9a4-4524-aa7d-39196011b81a')"
></button>

Actualizar un componente

Puede refrescar fácilmente un componente Livewire (desencadenar la ida y vuelta de la red para volver a renderizar la vista Blade de un componente) utilizando $wire.$refresh():

<button type="button" x-on:click="$wire.$refresh()"></button>

Compartiendo estado usando $wire.entangle

En la mayoría de los casos, $wire es todo lo que necesita para interactuar con el estado de Livewire desde Alpine. Sin embargo, Livewire proporciona una utilidad adicional $wire.entangle() que puede utilizarse para mantener los valores de Livewire sincronizados con los valores de Alpine.

Para demostrarlo, considere este ejemplo desplegable con su propiedad showDropdown entrelazada entre Livewire y Alpine usando $wire.entangle(). Usando el entrelazamiento, ahora somos capaces de controlar el estado del desplegable tanto desde Alpine como desde Livewire:

use Livewire\Component;
class PostDropdown extends Component
{
public $showDropdown = false;
public function archive()
{
// ...
$this->showDropdown = false;
}
public function delete()
{
// ...
$this->showDropdown = false;
}
}
<div x-data="{ open: $wire.entangle('showDropdown') }">
<button x-on:click="open = true">Show More...</button>
<ul x-show="open" x-on:click.outside="open = false">
<li><button wire:click="archive">Archive</button></li>
<li><button wire:click="delete">Delete</button></li>
</ul>
</div>

Ahora un usuario puede activar el desplegable inmediatamente con Alpine, pero cuando haga clic en una acción de Livewire como «Archivar», se le indicará al desplegable que se cierre desde Livewire. Tanto Alpine como Livewire pueden manipular sus respectivas propiedades, y la otra se actualizará automáticamente.

Por defecto, la actualización del estado es diferida (cambia en el cliente, pero no inmediatamente en el servidor) hasta la siguiente petición de Livewire. Si necesita actualizar el estado del lado del servidor tan pronto como el usuario haga clic, encadene el modificador .live así:

<div x-data="{ open: $wire.entangle('showDropdown').live }">...</div>

Integrar manualmente Alpine en su compilación JavaScript

Por defecto, el JavaScript de Livewire y Alpine se inyecta en cada página de Livewire automáticamente.

Esto es ideal para configuraciones más simples, sin embargo, es posible que desee incluir sus propios componentes, tiendas y plugins de Alpine en su proyecto.

Para incluir Livewire y Alpine a través de su propio paquete JavaScript en una página es sencillo.

En primer lugar, debe incluir la directiva @livewireScriptConfig en su archivo de diseño de la siguiente manera:

<html>
<head>
<!-- ... -->
@livewireStyles @vite(['resources/js/app.js'])
</head>
<body>
{{ $slot }} @livewireScriptConfig
</body>
</html>

Esto permite a Livewire proporcionar a tu bundle cierta configuración que necesita para que tu app funcione correctamente.

Ahora puedes importar Livewire y Alpine en tu archivo resources/js/app.js así:

import {
Livewire,
Alpine,
} from "../../vendor/livewire/livewire/dist/livewire.esm";
// Registre aquí cualquier directiva, componente o complemento de Alpine...
Livewire.start();

A continuación se muestra un ejemplo de registro de una directiva Alpine personalizada llamada “x-clipboard” en su aplicación:

import {
Livewire,
Alpine,
} from "../../vendor/livewire/livewire/dist/livewire.esm";
Alpine.directive("clipboard", (el) => {
let text = el.textContent;
el.addEventListener("click", () => {
navigator.clipboard.writeText(text);
});
});
Livewire.start();

Ahora la directiva x-clipboard estará disponible para todos sus componentes Alpine en su aplicación Livewire.