Setelah kita berhasil membuat konfigurasi untuk menampilkan halaman users index, maka
sekarang kita akan lanjutkan untuk menampilkan data users-nya dihalaman tersebut. Kita juga akan belajar
untuk menambahkan beberapa fitur, yaitu seperti proses pencarian dan pagination untuk membagi
data yang ditampilkan per-halaman.
Langkah 1 - Menampilkan Data
Users
Sekarang kita akan belajar menampilkan data users yang ada di Laravel ke dalam React.js
menggunakan Rest API. Sebelum itu, pastikan backend Laravel-nya sudah dijalankan terlebih
dahulu.
Silahkan buka file pages/admin/users/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";
function UsersIndex() {
document.title = "Users - Administrator Travel GIS";
const [users, setUsers] = 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/users', {
headers: {
Authorization: `Bearer ${token}`,
}
}).then(response => {
setUsers(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-md-12">
<div className="card border-0 border-top-success rounded shadow-sm mb-5">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-users"></i> USERS</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">Full Name</th>
<th scope="col">Email Address</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{users.map((user, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td className="text-center">
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</LayoutAdmin>
</React.Fragment>
);
}
export default UsersIndex;
Dari perubahan kode di atas, pertama-tama kita import beberapa hook dari React, yaitu
useState dan useEffect.
import React, { useState, useEffect } from "react";
Setelah itu, kita import layout admin yang sudah pernah kita buat sebelumnya.
Layout inilah yang akan membungkus view/component.
import LayoutAdmin from "../../../layouts/Admin";
Karena akan melakukan HTTP Request ke dalam server, maka kita perlu
melakukan import konfigurasi Api yang sudah pernah kita buat sebelumnya.
import Api from "../../../api";
Dan karena endpoint yang akan kita akses bersifat private, maka kita butuh sebuah
token untuk proses verifikasinya dan disini kita akan import package
Js Cookie untuk mempermudah kita dalam mengelola token yang disimpan di dalam
cookies browser.
import Cookies from "js-cookie";
Di dalam function component UsersIndex, pertama kita membuat konfigurasi
title untuk halaman.
document.title = "Users - Administrator Travel GIS";
Setelah itu, kita membuat beberapa state yang nantinya akan kita gunakan untuk menampung data
yang didapatkan dari Rest API, seperti data users, current page,
per-page dan total.
const [users, setUsers] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
Kemudian kita buat variable baru dengan nama token, yang isinya adalah
token yang disimpan di dalam browser.
const token = Cookies.get("token");
Dan setelah itu kita buat sebuah function yang bernama fetchData, dimana didalamnya
kita akan gunakan untuk melakukan HTTP request ke dalam server
menggunakan Rest API.
const fetchData = async () => {
}
Di dalam function fetchData kita melakukan fetching ke dalam
endpoint /api/admin/users dan karena bersifat private, maka kita perlu
menambahkan sebuah headers dengan Authorization + token.
await Api.get('/api/admin/users', {
headers: {
Authorization: `Bearer ${token}`,
}
})
Jika dari proses fetching berhasil dilakukan, maka kita akan melakukan assign
response data ke dalam beberapa state.
setUsers(response.data.data.data);
setCurrentPage(response.data.data.current_page);
setPerPage(response.data.data.per_page);
setTotal(response.data.data.total);
Agar function fetchData dapat dijalankan pertama kali, maka kita perlu memanggilnya
di dalam hook useEffect.
//hook
useEffect(() => {
//call function "fetchData"
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Dan untuk menampilkan data di dalam JSX, kita bisa menggunakan perulangan dengan map.
{users.map((user, index) => (
))}
Sekarang jika halaman users index direload/direfresh. Maka kita akan
mendapatkan hasil seperti berikut ini :
Langkah 2 - Membuat Fitur
Pencarian
Setelah berhasil menampilkan data, sekarang kita akan lanjutkan untuk menambahkan fitur pencarian data.
Dan disini kita akan melakukan perubahan dengan menambahkan beberapa kode di dalam file yang
sama.
Silahkan buka file src/pages/admin/users/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";
function UsersIndex() {
document.title = "Users - Administrator Travel GIS";
const [users, setUsers] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
const token = Cookies.get("token");
const [search, setSearch] = useState("");
const fetchData = async (searchData) => {
const searchQuery = searchData ? searchData : search;
await Api.get(`/api/admin/users?q=${searchQuery}`, {
headers: {
Authorization: `Bearer ${token}`,
}
}).then(response => {
setUsers(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-md-12">
<div className="card border-0 border-top-success rounded shadow-sm mb-5">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
</div>
<div className="card-body">
<form onSubmit={searchHandlder} className="form-group">
<div className="input-group mb-3">
<Link to="/admin/users/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 user 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">Full Name</th>
<th scope="col">Email Address</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{users.map((user, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td className="text-center">
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</LayoutAdmin>
</React.Fragment>
);
}
export default UsersIndex;
Dari perubahan kode di atas, pertama kita import provider Link dari
React Router DOM. Ini akan digunakan untuk melakukan navigasi ke tambah user
nanti-nya.
import { Link } from "react-router-dom";
Setelah itu, kita tambahkan sebuah state baru yang bernama search. State
ini yang akan kita gunakan untuk menampung kata kunci yang diketikkan di dalam form
input.
const [search, setSearch] = useState("");
Kemudian di dalam function fetchData kita berikan parameter baru dengan
nama searchData. Dan di dalam function tersebut kita membuat variable
dengan nama searchQuery yang berisi kondisi dengan ternary operator.
//define variable "searchQuery"
const searchQuery = searchData ? searchData : search;
Dan kita gunakan variable tersebut sebagai parameter di dalam endpoint Rest
API.
await Api.get(`/api/admin/users?q=${searchQuery}`, {
}
Kemudian kita membuat function baru yang bernama serachHandler. Dimana
function tersebut akan dijalankan ketikan form pencarian disubmit.
<form onSubmit={searchHandlder} className="form-group">
//...
</form>
//function "searchHandler"
const searchHandlder = (e) => {
e.preventDefault();
//call function "fetchDataPost" with state search
fetchData(search)
}
Di dalam function searchHandler kita memanggil function
fetchData dengan parameter state search.
Sekarang silahkan teman-teman coba melakukan proses pencarian pada halaman users.
Langkah 3 - Membuat Fitur Pagination
Kita lanjutkan untuk menambahkan fitur pagination. Dimana kita akan menampilkan navigasi nomor
untuk berpindah-pindah antar halaman.
karena sebelumnya kita sudah pernah membuat component untuk pagination, maka kita akan
import lalu menggunakan component tersebut (reusable).
Silahkan buka file src/pages/admin/users/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 UsersIndex() {
document.title = "Users - Administrator Travel GIS";
const [users, setUsers] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
const token = Cookies.get("token");
const [search, setSearch] = useState("");
const fetchData = async (pageNumber, searchData) => {
const page = pageNumber ? pageNumber : currentPage;
const searchQuery = searchData ? searchData : search;
await Api.get(`/api/admin/users?q=${searchQuery}&page=${page}`, {
headers: {
Authorization: `Bearer ${token}`,
}
}).then(response => {
setUsers(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-md-12">
<div className="card border-0 border-top-success rounded shadow-sm mb-5">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
</div>
<div className="card-body">
<form onSubmit={searchHandlder} className="form-group">
<div className="input-group mb-3">
<Link to="/admin/users/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 user 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">Full Name</th>
<th scope="col">Email Address</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{users.map((user, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td>{user.name}</td>
<td>{user.email}</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 UsersIndex;
Dari perubahan kode di atas, pertama kita melakukan import component
PaginationComponent yang sudah kita buat sebelumnya.
import PaginationComponent from "../../../components/utilities/Pagination";
Setelah itu, di dalam function fetchData kita berikan parameter satu lagi
untuk page number.
const fetchData = async (pageNumber, searchData) => {
}
Dan di dalam function fetchData kita membuat variable yang bernama
page yang mana isinya adalah parameter diatas dengan kondisi ternary
operator.
//define variable "page"
const page = pageNumber ? pageNumber : currentPage;
Dan kita gunakan variable page tersebut sebagai parameter di dalam
endpoint Rest API.
await Api.get(`/api/admin/users?q=${searchQuery}&page=${page}`, {
}
Dan kita juga melakukan perubahan di dalam function searchHandler dengan
menambahkan paramter 1 sebagai default page saat proses pencarian.
//call function "fetchDataPost" with state search and page number
fetchData(1, search)
Kemudian untuk menampilkan pagination di dalam JSX, kita cukup memanggil component
PaginationComponent dengan menambahkan beberapa data props dari state.
<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
Terakhir, kita akan membuat fungsi untuk melakukan proses hapus data, dimana kita akan menambahkan satu
button yang mengarah ke dalam sebuah function dan di dalamnya kita melakukan
delete data ke dalam server menggunakan Rest API.
Sebelum data dihapus, kita juga akan menampilkan sebuah jendela konfirmasi, guna memastikan apakah kita
benar-benar ingin menghapus data tersebut atau tidak. Dan untuk membuat fitur tersebut, kita butuh
package tambahan yang bernama React Confirm Alert.
Silahkan buka file src/pages/admin/users/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 UsersIndex() {
document.title = "Users - Administrator Travel GIS";
const [users, setUsers] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [perPage, setPerPage] = useState(0);
const [total, setTotal] = useState(0);
const token = Cookies.get("token");
const [search, setSearch] = useState("");
const fetchData = async (pageNumber, searchData) => {
const page = pageNumber ? pageNumber : currentPage;
const searchQuery = searchData ? searchData : search;
await Api.get(`/api/admin/users?q=${searchQuery}&page=${page}`, {
headers: {
Authorization: `Bearer ${token}`,
}
}).then(response => {
setUsers(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 deleteUser = (id) => {
confirmAlert({
title: 'Are You Sure ?',
message: 'want to delete this data ?',
buttons: [{
label: 'YES',
onClick: async () => {
await Api.delete(`/api/admin/users/${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-md-12">
<div className="card border-0 border-top-success rounded shadow-sm mb-5">
<div className="card-header">
<span className="font-weight-bold"><i className="fa fa-users"></i> USERS</span>
</div>
<div className="card-body">
<form onSubmit={searchHandlder} className="form-group">
<div className="input-group mb-3">
<Link to="/admin/users/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 user 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">Full Name</th>
<th scope="col">Email Address</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{users.map((user, index) => (
<tr key={index}>
<td className="text-center">{++index + (currentPage-1) * perPage}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td className="text-center">
<button onClick={() => deleteUser(user.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 UsersIndex;
Dari perubahan kode di atas, pertama kita import React Hot Toast dan React Confirm
Alert terlebih dahulu.
import toast from "react-hot-toast";
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';
Setelah itu, kita tambahkan sebuah button delete di dalam perulangan data
users dan di dalam button tersebut kita berikan event onClick
yang mengarah ke dalam function deleteUser.
<button onClick={() => deleteUser(user.id)} className="btn btn-sm btn-danger"><i className="fa fa-trash"></i></button>
Dan di dalam function deleteUser, pertama kita akan menggunakan
confirmAlert untuk menampilkan jendela konfirmasi.
confirmAlert({
})
Di dalamnya kita berikan 2 button, yaitu YES dan NO. Jika
button YES yang di klik, maka akan menjalankan sebuah endpoint dengan
method DELETE ke dalam server.
await Api.delete(`/api/admin/users/${id}`, {
}
Jika proses hapus data berhasil dilakukan di dalam server, maka kita akan menampilkan notifikasi
menggunakan React Hot Toast dengan status success.
toast.success("Data Deleted Successfully!", {
duration: 4000,
position: "top-right",
style: {
borderRadius: '10px',
background: '#333',
color: '#fff',
},
});
Kemudian kita panggil method fetchData, agar data yang ditampilkan diperbarui
dengan data yang terbaru.
//call function "fetchData"
fetchData();
Tapi, jika button yang di klik adalah NO, maka tidak akan menjalankan perintah
apapun.
{
label: 'NO',
onClick: () => {}
}
Sekarang kita bisa melakukan reload halaman users index, jika berhasil maka
kita akan mendapatkan button delete yang kurang lebih seperti berikut ini :
Silahkan klik salah satu button delete yang ada, jika berhasil maka akan menampilkan
jendela konfirmasi yang kurang lebih seperti berikut ini :