In this article, I would like to share how to get data from API and implement useful features such as filtering, sorting, and pagination in Vue.js applications. These features are especially convenient when users need an easy way to browse items such as product listings or content libraries. I’ll walk you step-by-step through the process, from obtaining the data to adding interactive controls so you can apply these techniques in your own projects. Let’s take a closer look!
Get data
<template>
class="min-h-screen bg-neutral-200 py-4">
class="w-[1280px] mx-auto">
class="mt-1">
- v-for="product in products" :key="product.id">
{{ product.id }}. {{ product.title }} - ${{ product.price }} -
{{ product.category }}
v-if="!loading && products.length === 0">Nothing to see here
v-if="loading">Loading ...
v-if="error">{{ error }}
template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
}
const BASE_URL = 'https://fakestoreapi.com/products';
const products = ref<Product[]>([]);
const loading = ref<boolean>(false);
const error = ref<string | null>(null);
const fetchProducts = async () => {
loading.value = true;
try {
const response = await fetch(BASE_URL);
if (!response.ok) {
throw new Error(`HTTP error! status ${response.status}`);
}
products.value = await response.json();
} catch (err) {
error.value = err instanceof Error ? err.message : 'Unknown error occurred';
} finally {
loading.value = false;
}
};
onMounted(() => fetchProducts());
script>
Installed:
In Vue.js, onMounted
It is one of the life cycle hooks executed after the component is mounted to the DOM. Simply put, the code inside onMounted
The component is run after it is fully prepared and displayed on the screen.
In the above example, onMounted
used to call fetchProducts
Activates when the component is loaded. This function retrieves product information from the external API and stores it in the products variable. This ensures that the data is available when users view the page. It’s especially useful for initializing data, such as getting any dynamic information you need for a product listing, article, or page.
v-for:v-for
It is a command in Vue.js that is used to dynamically render elements based on data in an array or object. In the above example, v-for
Used to automatically generate product lists based on data obtained from the API.
Implement filtering, sorting and paging
<template>
class="min-h-screen bg-neutral-200 py-4">
class="w-[1280px] mx-auto">
class="flex gap-x-4 items-center">
v-model="searchTerm" type="search" placeholder="search..." />
class="flex gap-x-4 items-center mt-4">
Page {{ page }} of {{ totalPage }}
class="mt-1">
- v-for="product in limitedProducts" :key="product.id">
{{ product.id }}. {{ product.title }} - ${{ product.price }} -
{{ product.category }}
v-if="!loading && searchedProducts.length === 0">
Nothing to see here
v-if="loading">Loading ...
v-if="error">{{ error }}
template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
rate: number;
count: number;
};
}
const BASE_URL = 'https://fakestoreapi.com/products';
const products = ref<Product[]>([]);
const loading = ref<boolean>(false);
const error = ref<string | null>(null);
const orderBy = ref<string>('');
const category = ref<string>('');
const sortBy = ref<string>('');
const searchTerm = ref<string>('');
const limit = ref<string>('');
const page = ref<number>(1);
const totalPage = computed(() => {
const itemsPerPage = limit.value ? Number(limit.value) : 10;
return Math.ceil(searchedProducts.value.length / itemsPerPage);
});
const fetchProducts = async () => {
loading.value = true;
try {
const response = await fetch(BASE_URL);
if (!response.ok) {
throw new Error(`HTTP error! status ${response.status}`);
}
products.value = await response.json();
} catch (err) {
error.value = err instanceof Error ? err.message : 'Unknown error occurred';
} finally {
loading.value = false;
}
};
const orderByProducts = computed(() => {
const sortedProducts = [...products.value];
if (sortBy.value === 'title') {
return orderBy.value === 'desc'
? sortedProducts.sort((a, b) => b.title.localeCompare(a.title))
: sortedProducts.sort((a, b) => a.title.localeCompare(b.title));
} else if (sortBy.value === 'price') {
return orderBy.value === 'desc'
? sortedProducts.sort((a, b) => b.price - a.price)
: sortedProducts.sort((a, b) => a.price - b.price);
} else if (orderBy.value === 'desc') {
return sortedProducts.sort((a, b) => b.id - a.id);
}
return sortedProducts;
});
const filteredProducts = computed(() => {
if (category.value) {
return orderByProducts.value.filter(
(product) => product.category === category.value
);
}
return orderByProducts.value;
});
const searchedProducts = computed(() => {
if (searchTerm.value) {
return filteredProducts.value.filter((product) =>
product.title
.toLocaleLowerCase()
.includes(searchTerm.value.toLocaleLowerCase())
);
}
return filteredProducts.value;
});
const limitedProducts = computed(() => {
const itemsPerPage = limit.value ? Number(limit.value) : 10;
return searchedProducts.value.slice(
(page.value - 1) * itemsPerPage,
page.value * itemsPerPage
);
});
onMounted(() => fetchProducts());
script>
vModel:
this v-model
Directives in Vue support two-way data binding between form inputs and their associated reaction variables. This means that any changes in the input field immediately update the link variable, and changes to the variable are programmatically reflected in the input field. For example, in this implementation, v-model
and use searchTerm
, category
, sortBy
, orderBy
and limit
Input ensures that the user’s selections or entries dynamically update the application state.
calculate:
this computed
Properties are used to perform reactive calculations based on the application’s state. They allow efficient updates because they are only re-evaluated when their dependencies change. In this implementation, computed properties such as orderByProducts
, filteredProducts
, searchedProducts
and limitedProducts
Enable seamless filtering, sorting and pagination of product listings. Each computed property builds on the result of the previous computed property, ensuring that all operations are applied consistently and dynamically as state changes.
filter:
const filteredProducts = computed(() => {
if (category.value) {
return orderByProducts.value.filter(
(product) => product.category === category.value
);
}
return orderByProducts.value;
});
The filtering process checks whether a category is selected. If so, only products belonging to that category are included.
Sort by:
const orderByProducts = computed(() => {
const sortedProducts = [...products.value];
if (sortBy.value === 'title') {
return orderBy.value === 'desc'
? sortedProducts.sort((a, b) => b.title.localeCompare(a.title))
: sortedProducts.sort((a, b) => a.title.localeCompare(b.title));
} else if (sortBy.value === 'price') {
return orderBy.value === 'desc'
? sortedProducts.sort((a, b) => b.price - a.price)
: sortedProducts.sort((a, b) => a.price - b.price);
}
return sortedProducts;
});
The sorting logic uses JavaScript’s sort method:
- For the title it uses
localeCompare
Handles string comparisons. - For prices, it performs numerical sorting.
Pagination:
const limitedProducts = computed(() => {
const itemsPerPage = limit.value ? Number(limit.value) : 10;
return searchedProducts.value.slice(
(page.value - 1) * itemsPerPage,
page.value * itemsPerPage
);
});
The slicing method determines which products are displayed based on the current page and the selected restrictions. Example: if limit = 5
and page = 2
which displays products with index 5–9.
in conclusion
Thank you for taking the time to read this article. I hope this explanation gives you a clear understanding of how to effectively implement filtering, sorting, and pagination in Vue.js. By combining the power of v-model for data binding with computed properties for reactive updates, this approach ensures an efficient and dynamic user experience.
If you have any suggestions, questions, or feedback, please feel free to share! I’m always open to discussion and happy to assist.
GitHub: https://github.com/rfkyalf/vue-api-handling.git
I’m always happy to connect and collaborate on web development projects. You can learn more about my work and past projects by visiting my portfolio website: https://www.rifkyalfarez.my.id.