Livewire 親子コンポーネントについて、親のプロパティを更新するには?Wireable編

ショコラ
ショコラ

Livewire 親子コンポーネントについて、親のプロパティを更新するには?Wireable編

子コンポーネントを更新した時に、子コンポーネントからイベントを発行して(emit)、親コンポーネントのプロパティを更新しようと思います。
Wireableインターフェースを

もっさん先輩
もっさん先輩

手順

Laravel と Livewire をインストールして、ライブワイヤーの親子のコンポーネントを作成してみます。

  1. プロジェクト名(wireble)を決めて以下のコマンドを実行します。
curl -s https://laravel.build/wireble | bash

インストール時にプロジェクト名のディレクトリが作成されます。

  1. インストールの最後に sudo でパスワードの入力を求められます。

↓下のメッセージが表示されてインストールは終わります。

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Get started with: cd wireble && ./vendor/bin/sail up
  1. sail のエイリアスを定義します。
echo "alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail'" >> ~/.bashrc
source ~/.bashrc

Laravel のインストールはここまで。

  1. 「sail up」でコンテナを起動します。
cd wireble && sail up -d
  1. ララベルのトップディレクトリで、Livewireパッケージ をインストールします。
sail composer require livewire/livewire
  1. ライブワイヤーをテストするビューを作成します。resources/views/livewire.blade.php
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <livewire:styles />
  </head>
  <body>
    <livewire:parent-component />
    <livewire:scripts />
  </body>
</html>
  1. routes/web.php にルートを定義します。
Route::get('/',fn() => view('livewire'));

これでブラウザでアクセスできるようになりました。

  1. データ転送オブジェクトを作成します。。app/Http/Livewire/Settings.php
<?php
namespace App\Http\Livewire;
class Settings implements \Livewire\Wireable
{
  public function __construct(public $value1, public $value2){}
  public function toLivewire() {
    return ['value1'=>$this->value1,'value2'=>$this->value2];
  }
  public static function fromLivewire($value) {
    return new static(...$value);
  }
}
  1. ライブワイヤーの親コンポーネントを作成します。app/Http/Livewire/ParentComponent.php
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class ParentComponent extends Component
{
  public Settings $settings;
  protected $listeners = ['update'=>'onUpdateEvent'];
  public function onUpdateEvent( $settings ) {
    \Log::debug(__METHOD__);
    $this->settings = Settings::fromLivewire($settings);
  }
  public function boot()                  { \Log::debug(__METHOD__); }
  public function booted()                { \Log::debug(__METHOD__); }
  public function mount() {
    \Log::debug(__METHOD__);
    $this->settings = new Settings('もっさん','ショコラ');
  }
  public function dehydrate()             { \Log::debug(__METHOD__); }
  public function dehydrateFoo($value)    { \Log::debug(__METHOD__); }
  public function hydrateFoo($value)      { \Log::debug(__METHOD__); }
  public function hydrate()               { \Log::debug(__METHOD__); }
  public function updating($name,$value)  { \Log::debug(__METHOD__); }
  public function updated($name,$value)   { \Log::debug(__METHOD__); }
  public function updatingFoo($value)     { \Log::debug(__METHOD__); }
  public function updatedFoo($value)      { \Log::debug(__METHOD__); }
  public function updatingFooBar($value)  { \Log::debug(__METHOD__); }
  public function updatedFooBar($value)   { \Log::debug(__METHOD__); }
  public function render() {
    \Log::debug(__METHOD__);
    return <<<'EOS'
      <div style="padding:1em;background-color:#AFA;">
        親コンポーネント<br>
        <livewire:child-component :settings="$settings" />
        {{ $settings->value1 }},{{ $settings->value2 }}
      </div>
      EOS;
  }
}

イベントをキャッチするには「protected $listeners」に定義します。

注目するポイントは33行目の「:settings=”$settings”」です。モデルではなくオブジェクトを渡しているところです。

  1. ライブワイヤーの子コンポーネントを作成します。app/Http/Livewire/ChildComponent.php
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class ChildComponent extends Component
{
  public Settings $settings;
  public function boot()                  { \Log::debug(__METHOD__); }
  public function booted()                { \Log::debug(__METHOD__); }
  public function mount()                 { \Log::debug(__METHOD__); }
  public function dehydrate() {
    \Log::debug(__METHOD__);
    \Log::debug('* emitUp *');
    $this->emit('update',$this->settings)->up();
  }
  public function dehydrateFoo($value)    { \Log::debug(__METHOD__); }
  public function dehydrateBoo($value)    { \Log::debug(__METHOD__); }
  public function hydrate()               { \Log::debug(__METHOD__); }
  public function hydrateFoo($value)      { \Log::debug(__METHOD__); }
  public function updating($name,$value)  { \Log::debug(__METHOD__); }
  public function updated($name,$value)   { \Log::debug(__METHOD__); }
  public function updatingFoo($value)     { \Log::debug(__METHOD__); }
  public function updatedFoo($value)      { \Log::debug(__METHOD__); }
  public function updatingFooBar($value)  { \Log::debug(__METHOD__); }
  public function updatedFooBar($value)   { \Log::debug(__METHOD__); }
  public function render() {
    \Log::debug(__METHOD__);
    return <<<'EOS'
      <div style="padding:1em;background-color:#EFE;">
        子コンポーネント<br>
        <input wire:model="settings.value1">
        <input wire:model="settings.value2">
        <br>
        <button wire:click="clear">クリア</button><br>
        {{ $settings->value1 }},{{ $settings->value2 }}
      </div>
      EOS;
  }
  public function clear() {
    \Log::debug(__METHOD__);
    $this->settings->value1 = '';
    $this->settings->value2 = '';
  }
}

親にイベントを送るには「emit()->up()」を使います。

  1. ブラウザでアクセスしてみましょう。

キーボードから子コンポーネントのテキストボックスに何か入力すると、クリアボタンの下の文字(子コンポーネント)と親コンポーネントの文字が変わります。子コンポーネントからイベントを発行して、親コンポーネントの Settings を更新することができました。

↑当初は子コンポーネントでオブジェクトを変更したら、イベントを発行しなくても親オブジェクトの値も変更されるのかなぁ?と思って確認してみたという感じです。

今回、子コンポーネントの dehydrateフック でイベントを発行しました。
当初は updatedフック でイベントを発行していました。テキストボックスに値を入力した場合には、updatedフック が呼び出されますが、クリアボタンを押してプロパティを書き換えた場合には、updatedフック は呼び出されません。dehydrateフック でイベントを発行しない場合は、updateフック と clickメソッド でイベント発行しなくてはなりません。

以上

Scroll to Top