Courses

Vue.js 3 + Laravel 11 API + Vite: SPA CRUD

Category Dropdown: Filtering Data

Summary of this lesson:
- Implementing watch for category changes
- Adding filter parameters to API requests
- Managing filtered data in Laravel backend
- Fixing pagination with filtered results

Now that we have a select input for the category, let's make it work as a filter. After selecting a category, the list should show only the posts that only belong to that category.

selected category filter


Vue: Watch the Selected Category

To get all the posts that belong to a category, we need to watch the selectedCategory variable. First, we need to import watch from the Vue.

resources/js/components/Posts/Index.vue:

<template>
// ...
</template>
 
<script setup>
import { onMounted, ref } from "vue";
import { onMounted, ref, watch } from "vue";
import { TailwindPagination } from 'laravel-vue-pagination';
import usePosts from "@/composables/posts";
import useCategories from "@/composables/categories";
// ...
</script>

Now using this watch() function, we can watch the selectedCategory variable for the changes with two values: current and previous.

Inside watch we can call the getPosts which already accepts page, but we can now also pass the current value of the selected category.

resources/js/components/Posts/Index.vue:

<script setup>
import { onMounted, ref, watch } from "vue";
// ...
 
watch(selectedCategory, (current, previous) => {
getPosts(1, current)
})
</script>

Composable: Accept & Pass the Category

Next, we need to modify posts Composable so that getPosts would accept the category, which by default will be an empty string.

And the same as for the page parameter, pass the category to the API request.

resources/js/composables/posts.js:

import { ref } from 'vue'
 
export default function usePosts() {
const posts = ref({})
 
const getPosts = async (page = 1) => {
axios.get('/api/posts?page=' + page)
const getPosts = async (page = 1, category = '') => {
axios.get('/api/posts?page=' + page + '&category=' + category)
.then(response => {
posts.value = response.data;
})
}
 
return { posts, getPosts }
}

Laravel: Filter by Category

And now, back to the back-end: we need to add a conditional clause to the Eloquent query where we get the posts.

app/Http/Controllers/Api/PostController.php:

use Illuminate\Database\Eloquent\Builder;
 
class PostController extends Controller
{
public function index()
{
$posts = Post::with('category')
->when(request('category'), function (Builder $query) {
$query->where('category_id', request('category'));
})
->paginate(10);
 
return PostResource::collection($posts);
}
}

Now the filter should work.


Fixing Pagination

But wait: the pagination is broken! For example, if you would change paginate to 2 records instead of 10 and select the category which has more posts than 2, you will see the pagination. But after clicking on the second page, the category wouldn't be passed and the pagination would break.

This can be seen clearly in the developer tools network tab.

broken pagination

Unfortunately, the fix isn't documented, but in one of the GitHub issues the author provided the solution. We need to pass the page as a parameter.

resources/js/components/Posts/Index.vue:

<template>
<div class="overflow-hidden overflow-x-auto p-6 bg-white border-gray-200">
<div class="min-w-full align-middle">
// ...
 
<TailwindPagination :data="posts" @pagination-change-page="getPosts" class="mt-4" />
<TailwindPagination :data="posts" @pagination-change-page="page => getPosts(page, selectedCategory)" class="mt-4" />
</div>
</div>
</template>
 
<script setup>
// ...
</script>

Now the pagination with the filter works as expected.

working pagination with filter

Previous: Category Dropdown: Second Composable
avatar

Really straightforward example of using Vue watchers.

avatar
You can use Markdown
avatar

Hello, I would like to make filter data without any relationship between Category and Post so I have PostController and I want to pass the query request into that

$posts = Post::with('category') ->when(request('category'), function (Builder $query) { $query->where('category_id', request('category')); })->paginate(10);

					can you help me please?
avatar
You can use Markdown
avatar

I want to make multi filter data what should I do :)?

avatar
You can use Markdown
avatar

Hi, sir , when I use where with the function Builder it highlight the where clause with redline but still working what is the problem of red underline . $posts = Post::with('category')->when(request('category'), function (Builder $builder){ $builder->where('category_id', request('category')); })->paginate(3);

avatar

Hard to tell why it underlines, might be your ide

avatar
You can use Markdown
avatar
You can use Markdown