iT邦幫忙

2021 iThome 鐵人賽

DAY 10
0
Modern Web

Laravel Livewire:不用 Vue 跟 jQuery 居然也能讓 Laravel 的畫面動起來 ?!系列 第 10

Day 10 | 嵌套元件(一)

嵌套元件(Nesting Components),在 Vue.js 中是很普遍的用法,可以在父元件賦予數值給子元件進而改變子元件顯示的資料。實作中通常會將可重複的內容與主畫面分成父子頁面,讓程式碼看起來更簡潔更好維護。但在 Livewire 卻有些使用上的限制:

子元件雖然可以接收來自父元件的參數,但不能像 Vue 一樣即時渲染子元件的內容。
即:透過在元件元素傳值的方式只能渲染第一次的值

以下用實際範例來演示:

DEMO網址請點我

首先是父元件

<?php

namespace App\Http\Livewire\Example;

use Livewire\Component;

class Day10 extends Component
{
    public $menu_type;

    public function render()
    {
        return view('livewire.example.day10');
    }
}

<div class="text-center">
    <h2>Day10: 嵌套元件</h2>
    <div class="mb-5">
        <button class="ui button" wire:click="$set('menu_type', '茶')">茶</button>
        <button class="ui button" wire:click="$set('menu_type', '果汁')">果汁</button>
        <button class="ui button" wire:click="$set('menu_type', '咖啡')">咖啡</button>
    </div>
    <h5>目前選的是{{ $menu_type }}</h5>
    @if($menu_type)
        @livewire('example.day10-card', ['menu' => $menu_type])
    @endif
</div>

以及子元件

<?php

namespace App\Http\Livewire\Example;

use Livewire\Component;

class Day10Card extends Component
{
    public $menu;
    
    public function render()
    {
        return view('livewire.example.day10-card');
    }
}

<div class="flex justify-center">
    <div class="rounded shadow-md bg-grey-50 border-2 w-96 p-4">
        <h1>{{ $menu }}的菜單</h1>
        <p>...</p>
    </div>
</div>

在這個範例中,我們設想了一個 電子菜單 的功能,點擊上方不同的類別,底下的菜單就會跟著改變。這裡我們透過按鈕wire:click()與之前提到的魔術方法$set()直接去修改 $menu_type 的值,並在$menu_type有值後顯示底下的子元件day10-card

如果是照著 Vue.js 的運行方式,當我們每次更改 $menu_type 的同時,也會對子元件所傳入的 $menu 一起做更改,但演示結果並不然。如頁面顯示,我們在父元件能清楚看到 $menu_type 有確實的更改,但子元件上印出的卻不為所動。


解決方法

簡單的解決方式則可以在父元件中透過 Lifecycle HooksupdatingFoo$menu_type 有改變時透過 $emit 傳遞最新的數據給子元件中負責更新 $menu 的函式,如此一來不用動到前端的程式碼也能在後端進行資料的同步。

父元件:

由於只要在 $menu_type 有更改時才需要動作,這邊就可以用 updatedMenuType 來限縮畫面更新時的事件。而 $emit 因為是 全畫面的Livewire元件 都會收到,為了避免誤傳到其他元件中同名的監聽函式,所以這裡保險一點使用了 $emitTo 並帶上 目標元件函式名數值

<?php

namespace App\Http\Livewire\Example;

use Livewire\Component;

class Day10 extends Component
{
    public $menu_type;

    public function render()
    {
        return view('livewire.example.day10');
    }

    /*
        加上 Lifecycle Hooks 的 updated
    */
    public function updatedMenuType($value)
    {
        $this->emitTo('example.day10-card', 'updateMenu', $value);
    }
        
}

子元件:

在子元件的部分 必須 建立一個 $listeners 去監聽 $emit 事件,並導向本地的函式。
在上面的父元件我們會$emitupdateMenu 這個名稱,並對應到本地的 setMenu() 來改變子元件中 $menu 的值,進而改變頁面上的顯示。

<?php

namespace App\Http\Livewire\Example;

use Livewire\Component;

class Day10Card extends Component
{
    public $menu;

    public function render()
    {
        return view('livewire.example.day10-card');
    }

    /*
        加上 updateMenu 事件的監聽,與對應的更新函式
    */

    protected $listeners = ['updateMenu' => 'setMenu'];
    
    public function setMenu($value)
    {
        $this->menu = $value;
    }
}


上一篇
Day 09 | Livewire 生命週期勾 Lifecycle Hooks
下一篇
Day 11 | 嵌套元件(二)
系列文
Laravel Livewire:不用 Vue 跟 jQuery 居然也能讓 Laravel 的畫面動起來 ?!34

尚未有邦友留言

立即登入留言