Laravel Livewire live search not correctly updating results

Issue

I have the following livewire code to perform a search of page titles which works fine on the first search, but subsequent searches do not completely remove the results from the previous search.

namespace App\Http\Livewire\Page;

use App\Models\Page;
use Livewire\Component;

class Search extends Component
{
    public $searchQuery = "";
    public $searchResults;

    public function resetSearchValue() {
        $this->searchQuery = "";
    }

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

    public function render()
    {
        if(strlen($this->searchQuery) > 2) {
            $this->searchResults = Page::query()
                ->select(['title', 'slug'])
                ->where('title', 'like', "%{$this->searchQuery}%")
                ->get();
        }
        return view('livewire.page.search');
    }
}

and

<div>
    <input 
        wire:model.debounce.350ms="searchQuery" 
        @focus="searchFocused = true" 
        @blur="searchFocused = false" 
        @click.outside="$wire.resetSearchValue()" 
        autocomplete="off" 
        type="text" 
        placeholder="Search..." 
        id="sitesearch" 
    >
    @if(strlen($searchQuery) > 0)
        <div  
            role="menu" 
            aria-orientation="vertical" 
            -labelledby="menu-button" 
            tabindex="-1"
        >
            <div class="py-1" role="none">
                @if(strlen($searchQuery) > 2)
                    @if($searchResults->count() > 0)
                        @foreach($searchResults as $result)
                            <a 
                                href="{{ route('pages.index', $result->slug) }}"
                                role="menuitem" 
                                tabindex="-1" 
                                id="menu-item-0"
                            >{{ $result->title }}</a>
                        @endforeach
                    @else
                        <p>
                            No results found
                        </p>
                    @endif
                @else
                    <p>
                        You need to type at least 3 characters
                    </p>      
            </div>     
        </div>
    @endif
</div>

Searching for "Page" for example will return something like

  • My Page 1
  • Page 2
  • Wonderful Page 3

But if I then search for "Page Z" the results I get are

  • My Page 1
  • Page 2
  • No results found

If I the search for "Page Zz" the results I get are

  • My Page 1
  • No results found

I can’t work out why it’s not clearing the results from previous results.

Solution

Basically, this is the result of a DOM-diffing issue Livewire has when it can’t keep track of elements, typically dynamic elements generated in a loop.

The simple solution to this, is to add wire:key with a value to the root element in your loop, like shown below.

@if(strlen($searchQuery) > 2)
    @if($searchResults->count() > 0)
        @foreach($searchResults as $result)
            <a 
                href="{{ route('pages.index', $result->slug) }}"
                role="menuitem" 
                tabindex="-1" 
                id="menu-item-0"
                wire:key="result-{{ $result->id }}"
            >{{ $result->title }}</a>
        @endforeach
    @else
        <p wire:key="no-results">
            No results found
        </p>
    @endif
@else
    <p wire:key="searchquery-short">
        You need to type at least 3 characters
    </p> 
@endif

Also, I’ve added these to the other options which may be shown in place, just so it’s no doubt about which element it should show.

Just a note, all the values to wire:key on a page must be unique (like with ID attributes to HTML elements).

Answered By – Qirel

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published