Add all form elements that we need for the filter to the view
Most of the form elements are Jetstream components
(Tip: Ctrl + Click on the component name to see how the component is implemented)
There is no form tag, because we don't need to submit the form
(Any change in the form elements will trigger the updated method and the results will be updated automatically)
None of the input elements have a value attribute
(The value attribute is not needed, because the wire:model attribute is used)
{{-- filter section: artist or title, genre, max price and records per page --}}<div class="grid grid-cols-10 gap-4"><div class="col-span-10 md:col-span-5 lg:col-span-3"><x-jet-label for="name" value="Filter"/><div
class="relative"><x-jet-input id="name" type="text"class="block mt-1 w-full"
placeholder="Filter Artist Or Record"/><div
class="w-5 absolute right-4 top-3 cursor-pointer"><x-phosphor-x-duotone/></div></div></div><div class="col-span-5 md:col-span-2 lg:col-span-2"><x-jet-label for="genre" value="Genre"/><x-tmk.form.select id="genre"class="block mt-1 w-full"><option value="%">All Genres</option></x-tmk.form.select></div><div class="col-span-5 md:col-span-3 lg:col-span-2"><x-jet-label for="perPage" value="Records per page"/><x-tmk.form.select id="perPage"class="block mt-1 w-full"><option value="3">3</option><option value="6">6</option><option value="9">9</option><option value="12">12</option><option value="15">15</option><option value="18">18</option><option value="24">24</option></x-tmk.form.select></div><div class="col-span-10 lg:col-span-3"><x-jet-label for="price">Price ≤<output id="pricefilter" name="pricefilter"></output></x-jet-label><x-jet-input type="range" id="price" name="price"
min="0"
max="100"
oninput="pricefilter.value = price.value"class="block mt-4 w-full h-2 bg-indigo-100 accent-indigo-600 appearance-none"/></div></div>
The Records per page is updated before the default value in the component is $perPage = 6
The default values of the $price and $name are null that's why these form elements are not modified
IMPORTANT
We can't use @foreach($allGenres as $genre) because $genre is already a property in the component.
That's the reason why we use @foreach($allGenres as $g) instead of @foreach($allGenres as $genre)
# Update the range input element with min and max values
TIP
Remember that the render() method is called every time a property is updated
Because the minimum and maximum price is not going to change, we can use the render() method, but this will slow down the page load
and that's not what we want
Livewire has a mount() method that is called only once, when the component is loaded and just before the first render() method is called
Set the min and max values of the range input element to the minimum and maximum price of the records
Therefor we need the extra properties $priceMin and $priceMax
The min and max values will also be calculated in the mount() method because they are not going to change
Line 6: add the properties $priceMin and $priceMax
Line 16: use the min()(opens new window) method to calculate the minimum price in the records collection and round it down to the nearest integer
Line 17: use the max()(opens new window) method to calculate the maximum price in the records collection and round it down to the nearest integer
Line 18: set the default selected $price property to the $priceMax property
// public propertiespublic perPage =6;public$name;public$genre='%';public$price;public$priceMin,$priceMax;public$loading='Please wait...';public$selectedRecord;public$showModal=false;publicfunctionshowTracks(Record $record){...}publicfunctionmount(){$this->priceMin=ceil(Record::min('price'));$this->priceMax=ceil(Record::max('price'));$this->price=$this->priceMax;}publicfunctionrender(){...}
Line 6: if you search for record title that contains the letters bo, you have to append and prepend a % to find these letters at any position:
where([['title', 'like', '%bo%'], [...], [...]])
Line 7: the values of $genre are numbers, except the first one. 'All genres' is not a number but corresponds to the value %.
Because of the different types, you have to use 'like' to compare and not '=' e.g.
for 'pop/rock' you get where([[...], ['genre_id', 'like', 1], [...]])
and for 'All genres' you get where([[...], ['genre_id', 'like', '%'], [...]])
Line 8: the price of the record must be less or equal to the $price property
It's a good practice to give some feedback to the user if the result is empty
We'll do this by adding an alert box below the form
Use the Blade @if directive in combination with the isEmpty()(opens new window) method to show the alert only if the result is empty
{{-- master section: cards with paginationlinks --}}<div class="my-4">{{$records->links()}}</div><div class="grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-8 mt-8"...><div class="my-4">{{$records->links()}}</div>{{-- No records found --}}
@if($records->isEmpty())<x-tmk.alert type="danger"class="w-full">
Can't find any artist or album with <b>'{{$name}}'</b>for this genre
</x-tmk.alert>
@endif
After that, we're redirected back to the first page, because with every change in one of the properties, the updated() method is called: public function updated() { $this->resetPage(); }
We only want to reset the page when the filter has been changed, not when anything else is changed
So we need to use a more specific paginator behavior inside the updated() method
The paginator will only reset the page when the $perPage, $name, $genre or $price property has changed
We already placed a little X over the input field to clear the filter but it's not working yet
Make an Alpine component:
Line 2: initialize an Alpine component with the x-data attribute
Bind (entangle) the Alpine name property to the $name property of the Livewire component
LIne 5 - 6: replace the server side Livewire bounding (wire:model) with the client side Alpine bounding (x-model)
Line 10: the div with the X is only visible when the $name property is not empty
Line 11: add a @click event to the X to empty the value of the input field with the name = '' statement