Dynamic Nested Components

Hi all!

I would like to build a Seatmap with Livewire. But the following Scenario is not working as expected.
Based on the Seat Status ($seat->status) I’m rendering the corresponding Livewire Component.

<?php  
  
namespace App\Http\Livewire\Seatplan;  
  
use App\Seat;  
use Livewire\Component;  
  
class Row extends Component  
{  
  public $row;
  public $y_pos;
  public $y;

  protected $listeners = ['rowUpdated'];  
  
  public function mount($row)  
  { 
    $this->row = $row;  
    $this->y_pos = $this->row->first()->y_pos;  
    $this->y = $this->row->first()->y;  
  }  
  
  public function getWayProperty()  
  {
    return $this->row->count() === $this->row->where('way', true)->count();  
  }  
  
  public function rowUpdated()  
  {
    $this->row = Seat::where('y_pos', $this->y_pos)->get();  
    $this->y_pos = $this->row->first()->y_pos;  
    $this->y = $this->row->first()->y;  
  }  
  
  public function render()  
  {  
    return view('livewire.seatplan.row');  
  }  
}
<div class="row justify-content-start flex-nowrap">  
 <div class="col-2 text-nowrap">{{ $this->way ? null : "Row $y" }}</div>  
 <div class="col-5 d-flex flex-nowrap">  
  @foreach($row as $seat)
    @livewire("seatplan.seat.$seat->status", ["seat" => $seat], key($seat->id))  
  @endforeach  
  </div>  
</div>

Now, when I’m emitting the rowUpdated Event, the Text based on $this->way is changing correctly. But the nested Livewire Component seatplan.seat.$seat->status keeps rendering the “old” Status Component.

For Example:
1 - Status: foo - Livewire Component foo is rendered
2 - Status: updates to bar - Livewire Component foo is still rendered

Thanks for your help!
Regards
Fabian

Where is it emitting rowUpdated?

I’m wondering the same thing, where are you emitting rowUpdated in a parent component?

$this->way is part of a computed property, but $this->row is not computed.

I just posted a similar question about child components not re-rendering new data here: Child component not rendering new data from parent

The rowUpdated Event is emitted inside the Child Component after the Data was changed.

<?php

namespace App\Http\Livewire\Seatplan\Seat;

use App\Seat;
use Livewire\Component;

class Available extends Component
{
    public $seat;

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

    public function toggleAvailable()
    {
        $this->seat->update([
            'available' => !$this->seat->available,
        ]);
        $this->emitUp('rowUpdated');
    }

    public function render()
    {
        return view('livewire.seatplan.seat.available');
    }
}

Instead of doing
@livewire("seatplan.seat.$seat->status", ["seat" => $seat], key($seat->id))

Try doing a conditional instead

@if($seat->status == 'foo')
    @livewire('seatplan.seat.foo'...
@elseif($seat->status == 'bar')
   @livewire('seatplan.seat.bar'...
And so on...

I think it has something to do with the dom diffing package and/or the id that livewire attaches to the root div, I’m not sure, but conditionals seem to work to swap out components and mount them as if it were the initial page load.

If you wish to render dynamic components you need to pass as a third argument to your @livewire directive a unique key. The following worked for me…

@livewire('finance.steps.' . $this->component, compact('application'), key($this->component))