嵌套元件(Nesting Components),在 Vue.js 中是很普遍的用法,可以在父元件賦予數值給子元件進而改變子元件顯示的資料。實作中通常會將可重複的內容與主畫面分成父子頁面,讓程式碼看起來更簡潔更好維護。但在 Livewire 卻有些使用上的限制:
子元件雖然可以接收來自父元件的參數,但不能像 Vue 一樣即時渲染子元件的內容。
即:透過在元件元素傳值的方式只能渲染第一次的值
首先是父元件
<?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 Hooks 的 updatingFoo
在 $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
事件,並導向本地的函式。
在上面的父元件我們會 $emit
到 updateMenu
這個名稱,並對應到本地的 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;
}
}