I have full page livewire called MerchMaterials, which uses laravel layout component:
<?php
namespace App\Http\Livewire\Retailer;
use App\Interfaces\HasBreadcrumbs;
use App\Models\RcdpsMerchMaterial;
use App\View\Components\Layouts\Retailer as RetailerLayout;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Collection;
use Livewire\Component;
class MerchMaterials extends Component implements HasBreadcrumbs
{
use AuthorizesRequests;
public $demands;
public $materials;
protected $listeners = [
'add'
];
public function mount($demands = new Collection())
{
$this->demands = $demands;
$this->materials = RcdpsMerchMaterial::all();
$this->authorize('viewAny', RcdpsMerchMaterial::class);
}
public function add(RcdpsMerchMaterial $material, $quantity)
{
$data = new Collection();
$data->put('name', $material->name);
$data->put('quantity', $quantity);
$this->demands->put($material->id, $data);
//dd($this->demands);
//$this->render();
}
public function remove($materialId)
{
//unset($this->demands[$materialId]);
}
public function render()
{
return view('livewire.retailer.merch-materials')
->layout(RetailerLayout::class, [
'title' => __('Materiały merch'),
'breadcrumb' => $this->breadcrumbs()
]);
}
public function breadcrumbs(): array
{
return [
__('Strefa rozwoju') => route('development-zone')
];
}
}
This livewire has child livewires in loop to display some material and add them to the parent $demands property by form:
<div>
<h5 class="py-4 text-uppercase gray"><span class="font-family-bold">{{ __('Magazyn PPSOM') }}</span>: {{ __('Zapotrzebowanie') }}</h5>
@json($demands)
<div class="row" wire:loading.remove>
<div class="col-8">
@foreach($demands as $materialId => $demand)
@json($demand)
@endforeach
<table class="table">
<thead class="font-family-bold text-uppercase bg-primary text-white">
<tr>
<th scope="col">{{ __('Materiały') }}</th>
<th scope="col">{{ __('Ilość') }}</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@foreach($this->demands as $materialId => $demand)
<tr>
<th class="text-uppercase" scope="row">{{ $demand['name'] }}</th>
<td>{{ $demand['quantity'] }}</td>
<td><a href="" wire:click.prevent="remove({{ $materialId }})" title="{{ __('Usuń materiał') }}"><object data="{{ asset('/images/rc-garbage-icon.svg') }}"></object></a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<x-alert />
<div class="row">
<div class="col-12 pt-5" wire:loading>
<div class="ring">
<object class="w-50" data="{{ asset('/images/rc-logo.svg') }}"></object>
<span></span>
</div>
</div>
</div>
<div class="row g-3 py-5" wire:loading.remove>
@forelse($materials as $material)
<livewire:retailer.merch-material-tile class="mt-4" :material="$material" :wire:key="'material-'.$material->id"></livewire:retailer.merch-material-tile>
@empty
<p>{{ __('Brak dostępnych materiałów') }}</p>
@endforelse
</div>
</div>
Here is child livewire class:
<?php
namespace App\Http\Livewire\Retailer;
use App\Models\RcdpsMerchMaterial;
use App\View\Components\Layouts\Retailer;
use Illuminate\Support\Collection;
use Livewire\Component;
class MerchMaterialTile extends Component
{
public $material;
public $quantity;
protected $rules = [
'quantity' => 'required|numeric|integer|min:1',
];
public function mount(RcdpsMerchMaterial $material)
{
$this->material = $material;
}
public function updated($propertyName)
{
$this->validateOnly($propertyName);
}
public function store()
{
$validatedData = $this->validate();
//$this->reset('quantity');
$this->emitTo('retailer.merch-materials', 'add', $this->material->id, $validatedData['quantity']);
}
public function render()
{
return view('livewire.retailer.merch-material-tile');
}
}
Here is child livewire blade:
<div class="d-flex justify-content-center align-items-stretch col-lg-3 col-12">
<div class="card shadow">
<img class="card-img-top img-fluid" src="{{ $material->image }}" alt="{{ $material->name }}">
<div class="card-body d-flex flex-column justify-content-between text-center">
<p class="card-title font-family-bold text-uppercase">{{ $material->name }}</p>
<p class="card-text">{{ $material->description }}</p>
</div>
<form class="py-5" wire:submit.prevent="store" method="post">
@csrf
<fieldset class="form-group">
<div class="row justify-content-center">
<div class="form-floating col-md-6 col-12">
<input type="number" wire:model.lazy="quantity" class="form-control @error('quantity') is-invalid @enderror" id="inputQuantity" placeholder="{{ __('Ilość') }}" min="0">
<label for="inputQuantity">{{ __('Ilość') }}<sup class="red">*</sup></label>
@error('quantity')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
</fieldset>
<div class="row pt-4">
<div class="col-12 d-flex justify-content-center">
<x-button type="submit" class="text-uppercase text-nowrap" font="bold">{{ __('Dodaj') }}</x-button>
</div>
</div>
</form>
</div>
</div>
Procedure is pretty simple:
Tried many things like refreshing livewire in add function, adding $this->render(), changing emitTo to emitUp but same result. Any clues? I have been struggling with this for two whole days now...
I would appreciate any help
Found problem.
It was nothing related to above code. I had in my layout {{ $slot }}
two times (second one displayed conditionally on mobile basing on bootstrap media breakpoint). Just refactored my layout to use only one {{ $slot }}
solved issue.
A light bulb went on over my head after reading this article: how-to-structure-your-layout-file-for-livewire