Courses

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

Category Dropdown: Second Composable

Summary of this lesson:
- Creating category composable for reusability
- Setting up categories API endpoint
- Building dropdown component for filtering
- Implementing category selection interface

In this lesson, we will create a dropdown select to pick from a list of categories. For this, we will create a new composable and a new API endpoint with the API Resource.

categories select list


Let's start this lesson by creating a Controller with the API route.

php artisan make:controller Api/CategoryController
php artisan make:resource CategoryResource

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

use App\Http\Resources\CategoryResource;
 
class CategoryController extends Controller
{
public function index()
{
return CategoryResource::collection(Category::all());
}
}

routes/api.php:

use App\Http\Controllers\Api\CategoryController;
 
Route::get('posts', [PostController::class, 'index']);
Route::get('categories', [CategoryController::class, 'index']);

In the Resource, we will only add the id and name fields.

app/Http/Resources/CategoryResource.php:

class CategoryResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
];
}
}

Now that we have an API endpoint, we can create a new Composable and use it to get the categories. The composable for the categories is going to be almost identical to the one that we have for the posts.

resources/js/composables/categories.js:

import { ref } from 'vue'
 
export default function useCategories() {
const categories = ref({})
 
const getCategories = async () => {
axios.get('/api/categories')
.then(response => {
categories.value = response.data.data;
})
}
 
return { categories, getCategories }
}

Next, we need to add categories Composable to the PostsIndex Vue component and show all the categories in the select input above the table.

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">
<div class="mb-4">
<select v-model="selectedCategory" class="block mt-1 w-full sm:w-1/4 rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
<option value="" selected>-- Filter by category --</option>
<option v-for="category in categories" :value="category.id" :key="category.id">
{{ category.name }}
</option>
</select>
</div>
 
// ...
</div>
</div>
</template>
 
<script setup>
import { onMounted, ref } from "vue";
import { TailwindPagination } from 'laravel-vue-pagination';
import usePosts from "@/composables/posts";
import useCategories from "@/composables/categories";
 
const selectedCategory = ref('')
const { posts, getPosts } = usePosts()
const { categories, getCategories } = useCategories()
 
onMounted(() => {
getPosts()
getCategories()
})
</script>

After visiting the page you should see the select list.

categories select list

Previous: Category: API Resource with Relationship
avatar

You have added this line as a new line const 'selectedCategory = ref('')'... And you missed highlighting it... I assume povillas did not do that mistake, may be some of his team member did... But please keep in mind for a new learner like me, it becomes so much annoying... Please do not ignore these little things

avatar

Thanks for the report. Don't forget that we are all humans and make mistakes :) look at it this way, you get an error or something doesn't work then you debug, it's a learning process! ;) good luck learning.

avatar

Yes, in fact I was thinking that maybe all the things you dont mention in the course is to force us find the solution ourselves so we can grasp better the key concepts and little traps of the development process. Thanks guys. Great work.

avatar
You can use Markdown
avatar

Could you please help with this error Uncaught (in promise) ReferenceError: ref is not defined

Sorry :) I miss this part " import { onMounted, ref } from "vue"; "

👍 1
avatar
You can use Markdown
avatar
You can use Markdown