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.
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>
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 }}
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.
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.
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.