I want bind my livewire component properties directly in the html elements using wire:model, I can make this work with simple elements, but When I try bind a property I get error. Leave explain me better with an example:
This is my current working component. This has 3 auxiliar variables that represent 3 fields in my edit form:
class ListShops extends Component
{
use WithPagination;
public $searchTerm;
public $currentShop;
public $showModal = false;
//Campos de edición
public $currentName;
public $currentStatus;
public $currentRegion;
public function cancelEditingShop() { $this->showModal = false; }
public function selectShopToEdit($editingShopId){
$this->currentShop = Tienda::findOrFail($editingShopId);
$this->currentName = $this->currentShop->name;
$this->currentRegion = $this->currentShop->region;
$this->currentStatus = $this->currentShop->enabled;
$this->showModal = true;
}
public function saveEditingShop(){
$this->currentShop->name = $this->currentName;
$this->currentShop->region = $this->currentRegion;
$this->currentShop->enabled = $this->currentStatus;
$this->currentShop->save();
$this->currentShop = null;
$this->showModal = false;
}
public function render()
{
if($this->searchTerm!=''){
$this->resetPage();
}
$tiendas = Tienda::
select('id', 'name', 'keyname', 'country_id', 'enabled', 'region')
->where('name', 'like', '%' . $this->searchTerm . '%')
->paginate(10);
return view('livewire.list-shops', ['tiendas' => $tiendas]);
}
}
On my blade.php file I have a html table with a list of shops, and at the end a conditional form that only loads when $currentShop exists. (Currentshops variable exists when you click Edit button and selectShopToEdit function executes.
@if ($currentShop)
<div class="{{ $showModal ? 'block' : 'hidden' }} fixed z-10 inset-0 overflow-y-auto">
<div >
<div >
<div >
<div>
<h3>{{__('shops.edit.country')}}</h3>
<div class="relative z-0 w-full group">
<input type="text"
wire:model="currentName"
name="shop_name" id="shop_name"
placeholder=" " required/>
<label for="shop_name">
{{ __('shops.name') }}</label>
</div>
<div class="group">
<label for="region" class="sr-only">Region</label>
<select id="region" wire:model="currentRegion" required>
<option {{ $currentShop->region == 'AM' ? 'selected' : '' }} value="AM">{{ __('shops.america') }}</option>
<option {{ $currentShop->region == 'EU' ? 'selected' : '' }} value="EU">{{ __('shops.europe') }}</option>
</select>
</div>
<div class="group my-4">
<fieldset >
<legend class="sr-only">Status</legend>
<div class="flex items-center mb-4">
<input id="status_enabled" wire:model="currentStatus" type="radio" name="status" value="1" checked />
<label for="status_enabled">
{{ __('shops.online') }}
</label>
</div>
<div class="flex items-center mb-4">
<input id="status_disabled" wire:model="currentStatus" type="radio" name="status" value="0" />
<label for="status_disabled">
{{ __('shops.offline') }}
</label>
</div>
</fieldset>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
{{-- Botones del modal --}}
<button wire:click="saveEditingShop" >
{{ __('shops.button.save') }}
</button>
<button wire:click="cancelEditingShop" >
{{ __('shops.button.close') }}
</button>
</div>
</div>
</div>
</div>
@endif
I want simplify and use my $currentShop instance instead the 3 auxiliar variables. I tried something like this (Deleting the use of these three vars in the component and using currentShop.xxx in the wire:model):
<?php
namespace App\Livewire;
use App\Models\Tienda;
use Livewire\Component;
use Livewire\WithPagination;
class ListShops extends Component
{
use WithPagination;
public $searchTerm;
public $currentShop;
public $showModal = false;
public function cancelEditingShop() { $this->showModal = false; }
public function selectShopToEdit($editingShopId){
$this->currentShop = Tienda::findOrFail($editingShopId);
$this->showModal = true;
}
public function saveEditingShop(){
$this->currentShop->save();
$this->currentShop = null;
$this->showModal = false;
}
public function render()
{
if($this->searchTerm!=''){
$this->resetPage();
}
$tiendas = Tienda::
select('id', 'name', 'keyname', 'country_id', 'enabled', 'region')
->where('name', 'like', '%' . $this->searchTerm . '%')
->paginate(10);
return view('livewire.list-shops', ['tiendas' => $tiendas]);
}
}
For reduce code, is the same that initial example but with this wire:model settings:
<input type="text"
wire:model="currentShop.name"
name="shop_name" id="shop_name"
placeholder=" " required />
...
<select id="region" wire:model="currentShop.region" required >
...
<input id="status_enabled" wire:model="currentShop.enabled" type="radio" name="status"
value="1" />
but this error produces when I open the modal (at javascript console, not error in laravel or php components):
Uncaught TypeError: Cannot read properties of null (reading 'name')
So the problem is when my input model is getting currentShop.name my component currentShop element is not initialized. But this component is not loaded in the html until I press in the edit button and the "selectShopToEdit" is executed that is where I am initiating this variable. So, I am confused on why currentShop is not initialized at this time when in the first example the variables $currentName, etc.. are working and initied in the same component function.