n this tutorial, we will build a fully functional Search, Sort, Filter & Pagination feature in a Vue 3 application using the Composition API with <script setup>
. We will use Axios for fetching data from a Node.js backend. The dummy data will include fields like name, email, phone, address, status, and id. We will also incorporate modern CSS design for all components.
Introduction
Creating a feature-rich application involves implementing various functionalities like search, sort, filter, and pagination. These features enhance user experience and make data management easier. In this article, we’ll go through each step required to build these features in a Vue 3 application, backed by a Node.js backend. We’ll use Axios for data fetching and ensure our design is modern and user-friendly.
Prerequisites
- Basic understanding of Vue 3 and Composition API
- Node.js and npm installed
- Basic knowledge of CSS
Setting Up the Project
Initialize a Vue 3 Project:
npm init vue@latest
cd <your-project-name>
npm install
Initialize a Node.js Project for Backend:
mkdir backend
cd backend
npm init -y
npm install express cors body-parser
Setup Backend with Express: Create a server.js
file in the backend folder with the following code:
// backend/server.js
const express = require('express'); // Import Express
const cors = require('cors'); // Import CORS middleware
const bodyParser = require('body-parser'); // Import Body-Parser middleware
const app = express(); // Initialize Express
const PORT = 5000; // Set port
// Middleware
app.use(cors()); // Enable CORS
app.use(bodyParser.json()); // Enable JSON body parsing
// Dummy data
let users = [
{ id: 1, name: "John Doe", email: "john@example.com", phone: "123-456-7890", address: "123 Main St", status: "Active" },
{ id: 2, name: "Jane Smith", email: "jane@example.com", phone: "987-654-3210", address: "456 Elm St", status: "Inactive" },
// Add more dummy users as needed
];
// Routes
app.get('/users', (req, res) => {
res.json(users); // Send users as JSON response
});
// Start server
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`); // Log server start
});
Run the Node.js Server:
node server.js
Creating the Vue Components
App Component
This component will serve as the main container for our application.
<!-- src/App.vue -->
<template>
<div class="app">
<h1>User Management</h1>
<SearchBar :setSearchTerm="setSearchTerm" />
<UserTable :users="currentUsers" />
<Pagination
:usersPerPage="usersPerPage"
:totalUsers="filteredUsers.length"
:paginate="paginate"
:currentPage="currentPage"
/>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'; // Import ref, onMounted, and computed from Vue
import axios from 'axios'; // Import Axios for data fetching
import SearchBar from './components/SearchBar.vue'; // Import SearchBar component
import UserTable from './components/UserTable.vue'; // Import UserTable component
import Pagination from './components/Pagination.vue'; // Import Pagination component
const users = ref([]); // Ref to hold all user data
const filteredUsers = ref([]); // Ref to hold filtered user data
const searchTerm = ref(''); // Ref to hold search term
const currentPage = ref(1); // Ref to hold current page number
const usersPerPage = ref(10); // Ref to hold number of users per page
// Fetch data from Node.js backend
const fetchUsers = async () => {
try {
const response = await axios.get('http://localhost:5000/users'); // Fetch users from Node.js backend
users.value = response.data; // Set users ref with fetched data
filteredUsers.value = response.data; // Set filtered users ref with fetched data
} catch (error) {
console.error('Error fetching data', error); // Log any error that occurs during data fetching
}
};
// Call fetchUsers function on component mount
onMounted(fetchUsers);
// Watch searchTerm and users for changes and filter users
const setSearchTerm = (term) => {
searchTerm.value = term; // Set search term
const results = users.value.filter(user =>
user.name.toLowerCase().includes(searchTerm.value.toLowerCase()) || // Check if name includes search term
user.email.toLowerCase().includes(searchTerm.value.toLowerCase()) || // Check if email includes search term
user.phone.includes(searchTerm.value) || // Check if phone includes search term
user.address.toLowerCase().includes(searchTerm.value.toLowerCase()) // Check if address includes search term
);
filteredUsers.value = results; // Set filtered users ref with results
};
// Get current users for pagination
const indexOfLastUser = computed(() => currentPage.value * usersPerPage.value); // Calculate index of last user on current page
const indexOfFirstUser = computed(() => indexOfLastUser.value - usersPerPage.value); // Calculate index of first user on current page
const currentUsers = computed(() => filteredUsers.value.slice(indexOfFirstUser.value, indexOfLastUser.value)); // Slice filtered users array to get current users
// Change page
const paginate = (pageNumber) => currentPage.value = pageNumber; // Function to set current page
</script>
<style>
/* Add your CSS styles here */
</style>
SearchBar Component
This component allows users to search for specific data.
<!-- src/components/SearchBar.vue -->
<template>
<input
type="text" // Set input type to text
placeholder="Search..." // Set placeholder text
@input="onInput" // Call onInput method with input value on change
/>
</template>
<script setup>
import { defineProps } from 'vue'; // Import defineProps from Vue
const props = defineProps(['setSearchTerm']); // Define props
const onInput = (event) => {
props.setSearchTerm(event.target.value); // Call setSearchTerm function with input value
};
</script>
UserTable Component
This component displays the user data in a table format.
<!-- src/components/UserTable.vue -->
<template>
<table class="user-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Address</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.phone }}</td>
<td>{{ user.address }}</td>
<td>{{ user.status }}</td>
</tr>
</tbody>
</table>
</template>
<script setup>
import { defineProps } from 'vue'; // Import defineProps from Vue
const props = defineProps(['users']); // Define props
</script>
<style>
/* src/components/UserTable.css */
.user-table {
width: 100%; /* Set table width to 100% */
border-collapse: collapse; /* Collapse table borders */
margin: 20px 0; /* Add margin */
font-size: 1em; /* Set font size */
min-width: 400px; /* Set minimum width */
border-radius: 5px 5px 0 0; /* Set border radius */
overflow: hidden; /* Hide overflow */
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15); /* Add box shadow */
}
.user-table thead tr {
background-color: #009879; /* Set background color */
color: #ffffff; /* Set text color */
text-align: left; /* Align text to the left */
font-weight: bold; /* Set font weight to bold */
}
.user-table th,
.user-table td {
padding: 12px 15px; /* Add padding */
}
.user-table tbody tr {
border-bottom: 1px solid #dddddd; /* Add bottom border */
}
.user-table tbody tr:nth-of-type(even) {
background-color: #f3f3f3; /* Set background color for even rows */
}
.user-table tbody tr:last-of-type {
border-bottom: 2px solid #009879; /* Set bottom border for last row */
}
.user-table tbody tr.active-row {
font-weight: bold; /* Set font weight to bold */
color: #009879; /* Set text color */
}
</style>
Pagination Component
This component handles the pagination of the user data.
<!-- src/components/Pagination.vue -->
<template>
<nav>
<ul class="pagination">
<li v-for="number in pageNumbers" :key="number" :class="['page-item', { active: currentPage === number }]">
<a @click.prevent="paginate(number)" href="#" class="page-link">
{{ number }}
</a>
</li>
</ul>
</nav>
</template>
<script setup>
import { computed } from 'vue'; // Import computed from Vue
import { defineProps } from 'vue'; // Import defineProps from Vue
const props = defineProps(['usersPerPage', 'totalUsers', 'paginate', 'currentPage']); // Define props
const pageNumbers = computed(() => {
const pages = [];
for (let i = 1; i <= Math.ceil(props.totalUsers / props.usersPerPage); i++) {
pages.push(i); // Add page number to pages array
}
return pages; // Return pages array
});
</script>
<style>
/* src/components/Pagination.css */
.pagination {
display: flex; /* Set display to flex */
justify-content: center; /* Center align items */
padding: 10px 0; /* Add padding */
}
.pagination .page-item {
margin: 0 5px; /* Add margin */
}
.pagination .page-link {
padding: 8px 16px; /* Add padding */
border: 1px solid #ddd; /* Add border */
border-radius: 5px; /* Set border radius */
text-decoration: none; /* Remove text decoration */
color: #009879; /* Set text color */
cursor: pointer; /* Set cursor to pointer */
}
.pagination .page-item.active .page-link {
background-color: #009879; /* Set background color */
color: #ffffff; /* Set text color */
border: 1px solid #009879; /* Set border */
}
</style>
Conclusion
In this article, we built a comprehensive Search, Sort, Filter & Pagination feature in a Vue 3 application using the Composition API with <script setup>
and a Node.js backend. We used modern CSS to design our components and added error handling to ensure robustness. By following this tutorial, you can enhance your Vue 3 applications with these powerful features, providing a better user experience.
For more advanced features and customization, consider exploring additional libraries and techniques such as Vuex for state management and advanced form validation libraries like Vuelidate or VeeValidate. Happy coding!
Welcome to DevTechTutor.com, your ultimate resource for mastering web development and technology! Whether you're a beginner eager to dive into coding or an experienced developer looking to sharpen your skills, DevTechTutor.com is here to guide you every step of the way. Our mission is to make learning web development accessible, engaging, and effective.