Pada materi kali ini kita akan belajar bersama-sama bagaiaman cara menampilkan data categories
dari Rest API. Tidak hanya menampilkan data saja, kita juga akan belajar bagaimana cara membuat fitur
pencarian, pagination dan delete data.
Langkah 1 - Menampilkan Data Categories
Pertama, kita akan belajar menampilkan data categories terlebih dahulu. Dan disini kita akan
melakukan penambahan kode di dalam file yang sudah pernah kita buat sebelumnya. Silahkan buka
file src/pages/admin/categories/Index.jsx, kemudian ubah kode-nya menjadi seperti
berikut ini :
import React, { useState, useEffect } from "react";
import LayoutAdmin from "../../../layouts/Admin";
import Api from "../../../api";
import Cookies from "js-cookie";
function CategoriesIndex() {
document.title = "Categories - Administrator Travel GIS";
const [categories, setCategories] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
const token = Cookies.get("token");
const fetchData = async () => {
await Api.get('/api/admin/categories', {
headers: {
Authorization: `Bearer ${token}`,
}
})
.then(response => {
setCategories(response.data.data.data);
setCurrentPage(response.data.data.current_page);
setPerPage(response.data.data.per_page);
setTotal(response.data.data.total);
});
};
useEffect(() => {
fetchData();
}, []);
return(
<React.Fragment>
<LayoutAdmin>
<div className="row mt-4">
<div className="col-12">
<div className="card border-0 rounded shadow-sm border-top-success">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-folder"></i> CATEGORIES</span>
</div>
<div className="card-body">
<div className="table-responsive">
<table className="table table-bordered table-striped table-hovered">
<thead>
<tr>
<th scope="col">No.</th>
<th scope="col">Image</th>
<th scope="col">Category Name</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{categories.map((category, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td className="text-center">
<img src={category.image} alt="" width="50" />
</td>
<td>{category.name}</td>
<td className="text-center"></td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</LayoutAdmin>
</React.Fragment>
)
}
export default CategoriesIndex
Dari perubahan kode di atas, pertama kita import 2 hook dari react, yaitu
useState dan useEffect.
import React, { useState, useEffect } from "react";
Karena akan melakukan HTTP request, maka kita akan import global
endpoint yang sudah pernah kita buat.
import Api from "../../../api";
Dan kita juga import package Js Cookie, karena kita akan gunakan untuk
mengambil data token dari cookies browser.
import Cookies from "js-cookie";
Dan di dalam function component CategoriesIndex, pertama kita atur
title untuk halaman categories index.
document.title = "Categories - Administrator Travel GIS";
Setelah itu, kita menambahkan 4 state baru, yaitu :
const [categories, setCategories] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
State categories akan digunakan untuk menyimpan response data
list categories. State currentPage digunakan untuk menyimpan
nilai pagination yang sedang aktif. State perPage merupakan nilai jumlah
data yang ditampilkan perhalaman dan untuk State total merupakan nilai
total dari data categories.
Setelah itu, kita buat 1 variable lagi dengan nama token yang mana isinya adalah
token yang ada di dalam cookies browser.
const token = Cookies.get("token");
Kemudian kita buat function yang bernama fetchData dengan jenis
asynchronus. Method ini akan kita gunakan untuk fetching data ke dalam Rest API.
const fetchData = async () => {
}
Di dalam function fetchDara, pertama-tama kita melakukan fetching ke dalam
endpoint /api/admin/categories dengan mengirimkan sebuah headers
Authorization yang berisi Berare + Token.
await Api.get('/api/admin/categories', {
headers: {
Authorization: `Bearer ${token}`,
}
})
Jika proses fetching berhasil dilakukan, maka kita akan assign response data
dari Rest API ke dalam state yang sudah kita buat di atas.
.then(response => {
setCategories(response.data.data.data);
setCurrentPage(response.data.data.current_page);
setPerPage(response.data.data.per_page);
setTotal(response.data.data.total);
});
Dan agar function fetchData dapat dijalankan saat component
diload, maka kita perlu memanggilnya di dalam hook useEffect.
//hook
useEffect(() => {
//call function "fetchData"
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Dan sekarang kita tinggal menampilkan datanya di dalam JSX. Kurang lebih seperti berikut ini :
{categories.map((category, index) => (
))}
Di atas, kita melakukan perulangan state categories menggunakan function
bawaan dari Javascript, yaitu map.
Sekarang, jika kita reload/refresh halaman categoris, maka kita akan
mendapatkan hasil kurang lebih seperti berikut ini :
CATATAN : jika belum muncul data apapun, silahkan periksa di dalam database
dan pastikan sudah memiliki data categories.
Langkah 2 - Membuat Fitur
Pencarian
Setelah berhasil menampilkan data categories, sekarang kita lanjutkan untuk menambahkan fitur
pencarian data. Silahkan buka file src/pages/admin/categories/Index.jsx, kemudian
ubah kode-nya menjadi seperti berikut ini :
import React, { useState, useEffect } from "react";
import LayoutAdmin from "../../../layouts/Admin";
import Api from "../../../api";
import Cookies from "js-cookie";
import { Link } from "react-router-dom";
function CategoriesIndex() {
document.title = "Categories - Administrator Travel GIS";
const [categories, setCategories] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
const [search, setSearch] = useState("");
const token = Cookies.get("token");
const fetchData = async (searchData) => {
const searchQuery = searchData ? searchData : search;
await Api.get(`/api/admin/categories?q=${searchQuery}`, {
headers: {
Authorization: `Bearer ${token}`,
}
})
.then(response => {
setCategories(response.data.data.data);
setCurrentPage(response.data.data.current_page);
setPerPage(response.data.data.per_page);
setTotal(response.data.data.total);
});
};
useEffect(() => {
fetchData();
}, []);
const searchHandlder = (e) => {
e.preventDefault();
fetchData(search)
}
return(
<React.Fragment>
<LayoutAdmin>
<div className="row mt-4">
<div className="col-12">
<div className="card border-0 rounded shadow-sm border-top-success">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-folder"></i> CATEGORIES</span>
</div>
<div className="card-body">
<form onSubmit={searchHandlder} className="form-group">
<div className="input-group mb-3">
<Link to="/admin/categories/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
<input type="text" className="form-control" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="search by category name" />
<button type="submit" className="btn btn-md btn-success"><i className="fa fa-search"></i> SEARCH</button>
</div>
</form>
<div className="table-responsive">
<table className="table table-bordered table-striped table-hovered">
<thead>
<tr>
<th scope="col">No.</th>
<th scope="col">Image</th>
<th scope="col">Category Name</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{categories.map((category, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td className="text-center">
<img src={category.image} alt="" width="50" />
</td>
<td>{category.name}</td>
<td className="text-center"></td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</LayoutAdmin>
</React.Fragment>
)
}
export default CategoriesIndex
Dari perubahan kode di atas, pertama kita import provider Link dari React
Router DOM. Ini akan kita gunakan untuk melakukan navigasi ke tambah data category.
import { Link } from "react-router-dom";
Setelah itu, kita buat 1 state baru dengan nama search.
const [search, setSearch] = useState("");
Kemudian kita melakukan sedikit perubahan di dalam function fetchData, dimana kita
menambahkan sebuah parameter searchData. Parameter tersebut akan bernilai
dinamis sesuai dengan kata kunci yang diinputkan.
const fetchData = async (searchData) => {
}
Di dalam function fetchData, kita membuat variable baru dengan nama
searchQuery yang isinya adalah sebuah kondisi menggunakan ternary
oprator.
Jika searchData memiliki value, maka akan diambil dari searchData itu
sendiri, tapi jika kosong maka akan mengambil dari nilai state search.
Setelah itu, kita buat varibale searchQuery tersebut menjadi parameter
untuk melakukan fetching data ke dalam Rest API.
await Api.get(`/api/admin/categories?q=${searchQuery}`, {
}
Kemudian kita buat function yang bernama searchHanlder, dimana fungsi ini akan
dijalankan ketika form pencarian di dalam JSX di submit.
<form onSubmit={searchHandlder} className="form-group">
//...
</form>
Ketika form di atas disubmit, maka akan menjalankan function
searchHanlder.
//function "searchHandler"
const searchHandlder = (e) => {
e.preventDefault();
//call function "fetchDataPost" with params
fetchData(search)
}
Di dalam function searchHandler kita memanggil function
fetchData dengan memberikan parameter state search dan
state tersebut akan berisi kata kunci dari input form.
Sekarang, jika kita coba melakukan proses pencarian, maka kurang lebih hasilnya seperti berikut ini :
(https://cdn.jsdelivr.net/gh/maulayyacyber/assets-images-ebooks/wisata-gis-react-laravel-10/Categories
- Administrator Travel GIS-categories-search.gif)
Langkah 3 - Membuat Fitur Pagination
Sekarang kita lanjutkan untuk membuat fitur pagination untuk membatasi data yang ditampilkan
per-halaman. Dimana kita akan memanfaatkan component yang sudah kita buat sebelumnya.
Silahkan buka file pages/admin/categories/Index.jsx, kemudian ubah semua kode-nya
menjadi seperti berikut ini :
import React, { useState, useEffect } from "react";
import LayoutAdmin from "../../../layouts/Admin";
import Api from "../../../api";
import Cookies from "js-cookie";
import { Link } from "react-router-dom";
import PaginationComponent from "../../../components/utilities/Pagination";
function CategoriesIndex() {
document.title = "Categories - Administrator Travel GIS";
const [categories, setCategories] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
const [search, setSearch] = useState("");
const token = Cookies.get("token");
const fetchData = async (pageNumber, searchData) => {
const page = pageNumber ? pageNumber : currentPage;
const searchQuery = searchData ? searchData : search;
await Api.get(`/api/admin/categories?q=${searchQuery}&page=${page}`, {
headers: {
Authorization: `Bearer ${token}`,
}
})
.then(response => {
setCategories(response.data.data.data);
setCurrentPage(response.data.data.current_page);
setPerPage(response.data.data.per_page);
setTotal(response.data.data.total);
});
};
useEffect(() => {
fetchData();
}, []);
const searchHandlder = (e) => {
e.preventDefault();
fetchData(1, search)
}
return(
<React.Fragment>
<LayoutAdmin>
<div className="row mt-4">
<div className="col-12">
<div className="card border-0 rounded shadow-sm border-top-success">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-folder"></i> CATEGORIES</span>
</div>
<div className="card-body">
<form onSubmit={searchHandlder} className="form-group">
<div className="input-group mb-3">
<Link to="/admin/categories/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
<input type="text" className="form-control" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="search by category name" />
<button type="submit" className="btn btn-md btn-success"><i className="fa fa-search"></i> SEARCH</button>
</div>
</form>
<div className="table-responsive">
<table className="table table-bordered table-striped table-hovered">
<thead>
<tr>
<th scope="col">No.</th>
<th scope="col">Image</th>
<th scope="col">Category Name</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{categories.map((category, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td className="text-center">
<img src={category.image} alt="" width="50" />
</td>
<td>{category.name}</td>
<td className="text-center"></td>
</tr>
))}
</tbody>
</table>
<PaginationComponent
currentPage={currentPage}
perPage={perPage}
total={total}
onChange={(pageNumber) => fetchData(pageNumber)}
position="end"
/>
</div>
</div>
</div>
</div>
</div>
</LayoutAdmin>
</React.Fragment>
)
}
export default CategoriesIndex
Dari perubahan kode di atas, pertama kita import component Pagination yang
sudah pernah kita buat sebelumnya.
import PaginationComponent from "../../../components/utilities/Pagination";
Setelah itu, kita melakukan perubahan di dalam function fetchData, dimana kita
tambahkan menjadi 2 parameter. Parameter pertama untuk pagination dan
parameter kedua untuk pencarian.
const fetchData = async (pageNumber, searchData) => {
}
Di dalam function fetchData kita juga menambahkan 1 variable baru dengan
nama page, variable berisi nilai dari parameter atau state
currentPage.
Dan variable page tersebut kita gunakan sebagai parameter di dalam proses
fetching ke Rest API.
await Api.get(`/api/admin/categories?q=${searchQuery}&page=${page}`, {
}
Dan di dalam function searchHandler kita juga melakukan sedikit perubahan, dimana
saat memanggil function fetchData kita berikan 2 parameter, yaitu untuk
pagination dengan default 1 dan state search.
//call function "fetchDataPost" with params
fetchData(1, search)
kemudian untuk menampilkan pagination, kita cukup seperti berikut ini :
<PaginationComponent
currentPage={currentPage}
perPage={perPage}
total={total}
onChange={(pageNumber) => fetchData(pageNumber)}
position="end"
/>
Di atas, kita mengirimkan beberapa data sebagai props, yaitu currentPage,
perPage, total, onChange dan position.
Sekarang, jika kita reload/refresh halaman-nya, maka kita akan mendapatkan hasil kurang
lebih seperti berikut ini :
Langkah 4 - Membuat Proses Hapus Data
Sekarang kita lanjutkan untuk membuat fitur hapus data category. Dimana sebelum data dihapus
kita akan menampilkan sebuah jendela konfirmasi, apakah yakin data akan benar-benar dihapus atau tidak.
Dan kita akan menggunakan package tambahan untuk menampilkan jendela konfirmasi tersebut, yaitu
React Confirm
Alert.
Silahkan buka file pages/admin/categories/Index.jsx, kemudian ubah semua kode-nya
menjadi seperti berikut ini :
import React, { useState, useEffect } from "react";
import LayoutAdmin from "../../../layouts/Admin";
import Api from "../../../api";
import Cookies from "js-cookie";
import { Link } from "react-router-dom";
import PaginationComponent from "../../../components/utilities/Pagination";
import toast from "react-hot-toast";
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';
function CategoriesIndex() {
document.title = "Categories - Administrator Travel GIS";
const [categories, setCategories] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
const [search, setSearch] = useState("");
const token = Cookies.get("token");
const fetchData = async (pageNumber, searchData) => {
const page = pageNumber ? pageNumber : currentPage;
const searchQuery = searchData ? searchData : search;
await Api.get(`/api/admin/categories?q=${searchQuery}&page=${page}`, {
headers: {
Authorization: `Bearer ${token}`,
}
})
.then(response => {
setCategories(response.data.data.data);
setCurrentPage(response.data.data.current_page);
setPerPage(response.data.data.per_page);
setTotal(response.data.data.total);
});
};
useEffect(() => {
fetchData();
}, []);
const searchHandlder = (e) => {
e.preventDefault();
fetchData(1, search)
}
const deleteCategory = (id) => {
confirmAlert({
title: 'Are You Sure ?',
message: 'want to delete this data ?',
buttons: [{
label: 'YES',
onClick: async () => {
await Api.delete(`/api/admin/categories/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
}
})
.then(() => {
toast.success("Data Deleted Successfully!", {
duration: 4000,
position: "top-right",
style: {
borderRadius: '10px',
background: '#333',
color: '#fff',
},
});
fetchData();
})
}
},
{
label: 'NO',
onClick: () => {}
}
]
});
}
return(
<React.Fragment>
<LayoutAdmin>
<div className="row mt-4">
<div className="col-12">
<div className="card border-0 rounded shadow-sm border-top-success">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-folder"></i> CATEGORIES</span>
</div>
<div className="card-body">
<form onSubmit={searchHandlder} className="form-group">
<div className="input-group mb-3">
<Link to="/admin/categories/create" className="btn btn-md btn-success"><i className="fa fa-plus-circle"></i> ADD NEW</Link>
<input type="text" className="form-control" value={search} onChange={(e) => setSearch(e.target.value)} placeholder="search by category name" />
<button type="submit" className="btn btn-md btn-success"><i className="fa fa-search"></i> SEARCH</button>
</div>
</form>
<div className="table-responsive">
<table className="table table-bordered table-striped table-hovered">
<thead>
<tr>
<th scope="col">No.</th>
<th scope="col">Image</th>
<th scope="col">Category Name</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{categories.map((category, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td className="text-center">
<img src={category.image} alt="" width="50" />
</td>
<td>{category.name}</td>
<td className="text-center">
<button onClick={() => deleteCategory(category.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>
</td>
</tr>
))}
</tbody>
</table>
<PaginationComponent
currentPage={currentPage}
perPage={perPage}
total={total}
onChange={(pageNumber) => fetchData(pageNumber)}
position="end"
/>
</div>
</div>
</div>
</div>
</div>
</LayoutAdmin>
</React.Fragment>
)
}
export default CategoriesIndex
Dari perubahan kode yang kita lakukan di atas, pertama kita import React Hot
Toast, karena kita akan tampilkan sebuah notifikasi setelah proses delete berhasil
dilakukan.
import toast from "react-hot-toast";
Setelah itu, kita import component dan CSS dari React Confirm Alert.
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';
Jika kita perhatikan, di dalam JSX, kita menambahkan 1 button baru untuk proses delete
data category.
<button onClick={() => deleteCategory(category.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>
Button di atas kita berikan event onClick yang mengarah ke dalam
function yang bernama deleteCategory dan di dalam function tersebut kita
berikan parameter berupa ID dari category.
Jika button di atas diklik, maka akan menjalankan function yang bernama
deleteCategory. Kurang lebih seperti berikut ini :
//function "deleteCategory"
const deleteCategory = (id) => {
//...
}
Di dalam function di atas, pertama kita membuat jendela konfirmasi menggunakan React
Confirm Alert.
confirmAlert({
});
Di dalam confirm alert, kita memberikan 2 button untuk opsinya, yaitu YES
dan NO.
Jika button YES di klik, maka akan menjalankan sebuah HTTP
request ke endpoint /api/admin/categories/:id dengan method
DELETE.
await Api.delete(`/api/admin/categories/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
}
})
Dan jika proses delete berhasil dilakukan di dalam server, maka kita akan tampilkan
notifikasi menggunakan React Hot Toast.
toast.success("Data Deleted Successfully!", {
duration: 4000,
position: "top-right",
style: {
borderRadius: '10px',
background: '#333',
color: '#fff',
},
});
Kemudian kita lakukan fetching lagi dengan cara memanggil function
fetchData. Dengan tujuan data diperbarui setelah proses delete.
//call function "fetchData"
fetchData();
Dan jika button NO diklik, kita cukup memberikan sebuah function kosong di
dalamnya.
onClick: () => {}
Sekarang jika kita coba reload/refresh halaman categories, maka kita akan
mendapatkan 1 button baru untuk delete data. Kurang lebih seperti berikut ini :
Jika kita klik button delete tersebut, maka akan menampilkan jendela konfirmasi kurang lebih
seperti berikut ini :