接著上篇,Laracasts,這邊將會展示 livewire 自定義組件和傳遞參數。

生成 livewire parent component

$ php artisan livewire:layout

生成主要 component

$ php artisan make:livewire TestForm

將原本在 route 做的組件注入遷移至 TestForm 當中,由 Form 進行生成:

Route::get('/', TestForm::class);

//App\Livewire\TestForm.php
class TestForm extends Component
{
    public $email; // 放置變化結果

    public function render()
    {
        $input = TextInput::make('email')
            ->label('Email Address');

        return view('livewire.test-form', [
            'input' => $input
        ]);
    }
}

到目前為止會是跟原本實作結果一致,接下來開始進行使 Label 具有 closure 傳遞調整:

$input = TextInput::make('email')
        ->label(function () {
            return Str::random();
        });

到 TextInput field 進行 allow property:

class TextInput implements Htmlable
{
    protected string | \Closure $label;

    public function getLabel():string
    {
        return $this->evaluate($this->label ?? null) ?? str($this->name)->title();
    }

簡單寫 evaluate 呼叫 closure 的方法:

public function evaluate($value)
{
    if ($value instanceof \Closure) {
        return $value(); // call
    }

    return $value;
}

希望 label 會因為我輸入的 input 隨機生成字串,而 text-input.blade.php則需要透過 livewire:model.live 進行實時數據綁定:

<input type="text" wire:model.live="{{ $getName() }}"/>

// App\Components\TextInput.php
public function getName(): string
{
    return $this->name;
}

到此已經完成需求,接下來要使用神奇的 app()->call() 這個可以透過 laravel container 進行呼叫函式並且傳遞指定變數在裡面操作:

public function evaluate($value)
{
if ($value instanceof \Closure) {
// $value = clsoure
    return app()->call($value, [
        'random' => Str::random(),
    ]);
}

然後在前面就可以這樣使用:

$input = TextInput::make('email')
        ->label(function ($random) {
            return $random;
        });
// 非常神奇,這樣在之後定義 closure 參數時可以多加利用:
return app()->call($value, [
            'random' => Str::random(),
            'name' => 'Yish'
        ]);

接下來可以把 livewire 組件也注入到方法內可以調用:

public function livewire(Component $livewire): self
{
    $this->livewire = $livewire;

    return $this;
}

$input = TextInput::make('email')
    ->label(function ($random, $name) {
        return $name;
    })->livewire($this);

public function evaluate($value)
{
    if ($value instanceof \Closure) {
        return app()->call($value, [
            'random' => Str::random(),
            'name' => 'Yish',
            // 取得 livewire component state 這邊是指 TestForm 的 $email
            'state' => $this->livewire->{$this->getName()},
        ]);
    }

$input = TextInput::make('email')
    ->label(function ($random, $name, $state) {
        return $state;
    })->livewire($this);

這樣 label 就會隨著輸入框內容進行 update。