Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

KLassiux's avatar

Livewire parameter is not passing on select change

Hello, I've got a little problem with Livewire, I'm trying to pass parameters to a component like this :

<livewire:create-screen :templates="$templates" :contents="$contents" :template_id="null"/>

Here's my component :

<div>
    <div class="mt-4 col-span-3">
        <label for="template" class="block text-sm font-medium text-gray-700 mt-1">Template</label>
        <select wire:model="template_id" required id="template" name="template" class="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
            <option value="">--</option>
            @foreach ($templates as $unTemplate)
                <option value="{{ $unTemplate->id }}">{{ $unTemplate->name }}</option>
            @endforeach
        </select>
    </div>
    {{ $contents }}
</div>

When I initially render the component, the $contents variable is returned correctly, but when I change the select's option, it returns an empty array. What's strange is that $templates always returns what it should return, never an empty array, only $contents is not working...

Here's the PHP :

<?php

namespace App\Http\Livewire;

use App\Models\Template;
use Livewire\Component;

class CreateScreen extends Component
{
    public $templates;
    public $template_id;
    public $template;
    public $contents;

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

    public function updatedTemplateId()
    {
        $this->template = Template::find($this->template_id);
    }

    public function render()
    {
        return view('livewire.create-screen', [
            'template' => $this->template,
        ]);
    }
}

And here's the query corresponding to $contents :

$first = VideoContent::selectRaw('id, "video" as type, video as filename, original_filename, null as text, name')
                            ->where('customer_id','=',$customer->id);
    
        $second = ImageContent::selectRaw('id, "image" as type, image as filename, original_filename, null as text, name')
                    ->where('customer_id','=',$customer->id);
        
        $contents = TextContent::selectRaw('id, "text" as type, null as filename, null as original_filename, content as text, name')
                    ->where('customer_id','=',$customer->id)
                    ->union($first)
                    ->union($second)
                    ->orderBy('id')
                    ->get();

Thanks for your help and have a great wednesday.

0 likes
38 replies
vincent15000's avatar

Step by step ... does this work ?

public function updatedTemplateId()
{
    dd($this->template_id);
}
KLassiux's avatar

@vincent15000 It looks like this is coming from my query, for some reasons, Livewire doesn't really like the union clause

1 like
vincent15000's avatar

@KLassiux Ok so the template id is successfully updated but the template isn't updated in the view. Is that the problem ?

KLassiux's avatar

@vincent15000 No, the $template is successfully updated, that's not the issue. The issue is coming from the $contents variable that is empty when the select's option is changed, even though $contents is never changed.

1 like
vincent15000's avatar

@KLassiux You can also rewrite the render function like this.

public function render()
{
    return view('livewire.create-screen');
}

As the template variable is a public variable, it is automatically sent to the view, you don't need to explicitely send it.

vincent15000's avatar

@KLassiux I already had such a problem with Livewire. I solved it by removing the public variable.

Remove the public template variable and write the render function like this.

public function render()
{
	$template = Template::find($this->template_id);
    return view('livewire.create-screen', compact('template'));
}
vincent15000's avatar

@KLassiux Wow big confusion sorry ... You already said it, I don't know why, I was thinking about the template.

vincent15000's avatar

@KLassiux Does this work ?

public function updatedTemplateId()
{
	dd($this->contents);
    $this->template = Template::find($this->template_id);
}
migsAV's avatar

@klassiux where are you running this code?

$first = VideoContent::selectRaw('id, "video" as type, video as filename, original_filename, null as text, name')
                            ->where('customer_id','=',$customer->id);
    
        $second = ImageContent::selectRaw('id, "image" as type, image as filename, original_filename, null as text, name')
                    ->where('customer_id','=',$customer->id);
        
        $contents = TextContent::selectRaw('id, "text" as type, null as filename, null as original_filename, content as text, name')
                    ->where('customer_id','=',$customer->id)
                    ->union($first)
                    ->union($second)
                    ->orderBy('id')
                    ->get();
1 like
KLassiux's avatar

@migsAV In a controller. I already tried reruning it in the component class, but it still returns an empty array for $contents

1 like
KLassiux's avatar

@migsAV

public function showCreateScreen(Customer $customer)
    {
        $this->authorize('affichage', [Customer::class, $customer]);

        $first = VideoContent::selectRaw('id, "video" as type, video as filename, original_filename, null as text, name')
                            ->where('customer_id','=',$customer->id);
    
        $second = ImageContent::selectRaw('id, "image" as type, image as filename, original_filename, null as text, name')
                    ->where('customer_id','=',$customer->id);
        
        $contents = TextContent::selectRaw('id, "text" as type, null as filename, null as original_filename, content as text, name')
                    ->where('customer_id','=',$customer->id)
                    ->union($first)
                    ->union($second)
                    ->orderBy('id')
                    ->get();

        return view('screens.screen-create', [
            'customer' => $customer,
            'templates' => $customer->templates,
            'contents' => $contents
        ]);
    }
migsAV's avatar

@vincent15000 then in that case it will return an empty array after changing the select option because the Livewire component gets re-rendered and the variable $content will be empty.

1 like
vincent15000's avatar

@migsAV Not sure ... if the component re-renders, there is no reason why the content variable should change.

KLassiux's avatar

@migsAV I don't understand what you mean, the $templates variable never updates and still returns the correct result, why would it not work for $contents ? Also, i tried without using the unions and it's working perfectly, but I really need it

1 like
migsAV's avatar

@KLassiux as @vincent15000 mentions above, what happens when you try

public function updatedTemplateId()
{
	dd($this->contents);
    $this->template = Template::find($this->template_id);
}
migsAV's avatar

@KLassiux @vincent15000 , If I understand Livewire correctly it sends a network request like ajax so your component will re-reneder. If you don't want this you need to add defer on your input.

<select wire:model.defer="template_id" required id="template" name="template" class="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
            <option value="">--</option>
            @foreach ($templates as $unTemplate)
                <option value="{{ $unTemplate->id }}">{{ $unTemplate->name }}</option>
            @endforeach
        </select>
1 like
KLassiux's avatar

@migsAV I want my component to rerender, because the $template is supposed to change on select's option change. When it changes, my view is updated.

1 like
migsAV's avatar

In this case you do not want to use defer

1 like
KLassiux's avatar

@migsAV here's the full component :

<div>
    <div class="mt-4 col-span-3">
        <label for="template" class="block text-sm font-medium text-gray-700 mt-1">Template</label>
        <select wire:model="template_id" required id="template" name="template" class="mt-1 block w-full py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
            <option value="">--</option>
            @foreach ($templates as $unTemplate)
                <option value="{{ $unTemplate->id }}">{{ $unTemplate->name }}</option>
            @endforeach
        </select>
    </div>

    {{ $contents }}

    @if ($template)
        <div class="mt-10" id="wrapper">
            @foreach($template->template_lines as $line)
                <div class="grid grid-cols-{{$line->nb_blocs}} h-40 gap-2 mb-2" id="line-{{$line->nb_blocs}}">
                    @for ($i = 0; $i < $line->nb_blocs; $i++)
                        <div class="outline-2 outline outline-blue-500 rounded-md w-full h-full flex items-center justify-center">
                            <select name="bloc[{{$line->id}}][{{$i+1}}]" class="mt-1 block py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm w-52">
                                <option value="">Aucun élément</option>
                                @foreach($contents as $content)
                                    <option value="{'bloc':[{{$content->id}},{{$content->type}}]}">@if (strlen($content->name) > 50) {{ substr($content->name, 0, 50) . '...' }} @else {{ $content->name }} @endif</option>
                                @endforeach
                            </select>
                        </div>
                    @endfor
                </div>
            @endforeach
        </div>
    @endif
</div>

As I previously said, everything's working correctly except for the $contents variable

migsAV's avatar
migsAV
Best Answer
Level 31

@KLassiux try this and see what happens

<?php

namespace App\Http\Livewire;

use App\Models\Template;
use Livewire\Component;

class CreateScreen extends Component
{
    public $templates;
    public $template_id;
    public $template;

   public function contentsQuery() {
        $first = VideoContent::selectRaw('id, "video" as type, video as filename, original_filename, null as text, name')
                            ->where('customer_id','=',1);
    
        $second = ImageContent::selectRaw('id, "image" as type, image as filename, original_filename, null as text, name')
                    ->where('customer_id','=',1);
        
        $contents = TextContent::selectRaw('id, "text" as type, null as filename, null as original_filename, content as text, name')
                    ->where('customer_id','=',1)
                    ->union($first)
                    ->union($second)
                    ->orderBy('id')
                    ->get();

       return $contents;
   }

    public function updatedTemplateId()
    {
        dd($this->contentsQuery());
        $this->template = Template::find($this->template_id);
    }

    public function render()
    {
        return view('livewire.create-screen', [
            'template' => $this->template,
        ]);
    }
}
1 like
KLassiux's avatar

@migsAV Wow, it's actually working... Problem is that everytime a user will change the selected option, 4 queries will be executed instead of 1

1 like
migsAV's avatar

@KLassiux yes, unfortunately, that's where you will need to optimise the query because in your case you will need to execute each time an option is selected

1 like
vincent15000's avatar

@migsAV What's strange is that the contents query isn't binded with the templates choice. So the contents should not reinitialize.

migsAV's avatar

@vincent15000 that is true

In that case @klassiux why don't you wrap the {{ $contents }} in a div with wire:ignore

<div wire:ignore>
{{ $contents }}
</div>
1 like
migsAV's avatar

@KLassiux you're welcome.

with the wire:ignore you don't need to add the contectsQuery to your component

dipankarb's avatar

This issue happens when you make property private/protected. Make it public if possible it will work.

Please or to participate in this conversation.