I'm building a full page Livewire component that filters Meilisearch results.
When I check one of the filters the remaning checkboxes disappear, and I am struggling to understand why. I followed a tutorial to figure out how to build the bulk of the component and in the tutorial the behaviour didn't occur. I've deconstructed the component to the bare minimum and the behaviour happens as soon as I wire up the checkboxes.
Here is the component:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\CourseDate;
use App\Models\Course;
class CourseBrowser extends Component
{
public $queryFilters = [];
public $priceRange = [
'min' => null,
'max' => null
];
public function mount()
{
$this->queryFilters = [
'venue' => [],
'type' => [],
'category' => [],
'days' => [],
'supplier' => []
];
}
public function render()
{
$search = CourseDate::search(
'',
function ($meilisearch, string $query, array $options) {
$filters = collect($this->queryFilters)
->filter(fn ($filter) => !empty($filter))
->recursive()
->map(function ($value, $key) {
return $value->map(fn ($value) => $key . ' = "' . $value . '"');
})
->flatten()
->join(' AND ');
$options['facets'] = ['venue', 'category', 'type', 'supplier', 'days'];
$options['filter'] = null;
if ($filters) {
$options['filter'] = $filters;
}
if ($this->priceRange['max']) {
$options['filter'] .= (isset($options['filter']) ? ' AND ' : '') . 'price <= ' . $this->priceRange['max'];
}
return $meilisearch->search($query, $options);
}
)->raw();
$coursedates = CourseDate::find(collect($search['hits'])->pluck('id'));
$minPrice = Course::all()->min('price');
$maxPrice = Course::all()->max('price');
$this->priceRange['min'] = $this->priceRange['min'] ?: $minPrice;
$this->priceRange['max'] = $this->priceRange['max'] ?: $maxPrice;
return view('livewire.course-browser')
->with(
[
'coursedates' => $coursedates,
'filters' => $search['facetDistribution'],
'minPrice' => $minPrice,
'maxPrice' => $maxPrice,
]
);
}
}
Here is the Blade view. I had originally posted an edited section showing just the checkboxes but I have edited this question to show the view in its entirety.
<div>
<div class="mb-4 text-gray-500">
<a href="{{ route('home') }}">Home</a> / <a href="{{ route('course.index') }}">Courses</a>
</div>
<div class="align-center mt-8 mb-10 flex items-center justify-between">
<div class="text-3xl font-bold">
All Courses
</div>
</div>
<div class="grid w-full grid-cols-4 gap-6">
<div class="col-span-1 rounded-sm bg-white shadow">
<div class="space-y-2 px-6 py-6">
<h6 class="text-sm font-bold uppercase">Filters</h6>
<hr class="mt-4 mb-6 border-gray-200 pb-2">
<h6 class="text-sm font-bold uppercase">Price </h6>
<div class="flex gap-1">
<input type="range"
min="{{ $minPrice }}"
max="{{ $maxPrice }}"
step="25"
class="accent-pink-700"
wire:model="priceRange.max" />
<span class="font-sm">
(£{{ $priceRange['max'] }})
</span>
</div>
@foreach ($filters as $title => $filter)
<h6 class="pt-2 text-sm font-bold uppercase">{{ Str::title($title) }}</h6>
@foreach ($filter as $option => $count)
<div wire:ignore.self
class="flex items-center space-x-2">
<input type="checkbox"
class="h-4 w-4 rounded border-gray-300 text-sm text-pink-600 focus:ring-0 focus:ring-offset-0"
wire:model="queryFilters.{{ $title }}"
id="{{ $title }}_{{ strtolower($option) }}"
value="{{ $option }}">
<label class="text-sm"
for="{{ $title }}_{{ strtolower($option) }}">{{ $option }} ({{ $count }})</label>
</div>
@endforeach
@endforeach
</div>
</div>
<div class="col-span-3">
<h6 class="text-md my-4 mb-4">{{ $coursedates->count() }} {{ Str::plural('course', $coursedates) }} matching your filters</h6>
@forelse ($coursedates as $coursedate)
<div wire:loading.class="opacity-50"
class="relative mb-6 w-full rounded-sm bg-white shadow">
<span class="absolute top-2 left-2 rounded bg-pink-800 px-3 py-1 font-bold text-white">{{ $coursedate->course->category->name }}</span>
<span class="absolute top-2 right-2 rounded bg-pink-600 px-3 py-1 font-bold text-white">{{ $coursedate->course->supplier->name }}</span>
<a href="{{ route('course.show', $coursedate->course->slug) }}">
<img src="{{ asset('storage/courses/images/' . $coursedate->course->title_image) }}"
alt="{{ $coursedate->course->slug }}"
class="h-40 w-full object-cover">
</a>
<div class="px-6 py-4">
<h6 class="mb-2 text-lg font-bold"><a href="{{ route('course.show', $coursedate->course->slug) }}">{{ $coursedate->course->name }} </a></h6>
<p class="mb-6 text-sm leading-tight">{{ $coursedate->course->tagline }}</p>
{{-- Pills Container --}}
<div class="flex">
<div class="mr-1 flex items-center rounded bg-pink-300 font-bold text-pink-800">
<div class="flex h-full items-center rounded-l bg-pink-400 px-2 py-1">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="h-4 w-4 text-pink-200">
<path d="M4.5 3.75a3 3 0 00-3 3v.75h21v-.75a3 3 0 00-3-3h-15z" />
<path fill-rule="evenodd"
d="M22.5 9.75h-21v7.5a3 3 0 003 3h15a3 3 0 003-3v-7.5zm-18 3.75a.75.75 0 01.75-.75h6a.75.75 0 010 1.5h-6a.75.75 0 01-.75-.75zm.75 2.25a.75.75 0 000 1.5h3a.75.75 0 000-1.5h-3z"
clip-rule="evenodd" />
</svg>
</div>
<div class="flex h-full items-center rounded bg-pink-300 px-3 py-1">
£{{ $coursedate->course->price }}
</div>
</div>
<div class="mr-1 flex items-center rounded bg-pink-500 font-bold text-white">
<div class="flex h-full items-center rounded-l bg-pink-600 px-2 py-1">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="h-4 w-4">
<path fill-rule="evenodd"
d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z"
clip-rule="evenodd" />
</svg>
</div>
<div class="flex h-full items-center rounded bg-pink-500 px-3 py-1">
{{ $coursedate->venue->city }}
</div>
</div>
@foreach ($coursedate->actualdates as $dates)
@if ($coursedate->course->number_of_days > 1)
<div class="mr-1 flex items-center rounded bg-pink-200 font-bold text-pink-800">
<div class="flex h-full items-center gap-1 rounded-l bg-pink-200 px-2 py-1 text-xs">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="inline-flex h-5 w-5">
<path d="M12.75 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM7.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM8.25 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM9.75 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM10.5 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM12.75 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM14.25 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 13.5a.75.75 0 100-1.5.75.75 0 000 1.5z" />
<path fill-rule="evenodd"
d="M6.75 2.25A.75.75 0 017.5 3v1.5h9V3A.75.75 0 0118 3v1.5h.75a3 3 0 013 3v11.25a3 3 0 01-3 3H5.25a3 3 0 01-3-3V7.5a3 3 0 013-3H6V3a.75.75 0 01.75-.75zm13.5 9a1.5 1.5 0 00-1.5-1.5H5.25a1.5 1.5 0 00-1.5 1.5v7.5a1.5 1.5 0 001.5 1.5h13.5a1.5 1.5 0 001.5-1.5v-7.5z"
clip-rule="evenodd" />
</svg>
{{ $dates->label }}
</div>
<div class="flex h-full items-center rounded-r bg-pink-700 px-2 py-1 text-pink-100">
{{ $dates->date->format('jS M Y') }}
</div>
</div>
@else
<div class="mr-1 flex items-center rounded bg-pink-200 text-sm font-bold text-pink-800">
<div class="flex h-full rounded-l bg-pink-200 py-1 px-2 text-sm font-bold text-pink-800">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="inline-flex h-5 w-5">
<path d="M12.75 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM7.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM8.25 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM9.75 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM10.5 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM12.75 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM14.25 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 13.5a.75.75 0 100-1.5.75.75 0 000 1.5z" />
<path fill-rule="evenodd"
d="M6.75 2.25A.75.75 0 017.5 3v1.5h9V3A.75.75 0 0118 3v1.5h.75a3 3 0 013 3v11.25a3 3 0 01-3 3H5.25a3 3 0 01-3-3V7.5a3 3 0 013-3H6V3a.75.75 0 01.75-.75zm13.5 9a1.5 1.5 0 00-1.5-1.5H5.25a1.5 1.5 0 00-1.5 1.5v7.5a1.5 1.5 0 001.5 1.5h13.5a1.5 1.5 0 001.5-1.5v-7.5z"
clip-rule="evenodd" />
</svg>
</div>
<div class="flex h-full items-center rounded-r bg-pink-700 px-2 py-1 text-pink-100"> {{ $dates->date->format('jS M Y') }} </div>
</div>
@endif
@endforeach
</div>
<div class="mt-6 mb-4">
</div>
</div>
</div>
@empty
No Course Dates to show
@endforelse
</div>
</div>
</div>
The component in itself works fine. But as you can see in the gif below, the non selected checkboxes disappear, so I cannot apply multiple filters.
I suspect the problem lies in the way I'm storing the filters in the array but I can't see the woods for the trees at the moment. The filtering itself currently works, and any attempt to change the format of the array results in it not working at all.
Thanks in advance.


There are certain bugs in Livewire, and this is also one of them.
Try enclosing the each checkbox in a div.
If the issue still presist, add wire:ignore.self to container