Livewire component not refreshing after Wire:Change

I have some task list functionality that works, except for the AJAX functionality.

The checkbox checks. It changes the value in the DB. And if I refresh the page the correct CSS is applied to show the text with a strikethrough.

The expected behavior is all of the above, with the addition of the strikethrough happening immediately after clicking the checkbox.

I am using Livewire version 0.7.4 and have updated my code to convert @livewireAssets to @livewireStyles/@livewireScripts as instructed in the release documentation back at version 0.6.0.

Here is the code for my checkbox:

<ul class="list-group">
    @foreach($this->tasks as $task)
        <li class="list-group-item d-fleex-justify-content-between align-items-center">
            <div>
                <input type="checkbox" wire:click="toggleTask({{$task->id}})" class="mr-4" {{ $task->taskcomplete ? 'checked' : '' }}>
                <span class="{{ $task->taskcomplete ? 'completed' : '' }}">
                    {{ Carbon\Carbon::createFromDate($task->taskduedate)->format('m-d-Y') }}: <a href="/contacts/{{ $task->deal->contact->id }}">{{ $task->deal->contact->contact1lastname }}</a> - {{ $task->tasktext }} - {{ $task->deal->dealtype }}
                </span>
            </div>
        </li>
    @endforeach
</ul>

And here is my Livewire controller file:

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Task;

class Alltasks extends Component
{
    public $id;
    public $tasks;
    public $contacts;
    public $tasktext;
    public $taskduedate;

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

    public function render()
    {
        return view('livewire.alltasks');
    }

    public function toggleTask($id)
    {
        $task = Task::find($id);

        $task->taskcomplete = !$task->taskcomplete;
        $task->save();
    }
}

I got most of this code from the following YouTube tutorial (which is very good I might add):

Just not sure why the AJAX portion of the change to my page is not working correctly.

I will say that my setup runs quite slow because I am running Laragon on a USB drive which is painfully slow. Not sure if any timeouts are occurring from an AJAX perspective within Livewire.

What’s happening is:

  1. All of your tasks are being pulled from the database and loaded into your public $tasks variable when the component hydrates.
  2. Then toggleTask() is being called which pulls the individual task out of the database.
  3. taskcomplete is toggled, and the task is saved.
  4. Component is rerendered.

The problem is the data loaded to your public $tasks property hasn’t changed, so the taskcomplete property on the task you just changed in the database is still the original value that was pulled from the db before you changed it, so your class isn’t updating in your view.

Knee jerk reaction to fix it would be to refresh your public $tasks by $this->tasks = $this->tasks->fresh(); after you save the task in toggleTask(). ->fresh() reaches into the database again and repulls all the information, so it will contain the change you just made.

But, I think we can cut your database calls from 4 to 2 by changing
$task = Task::find($id);
to
$task = $this->tasks->find($id);
because I’m like 90% sure that they stay linked in memory, so your $tasks property will stay in sync without having to go into the database again. If I’m wrong, doing it the first way will fix it.

3 Likes

Thanks so much for the explanation! And your last suggestion worked perfectly!

I guess I am just wondering why my original code worked for the guy in the tutorial but mine had to be adjusted with your suggestion.

Could you help me understand that?

I only skimmed through the video, so if he changed this and I missed it, I’ll have to look again.

It looks like the difference is you are pulling in your tasks in the mount() method, (you pulled from the database already and passed in the collection into your component) so you are pulling them in the beginning and persisting them through all the livewire requests. In the video, he is pulling fresh information from the database every time the render method is hit, so at the end of every request.

Your current way is a much more efficient way of doing this, and this video tutorial isn’t showing great practices. As soon as he added wire:model to the text input, every time he types into the text box, the database is being queried for no reason now. Livewire defaults to a 150ms debounce, so for the way most “regular” people type, every time a character is entered into that text box, livewire is making a request to the database. By having your tasks assigned to a property, livewire only hits the database when something on that property needs changed.

1 Like