JS整合

AlpineJS

在許多情況下,頁面交互並不保證能對伺服器進行完整的請求流程,比如開啟或關閉一個 Modal 視圖

在這種情況下, AlpineJS 是 Livewire 最完美的夥伴

它使您可以以聲明式/反應式的方式將JavaScript直接添加到你的網頁標記中,就如同Vue.js那樣。(看起來Livewire 真的很想讓自己變成PHP版本的Vue.js)

安裝

你如果想要在 Livewire 裡頭使用 Alpine 就必須安裝它

要安裝 Alpine,請加入以下 script 標籤到視圖檔的 區塊

<head>
    ...
    <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.8.0/dist/alpine.min.js" defer></script>
    <!-- The "defer" attribute is important to make sure Alpine waits for Livewire to load first. -->
</head>

如需了解更多的安裝資訊,請參考 Alpine 的文件

在Livewire 裡頭使用 Alpine

這是一個在 Livewire 組件視圖檔案使用 AlpineJS 做出下拉選單功能的例子

<div>
    ...

    <div x-data="{ open: false }">
        <button @click="open = true">Show More...</button>

        <ul x-show="open" @click.away="open = false">
            <li><button wire:click="archive">Archive</button></li>
            <li><button wire:click="delete">Delete</button></li>
        </ul>
    </div>
</div>

萃取出可重複使用的 Blade 組件

假如你還不熟悉 Livewire 和 AlpineJS,那麼在程式碼中混合這兩者的語法可能會造成你混淆

因此有一個策略可以使用的是,將 Alpine 語法的部分包成可重複使用的 Blade 組件,然後將它們用在 Livewire 或者是應用當中

以下是一個例子,用的是 Laravel 7 Blade 組件標籤指令

Livewire 視圖:

<div>
    ...

    <x-dropdown>
        <x-slot name="trigger">
            <button>Show More...</button>
        </x-slot>

        <ul>
            <li><button wire:click="archive">Archive</button></li>
            <li><button wire:click="delete">Delete</button></li>
        </ul>
    </x-dropdown>
</div>

可重複使用的 Blade "dropdown" 組件:

<div x-data="{ open: false }">
    <span @click="open = true">{{ $trigger }}</span>

    <div x-show="open" @click.away="open = false">
        {{ $slot }}
    </div>
</div>

現在,Livewire 和 Alpine 的語法已經完成分離了,而且你已經有了一個可重複使用的 Blade 組件來用在其他 Livewire 組件

從 Alpine 來與 Livewire 進行互動: $wire

從任何 Livewire 組件裡頭的 Alpine 組件,你能夠取用魔法 $wire 物件來取用甚至是控制 Livewire 組件

為了示範該如何使用,我們將在 Alpine 創造一個 "counter" 組件,並用它來透過 hood 來控制 Livewire

class Counter extends Component
{
    public $count = 0;

    public function increment()
    {
        $this->count++;
    }
}
<div>
    <!-- Alpine Counter Component -->
    <div x-data>
        <h1 x-text="$wire.count"></h1>

        <button x-on:click="$wire.increment()">Increment</button>
    </div>
</div>

現在當使用者按下 "Increment" 按鈕,一個標準的 Livewire 流程將會觸發,而且 Alpine 將會呈現 Livewire 的新 $count 值

因為 $wire 在 hood 裡頭使用了 JavaScript 代理,因此你能夠取用 Livewire 的屬性以及方法,而且這些操作都將傳給 Livewire。除了這些功能之外,$wire 也提供你一些內建方法讓你使用

以下是 $wire 完整的 API

// 取得 Livewire 的屬性 $wire.foo

// 呼叫 Livewire 的方法 $wire.someMethod(someParam)

// 呼叫 Livewire 的方法,並用其回傳值來做事情 $wire.someMethod(someParam) .then(result => { ... })

// 呼叫 Livewire 的方法,並用 async/await(異步的方式) 來儲存其回應 let foo = await $wire.getFoo()

// 發出一個名為 "some-event" 的 Livewire 事件並傳入兩個參數 $wire.emit('some-event', 'foo', 'bar')

// 偵聽一個名為 "some-event" 的 Livewire 事件 $wire.on('some-event', (foo, bar) => {})

// 取得 Livewire 屬性 $wire.get('property')

// 設定 Livewire 屬性為某個指定值 $wire.set('property', value)

// 呼叫 Livewire 的行動(方法) $wire.call('someMethod', param)

// 取得 Livewire 組件底層的 JavaScript 實例 $wire.__instance

在 Livewire 和 Alpine 之間分享狀態 :@entangle

Livewire 有一個極為強大的功能稱為 "entangle" 允許你把 Livewire 和 Alpine 屬性綁定在一起。透過綁定,當一個值改變,被綁定的另一個值也跟著改變

為了示範,同樣使用之前的 dropdown 範例,但這次 show 屬性將在 livewire 和 Alpine 之間綁定在一起,我們現在可以在兩者之間控制 dropdown 的狀態

class Dropdown extends Component
{
    public $showDropdown = false;

    public function archive()
    {
        ...
        $this->showDropdown = false;
    }

    public function delete()
    {
        ...
        $this->showDropdown = false;
    }
}
<div x-data="{ open: @entangle('showDropdown') }">
    <button @click="open = true">Show More...</button>

    <ul x-show="open" @click.away="open = false">
        <li><button wire:click="archive">Archive</button></li>
        <li><button wire:click="delete">Delete</button></li>
    </ul>
</div>

現在使用者可透過 Alpine 開啟 dropdown,但當他按下一個 Livewire 行動,比如 "Archive" 將會告知 Livewire 去關閉 dropdown。不管是 Alpine 又或者是 Livewire 都能夠控制其對應的屬性,那與之對應的屬性也會跟著變更

有時,不必在每次 Alpine 更改時都更新Livewire,而你希望將更改與下一個發出的 Livewire 請求捆綁在一起。在這些情況下,你可以像這樣鏈接 .defer 屬性

<div x-data="{ open: @entangle('showDropdown').defer }">
    ...

現在,當使用者切換下拉菜單的打開和關閉狀態時,將不會對 Livewire 發送 AJAX 請求,但是當通過 “archive” 或 “delete” 之類的按鈕觸發 Livewire 操作時,“showDropdown” 的新狀態將變為與請求捆綁在一起

假如你分不清兩者的差異,開啟瀏覽器的開發者工具,從 XHR 請求來觀察 .defer 有沒有加的差異

從 Blade 組件取用 Livewire 語句

在 Livewire 應用中提取可重複使用的 Blade 組件是一種基本模式

在 Livewire 上下文中實現 Blade 組件時,你可能會遇到的一個困難是從組件內部訪問屬性值,例如 wire:model

例如,你可能創造了一個文字輸入項 Blade 組件,像這樣:

使用
<x-inputs.text wire:model="foo"/>

定義
<div>
    <input type="text" {{ $attributes }}>
</div>

像這樣的簡單 Blade 組件可以運作良好, Laravel 和 Blade 將自動轉發添加到組件的任何其他屬性(在這種情況下,如wire:model),並將它們放置在 標籤上,因為我們回應了屬性包($ attributes)

但是,有時你可能需要提取有關傳遞給組件的 Livewire 屬性之更多詳細資料

針對這種情況, Livewire 提供了一個 $attributes->wire() 來幫助你搞定這些工作

比如下面的例子:

<x-inputs.text wire:model.defer="foo" wire:loading.class="opacity-25"/>

你能夠像這樣從 Blade 的 $attribute 屬性包取得 Livewire 語句訊息

$attributes->wire('model')->value(); // "foo"
$attributes->wire('model')->modifiers(); // ["defer"]
$attributes->wire('model')->hasModifier('defer'); // true


$attributes->wire('loading')->hasModifier('class'); // true
$attributes->wire('loading')->value(); // "opacity-25"

你也能夠個別的轉發這些 Livewire 語句,例如:

<x-inputs.text wire:model.defer="foo" wire:loading.class="opacity-25"/>

你能夠像這樣去轉發 "wire:model.defer="foo" 語句
<input type="text" {{ $attributes->wire('model') }}>

輸出
<input type="text" wire:model.defer="foo">

使用此工具的方式有很多,但是一種常見的例子是將其與上述的 @entangle 指令結合在一起使用

<x-dropdown wire:model="show">
    <x-slot name="trigger">
        <button>Show</button>
    </x-slot>

    Dropdown Contents
</x-dropdown>
定義
<div x-data="{ open: @entangle($attributes->wire('model')) }">
    <span @click="open = true">{{ $trigger }}</span>

    <div x-show="open" @click.away="open = false">
        {{ $slot }}
    </div>
</div>

注意

假如 .defer 修飾子是透過 wire:model.defer 來傳入, @entangle 語句將自動進行分析並在 hood 時加入 @entangle('...').defer 修飾子

建立一個 Datepicker 組件

Livewire 裡頭的 JavaScript 的常見應用是自定義表單輸入,比如日期選擇器,顏色選擇器等之類的組件對於你的應用通常是必不可少的

通過使用與上面相同的模式(並添加一些額外的小變化),我們可以利用 Alpine 輕鬆地與這些類型的 JavaScript 組件進行交互

讓我們創建一個稱為 date-picker 的可重複使用的 Blade 組件,我們可以透過它使用 wire:model 將某些數據綁定到 Livewire 中

示範你可以如何使用它:

<form wire:submit.prevent="schedule">
    <label for="title">Event Title</label>
    <input wire:model="title" id="title" type="text">

    <label for="date">Event Date</label>
    <x-date-picker wire:model="date" id="date"/>

    <button>Schedule Event</button>
</form>

對於此組件,我們將使用 Pikaday 函式庫

根據文件,該套件包的最基本用法如下所示範:

<input type="text" id="datepicker">

<script>
    new Pikaday({ field: document.getElementById('datepicker') })
</script>

你只需要加入一個元素, Pikaday 將為你添加所有額外的日期選擇器行為

現在,讓我們看看如何利用這個函式庫來編寫可重複使用的 Blade 組件

可重複使用的日期選擇器 Blade 組件:

<input
    x-data
    x-ref="input"
    x-init="new Pikaday({ field: $refs.input })"
    type="text"
    {{ $attributes }}
>

注意:

表達式是 Laravel 7 及更高版本中的一種機制,用於轉發在組件標籤上聲明的額外HTML屬性

轉發 wire:model 輸入事件

Let's create a contrived example where when a user clicks the first button a property called $foo is set to bar, and when a user clicks the second button, $foo is set to baz.

在 hood 裡頭, wire:model 每次派發輸入事件在元素上或元素下時,都會添加一個事件偵聽器以更新屬性。在 Livewire 和 Alpine 之間進行通信的另一種方法是使用 Alpine 派發一個輸入事件,該事件具有在其上具有 wire:model 的元素內或元素上的一些數據

請看這個例子,當使用者按下第一個按鈕時,名為 $foo 的屬性設置為bar,而當使用者按下第二個按鈕時,$foo 設置為baz。

Livewire 組件視圖:

<div>
    <div wire:model="foo">
        <button x-data @click="$dispatch('input', 'bar')">Set to "bar"</button>
        <button x-data @click="$dispatch('input', 'baz')">Set to "baz"</button>
    </div>
</div>

一個更常見的例子是創建一個"顏色選擇器" Blade 組件,該組件可能會在 Livewire 組件中使用

選色器組件用法:

<div>
    <x-color-picker wire:model="color"/>
</div>

為了定義組件,我們將使用名為 Vanilla Picker 的第三方顏色選擇器函式庫

本範例假定你已將該函式庫加載到頁面上

選色器 Blade組件定義:

<div
    x-data="{ color: '#ffffff' }"
    x-init="
        picker = new Picker($refs.button);
        picker.onDone = rawColor => {
            color = rawColor.hex;
            $dispatch('input', color)
        }
    "
    wire:ignore
    {{ $attributes }}
>
    <span x-text="color" :style="`background: ${color}`"></span>
    <button x-ref="button">Change</button>
</div>
Color-picker Blade Component Definition (Commented):

<div
    x-data="{ color: '#ffffff' }"
    x-init="
        // Wire up to show the picker when clicking the 'Change' button.
        picker = new Picker($refs.button);
        // Run this callback every time a new color is picked.
        picker.onDone = rawColor => {
            // Set the Alpine 'color' property.
            color = rawColor.hex;
            // Dispatch the color property for 'wire:model' to pick up.
            $dispatch('input', color)
        }
    "
    // Vanilla Picker will attach its own DOM inside this element, so we need to
    // add `wire:ignore` to tell Livewire to skip DOM-diffing for it.
    wire:ignore
    // Forward the any attributes added to the component tag like `wire:model=color`
    {{ $attributes }}
>
    <!-- Show the current color value with the backgound color set to the chosen color. -->
    <span x-text="color" :style="`background: ${color}`"></span>
    <!-- When this button is clicked, the color-picker dialogue is shown. -->
    <button x-ref="button">Change</button>
</div>

忽略 DOM-改動(使用 wire:ignore)

幸運的是,像 Pikaday 這樣的函式庫在頁面末尾添加了其額外的DOM。許多其他函式庫在初始化DOM後就立即對其進行操作,並在與它們交互時繼續使DOM發生變化

發生這種情況時, Livewire 很難跟踪要在組件更新中保留哪些DOM操作以及要丟棄哪些DOM操作

要告訴 Livewire 忽略組件中 HTML 子集的更改,可以添加 wire:ignore 指令

Select2 是接管一部分DOM的函式庫之一(它將

這是一個在 Livewire 組件中使用 Select2 函式庫的範例,以說明 wire:ignore 的用法

<div>
    <div wire:ignore>
        <select class="select2" name="state">
            <option value="AL">Alabama</option>
            <option value="WY">Wyoming</option>
        </select>

        <!-- Select2 will insert its DOM here. -->
    </div>
</div>

@push('scripts')
<script>
    $(document).ready(function() {
        $('.select2').select2();
    });
</script>
@endpush

注意:

有時忽略元素本身的更改(而不是其子元素)會很有用。在這種情況下,你可以將 self 修飾子添加到 wire:ignore 指令,如下所示: wire:ignore.self

Laravel Echo

Livewire 與 Laravel Echo 的完美搭配,可使用 WebSocket 在你的網頁上提供實時功能

此功能假定你已經安裝了 Laravel Echo ,並且 window.Echo 物件為全域。有關更多資訊請查看文檔

考慮以下Laravel事件:

class OrderShipped implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function broadcastOn()
    {
        return new Channel('orders');
    }
}

比如你觸發 Laravel 廣播系統的事件:

event(new OrderShipped);

一般來說,你會像這樣來偵聽 Laravel Echo 的事件:

Echo.channel('orders')
        .listen('OrderShipped', (e) => {
            console.log(e.order.name);
        });

但是使用 Livewire ,你要做的就只是在 $listeners 屬性中註冊它,並使用一些特殊的語法來指定它源自於Echo

class OrderTracker extends Component
{
    public $showNewOrderNotification = false;

    // Special Syntax: ['echo:{channel},{event}' => '{method}']
    protected $listeners = ['echo:orders,OrderShipped' => 'notifyNewOrder'];

    public function notifyNewOrder()
    {
        $this->showNewOrderNotification = true;
    }
}

現在 Livewire 將攔截從 Pusher 收到的事件,並採取相應的措施。以類似的方式,你還可以收聽廣播到私人/狀態頻道的事件

請確保正確定義了身份驗證的回呼

class OrderTracker extends Component
{
    public $showNewOrderNotification = false;
    public $orderId;

    public function mount($orderId)
    {
        $this->orderId = $orderId;
    }

    public function getListeners()
    {
        return [
            "echo-private:orders.{$this->orderId},OrderShipped" => 'notifyNewOrder',
            // Or:
            "echo-presence:orders.{$this->orderId},OrderShipped" => 'notifyNewOrder',
        ];
    }

    public function notifyNewOrder()
    {
        $this->showNewOrderNotification = true;
    }
}

Inline Scripts

Livewire 建議你將 AlpineJS 用於大多數 JavaScript 需求,但它確實支持直接在你的組件視圖裡頭使用

Copyright © Goblin Lab Studio 2021 all right reserved,powered by Gitbook該文件修訂時間: 2021-02-22 10:28:17

results matching ""

    No results matching ""