Search code examples
laravellaravel-livewire

how to bind the model relation value in livewire wire:model?


There is a Product model with hasmany descriptions relationship.
descriptions relation has two columns, count, body.

How I must define the wire:model value to show the selected product descriptions values in Livewire Update component?

I must mention getting data from relation works fine just can't show data in the input tag of wire:model attribute! I think the problem is on the key definition of protected $rules or wire:model value!

the Update Class:

public $product;

protected $listeners = ['selectedProduct'];

public function selectedProduct($id){
    $this->product = Product::with('descriptions')->findOrFail($id);
}

protected $rules = [
    "description.count" => "required",
    "description.body" => "required",
];

the livewire view:

@if($product)
    @foreach($product->descriptions as $description)
        <input type="text" wire:model="description.count">
        <texatarea wire:model="description.body"></texatarea>
    @endforeach
@endif

the loop and number of filed is repeated correct but no data is shown!


Solution

  • There are a few things I'd like to mention, first is that your view should always only have one root HTML element - and subsequent elements that is generated in a loop should also have one unique root element inside, that has wire:key on it. The wire:key should be something unique on your page.

    Then we need to have a look at the rules - its correct that any models needs a rule to be visible in input-elements, but you have a collection of elements, so the rules need to reflect that. To do that, you specify a wildcard as the key

    protected $rules = [
        "description.*.count" => "required",
        "description.*.body" => "required",
    ];
    

    The fields then has to be bound towards an index, so Livewire knows its in a collection.

    <div>
        @if ($product)
            @foreach($product->descriptions as $index=>$description)
                <div wire:key="product-description-{{ $description->id }}">
                    <input type="text" wire:model="descriptions.{{ $index }}.count">
                    <texatarea wire:model="descriptions.{{ $index }}.body"></texatarea>
                </div>
            @endforeach
        @endif
    </div>
    

    Finally, you need to declare the descriptions as a public property on the class, and store them there.

    public  $product;
    public  $descriptions;
    
    protected $listeners = ['selectedProduct'];
    protected $rules = [
        "description.*.count" => "required",
        "description.*.body" => "required",
    ];
    
    public function selectedProduct($id){
        $this->product = Product::with('descriptions')->findOrFail($id);
        $this->descriptions = $this->product->descriptions;
    }