We will do all Searching, sorting and pagination implementation by Spring Boot Restful API.
Let's start with creating a React App using create-react-app CLI.
To create a new app, you may choose one of the following methods:
Using npx
npx create-react-app react-appname
Using npm
npm init react-app react-appname
Install some npm packages:
npm install bootstrap reactstrap
npm install react-bootstrap bootstrap
npm install reactstrap react react-dom
npm install axios
npm install react-router-dom
npm install @material-ui/core
npm install @material-ui/lab
{
"name": "react-pagination-sorting-searching-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-free": "^6.4.0",
"@material-ui/core": "^4.12.4",
"@material-ui/lab": "^4.0.0-alpha.61",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.4.0",
"bootstrap": "^5.3.0",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.1",
"react-scripts": "5.0.1",
"react-table": "^7.8.0",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
--------------------------------app.js----------------------------------------
import React from "react";
import { Routes, Route ,Link} from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "@fortawesome/fontawesome-free/css/all.css";
import "@fortawesome/fontawesome-free/js/all.js";
import "./App.css";
import Portfolio from "./components/Portfolio";
import AddPortfolio from "./components/AddPortfolio";
import EditPortfolio from "./components/EditPortfolio";
function App() {
return (
<div>
<nav className="navbar navbar-expand navbar-dark bg-dark">
<a href="/" className="navbar-brand">
ramsis-code
</a>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/"} className="nav-link">
Fetch Portfolio
</Link>
</li>
<li className="nav-item">
<Link to={"/insert"} className="nav-link">
Insert
</Link>
</li>
</div>
</nav>
<div className="container mt-3">
<Routes>
<Route path='/' element={<Portfolio/>}></Route>
<Route path='/insert' element={<AddPortfolio/>}></Route>
<Route path='/edit/:id' element={<EditPortfolio/>}></Route>
</Routes>
</div>
</div>
);
}
export default App;
-------------------------------------AddPortfolio.js-------------------------------------
import React, { useState } from "react";
import PortfolioDataService from "../services/PortfolioService";
const AddPortfolio = () =>{
const initialPortfolioState = {
id: null,
name: "",
email: "",
department: "",
phone: "",
city: ""
};
const [portfolio, setPortfolio] = useState(initialPortfolioState);
const [submitted, setSubmitted] = useState(false);
const [msgName, setMsgName] = useState("");
const [msgEmail, setMsgEmail] = useState("");
const [msgDept, setMsgDept] = useState("");
const [msgCity, setMsgCity] = useState("");
const [msgPhone, setMsgPhone] = useState("");
const regexEmail = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;
const handleInputChange = event => {
const { name, value } = event.target;
setPortfolio({ ...portfolio, [name]: value });
};
const savePortfolio = () => {
var data = {
id: portfolio.id,
name: portfolio.name,
email: portfolio.email,
department: portfolio.department,
phone: portfolio.phone,
city: portfolio.city
};
if(data.name===''){
setMsgName("Name is Required.");
return false;
}else{
setMsgName('');
}
if(data.email===''){
setMsgEmail("Email is Required.");
return false;
}else if(!regexEmail.test(data.email)){
setMsgEmail("Invalid Email.");
return false;
} else{
setMsgEmail('');
}
if(data.department===''){
setMsgDept("Department is Required.");
return false;
}else{
setMsgDept('');
}
if(data.phone===''){
setMsgPhone("Phone is Required.");
return false;
}else{
setMsgPhone('');
}
if(data.city===''){
setMsgCity("City is Required.");
return false;
}else{
setMsgCity('');
}
PortfolioDataService.create(data)
.then(response => {
setPortfolio({
id: response.data.id,
name: response.data.name,
email: response.data.email,
department: response.data.department,
phone: response.data.phone,
city: response.data.city
});
setSubmitted(true);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const newPortfolio = () => {
setPortfolio(initialPortfolioState);
setSubmitted(false);
};
return (
<div className="submit-form">
{submitted ? (
<div>
<h5>You submitted successfully!</h5>
<button className="btn btn-success" onClick={newPortfolio}>
Add
</button>
</div>
) : (
<div>
<div className="form-group">
<label htmlFor="name">Name</label>
<input
type="text"
className="form-control"
id="name"
required
value={portfolio.name}
onChange={handleInputChange}
name="name"
/>
{msgName && <p style={{color: "red"}}>{msgName}</p>}
</div>
<div className="form-group">
<label htmlFor="email">email</label>
<input
type="text"
className="form-control"
id="email"
required
value={portfolio.email}
onChange={handleInputChange}
name="email"
/>
{msgEmail && <p style={{color: "red"}}>{msgEmail}</p>}
</div>
<div className="form-group">
<label htmlFor="department">department</label>
<input
type="text"
className="form-control"
id="department"
required
value={portfolio.department}
onChange={handleInputChange}
name="department"
/>
{msgDept && <p style={{color: "red"}}>{msgDept}</p>}
</div>
<div className="form-group">
<label htmlFor="phone">phone</label>
<input
type="text"
className="form-control"
id="phone"
required
value={portfolio.phone}
onChange={handleInputChange}
name="phone"
/>
{msgPhone && <p style={{color: "red"}}>{msgPhone}</p>}
</div>
<div className="form-group">
<label htmlFor="city">city</label>
<input
type="text"
className="form-control"
id="city"
required
value={portfolio.city}
onChange={handleInputChange}
name="city"
/>
{msgCity && <p style={{color: "red"}}>{msgCity}</p>}
</div>
<br/>
<button onClick={savePortfolio} className="btn btn-success">
Submit
</button>
</div>
)}
</div>
);
}
export default AddPortfolio
----------------------------------EditPortfolio.js ----------------------------------
import React, { useState, useEffect } from "react";
import PortfolioDataService from "../services/PortfolioService";
import { useParams } from "react-router-dom";
const EditPortfolio = () =>{
const {id} = useParams();
const initialPortfolioState = {
id: null,
name: "",
email: "",
department: "",
phone: "",
city: ""
};
const [portfolio, setPortfolio] = useState(initialPortfolioState);
const [submitted, setUpdate] = useState(false);
const [message, setMessage] = useState("");
const handleInputChange = event => {
const { name, value } = event.target;
setPortfolio({ ...portfolio, [name]: value });
};
const updatePortfolio = () => {
var data = {
id: portfolio.id,
name: portfolio.name,
email: portfolio.email,
department: portfolio.department,
phone: portfolio.phone,
city: portfolio.city
};
PortfolioDataService.update(data.id,data)
.then(response => {
setMessage("The tutorial was updated successfully!");
setUpdate(true);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const newPortfolio = () => {
setPortfolio(initialPortfolioState);
setUpdate(false);
};
const getPortfolio = (id) => {
PortfolioDataService.get(id)
.then(response => {
setMessage("The tutorial was updated successfully!");
setPortfolio(response.data);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
useEffect(() => {
getPortfolio(id);
}, [id]);
return (
<div className="submit-form">
{submitted ? (
<div>
<h6>Your form is updated successfully!</h6>
<button className="btn btn-success" onClick={newPortfolio}>
Add
</button>
</div>
) : (
<div>
<div className="form-group">
<label htmlFor="name">Name</label>
<input
type="text"
className="form-control"
id="name"
required
value={portfolio.name}
onChange={handleInputChange}
name="name"
/>
</div>
<div className="form-group">
<label htmlFor="email">email</label>
<input
type="text"
className="form-control"
id="email"
required
value={portfolio.email}
onChange={handleInputChange}
name="email"
/>
</div>
<div className="form-group">
<label htmlFor="department">department</label>
<input
type="text"
className="form-control"
id="department"
required
value={portfolio.department}
onChange={handleInputChange}
name="department"
/>
</div>
<div className="form-group">
<label htmlFor="phone">phone</label>
<input
type="text"
className="form-control"
id="phone"
required
value={portfolio.phone}
onChange={handleInputChange}
name="phone"
/>
</div>
<div className="form-group">
<label htmlFor="city">city</label>
<input
type="text"
className="form-control"
id="city"
required
value={portfolio.city}
onChange={handleInputChange}
name="city"
/>
</div>
<br/>
<button onClick={updatePortfolio} className="btn btn-success">
Update
</button>
</div>
)}
</div>
);
}
export default EditPortfolio;
---------------------------Portfolio.js--------------------------
import React, { useState, useEffect } from "react";
import Table from 'react-bootstrap/Table';
import Pagination from "@material-ui/lab/Pagination";
import PortfolioDataService from "../services/PortfolioService";
import {Link } from "react-router-dom";
const Portfolio = (props) => {
const [portfolios, setPortfolios] = useState([]);
const [searchName, setSearchName] = useState("");
const [page, setPage] = useState(1);
const [count, setCount] = useState(0);
const [pageSize, setPageSize] = useState(5);
const [pageSort, setPageSort] = useState("id,asc");
const pageSizes = [5, 8, 10];
const retrievePortfolios = () => {
const params = getRequestParams(searchName, page, pageSize, pageSort);
PortfolioDataService.getAll(params)
.then((response) => {
const { portfolios, totalPages } = response.data;
console.log(portfolios);
setPortfolios(portfolios);
setCount(totalPages);
console.log("param"+params);
console.log(response.data);
})
.catch((e) => {
console.log(e);
});
};
const getPortfolio = () => {
retrievePortfolios();
}
useEffect(retrievePortfolios, [page, pageSize,pageSort,searchName]);
const handlePageChange = (event, value) => {
setPage(value);
};
const deleteTutorial = (rowIndex) => {
PortfolioDataService.remove(rowIndex)
.then((response) => {
getPortfolio();
})
.catch((e) => {
console.log(e);
});
};
const handlePageSizeChange = (event) => {
setPageSize(event.target.value);
setPage(1);
};
const handlePageSortClick = (event,value) =>{
//alert(event.currentTarget.id);
const sdir="asc";
const sortString = event.currentTarget.id+","+sdir;
setPageSort(sortString);
retrievePortfolios();
}
const findByName = () => {
setPage(1);
retrievePortfolios();
};
const onChangeSearchName = (e) => {
const searchName = e.target.value;
setSearchName(searchName);
};
const getRequestParams = (searchName, page, pageSize, pageSort) => {
let params = {};
if (searchName) {
params["name"] = searchName;
}
if (page) {
params["page"] = page - 1;
}
if (pageSize) {
params["size"] = pageSize;
}
if (pageSort) {
params["sort"] = pageSort;
}
return params;
};
return (
<>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchName}
onChange={onChangeSearchName}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={findByName}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-12 list">
<div className="mt-6 my-3">
{"Rows per page: "}
<select onChange={handlePageSizeChange} value={pageSize}>
{pageSizes.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
</div>
<div className="col-md-12 list">
<Table table table-striped table-bordered>
<thead>
<th><Link id="id" onClick={handlePageSortClick}>Id</Link></th>
<th><Link id="name" onClick={handlePageSortClick}>Name</Link></th>
<th><Link id="email" onClick={handlePageSortClick}>Email</Link></th>
<th><Link id="department" onClick={handlePageSortClick}>Department</Link></th>
<th><Link id="phone" onClick={handlePageSortClick}>Phone</Link></th>
<th><Link id="city" onClick={handlePageSortClick}>City</Link></th>
<th><Link>Action</Link></th>
</thead>
<tbody>
{
portfolios.map((data,index)=>(
<tr>
<td>{data.id}</td>
<td>{data.name}</td>
<td>{data.email}</td>
<td>{data.department}</td>
<td>{data.phone}</td>
<td>{data.city}</td>
<td>
<Link to={"/edit/"+data.id} >
<i className="far fa-edit action fa-1x mx-2" style={{color: "green"}}></i>
</Link>
<span onClick={() => deleteTutorial(data.id)}>
<i className="fas fa-trash action fa-1x mx-2" style={{color: "red"}}></i>
</span>
</td>
</tr>
))
}
</tbody>
</Table>
</div>
</div>
<div className="mt-6">
<Pagination
className="my-2"
count={count}
page={page}
siblingCount={1}
boundaryCount={1}
variant="outlined"
shape="rounded"
onChange={handlePageChange}
/>
</div>
</div>
</>
);
};
export default Portfolio;
-------------------------PortfolioService.js-------------------------
import http from "../http-common";
const getAll = (params) => {
return http.get("/portfolios", { params });
};
const create = (data) => {
return http.post("/portfolios", data);
};
const remove = (id) => {
return http.delete(`/portfolios/${id}`);
};
const get = (id) => {
return http.get(`/portfolios/${id}`);
};
const update = (id, data) => {
return http.put(`/portfolios/${id}`, data);
};
const PortfolioService = {
getAll,
create,
remove,
get,
update,
};
export default PortfolioService;
-----------------------------http-common.js----------------------------
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:9090/api",
headers: {
"Content-type": "application/json"
}
});