article

Sunday, April 23, 2023

ReactJS Python Flask Pagination

ReactJS Python Flask Pagination

Python Flask 

https://flask.palletsprojects.com/en/2.2.x/installation/
 
Create an environment
C:\flask_dev>py -3 -m venv venv

Activate the environment
C:\flask_dev>venv\Scripts\activate

Install Flask
venv C:\flask_dev>pip install Flask
C:\flask_dev\flaskreact\app.py

Install requirements

Flask-SQLAlchemy
Flask-SQLAlchemy is an extension for Flask that adds support for SQLAlchemy to your application.
https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/

(venv) PS C:\flask_dev\flaskreact>pip install -U Flask-SQLAlchemy
C:\flask_dev\flaskreact\app.py

Flask + marshmallow for beautiful APIs
https://pypi.org/project/flask-marshmallow/

(venv) PS C:\flask_dev\flaskreact>pip install flask-marshmallow

Flask-Cors
A Flask extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible.
https://pypi.org/project/Flask-Cors/

(venv) PS C:\flask_dev\flaskreact>pip install -U flask-cors

C:\flask_dev\flaskreact\app.py

 
#C:\flask_dev\flaskreact\app.py
from flask import Flask, jsonify
from flask_cors import CORS #ModuleNotFoundError: No module named 'flask_cors' = pip install Flask-Cors
from flask_marshmallow import Marshmallow #ModuleNotFoundError: No module named 'flask_marshmallow' = pip install flask-marshmallow https://pypi.org/project/flask-marshmallow/

from models import db, Countries

app = Flask(__name__)
CORS(app, supports_credentials=True)

#app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///flaskdb.db'
# Databse configuration mysql                             Username:password@hostname/databasename
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:''@localhost/flaskreact'

db.init_app(app)
  
with app.app_context():
    db.create_all()

ma=Marshmallow(app)
 
class CountriesSchema(ma.Schema):
    class Meta:
        fields = ('ID','countryName')
         
countries_schema = CountriesSchema(many=True)

@app.route("/")
def hello_world():
    return "Hello, World!" 

@app.route('/listall',methods =['GET'])
def listall():
    all_countries = Countries.query.all()
    results = countries_schema.dump(all_countries)
    return jsonify(results)
C:\flask_dev\flaskreact\models.py
 
#C:\flask_dev\flaskreact\models.py
from flask_sqlalchemy import SQLAlchemy
   
db = SQLAlchemy()
   
class Countries(db.Model):
    __tablename__ = "countries"
    ID = db.Column(db.Integer, primary_key=True)
    countryName = db.Column(db.String(120), index=True, unique=True)
run (venv) C:\flask_dev\flaskreact>flask run
http://127.0.0.1:5000/listall

React JS
https://create-react-app.dev/

Create Project
C:\react-js>npx create-react-app myreactdev
Run
C:\react-js\myreactdev> npm start
C:\react-js\myreactdev\src\App.js

//C:\react-js\myreactdev\src\App.js
import React, { useState, useEffect } from 'react';
import Pagination from './Pagination'
import axios from 'axios'
import './App.css';
 
function App() {
  const [countries, setCountries] = useState([])
  const [loading, setLoading] = useState(false)
  const [currentPage, setCurrentPage] = useState(1)
  const [CountriesPerPage] = useState(10) //10 Per Page
 
  useEffect(() => {
    const fetchPosts = async () => {
      setLoading(true)
      const res = await axios.get('http://127.0.0.1:5000/listall')
      setCountries(res.data)
      setLoading(false)
    }
 
    fetchPosts()
  }, [])
 
  if (loading && countries.length === 0) {
    return <h2>Loading...</h2>
  }
  //Get current countries
  const indexOfLastPost = currentPage * CountriesPerPage;
  const indexOfFirstPost = indexOfLastPost - CountriesPerPage;
  const CountriesList = countries.slice(indexOfFirstPost, indexOfLastPost)
  const howManyPages = Math.ceil(countries.length/CountriesPerPage)
   
  return (
    <div className="container" style={{padding: 20}}>
      <h4 className="d-inline-block">ReactJS Python Flask Pagination</h4>
 
      <table className="table table-striped">
          <thead className="thead-light ">
            <th>ID</th>
            <th>Country</th>
          </thead>
          <tbody>
          {CountriesList.map(row => <tr>
            <td>{row.ID}</td>
              <td>{row.countryName}</td>
            </tr>)}
          </tbody>
        </table>
 
      <Pagination pages = {howManyPages} setCurrentPage={setCurrentPage}/>
    </div>
  );
}
 
export default App;
Install React Router Dom
https://www.npmjs.com/package/react-router-dom
C:\react-js\myreactdev>npm i react-router-dom --save

Install Axios
https://www.npmjs.com/package/axios
C:\react-js\myreactdev>npm install axios --save

C:\react-js\myreactdev\src\Pagination.js
//C:\react-js\myreactdev\src\Pagination.js
import React, { useState, useEffect } from 'react';
 
function Pagination({ pages = 7, setCurrentPage }) {
 
  //Set number of pages
  const numberOfPages = []
  for (let i = 1; i <= pages; i++) {
    numberOfPages.push(i)
  }
 
  // Current active button number
  const [currentButton, setCurrentButton] = useState(1)
 
  // Array of buttons what we see on the page
  const [arrOfCurrButtons, setArrOfCurrButtons] = useState([])
 
  useEffect(() => {
    let tempNumberOfPages = [...arrOfCurrButtons]
 
    let dotsInitial = '...'
    let dotsLeft = '... '
    let dotsRight = ' ...'
 
    if (numberOfPages.length < 6) {
      tempNumberOfPages = numberOfPages
    }
 
    else if (currentButton >= 1 && currentButton <= 3) {
      tempNumberOfPages = [1, 2, 3, 4, dotsInitial, numberOfPages.length]
    }
 
    else if (currentButton === 4) {
      const sliced = numberOfPages.slice(0, 5)
      tempNumberOfPages = [...sliced, dotsInitial, numberOfPages.length]
    }
 
    else if (currentButton > 4 && currentButton < numberOfPages.length - 2) {               
      const sliced1 = numberOfPages.slice(currentButton - 2, currentButton)               
      const sliced2 = numberOfPages.slice(currentButton, currentButton + 1)                
      tempNumberOfPages = ([1, dotsLeft, ...sliced1, ...sliced2, dotsRight, numberOfPages.length]) 
    }
     
    else if (currentButton > numberOfPages.length - 3) {                
      const sliced = numberOfPages.slice(numberOfPages.length - 4)       
      tempNumberOfPages = ([1, dotsLeft, ...sliced])                        
    }
     
    else if (currentButton === dotsInitial) {
      setCurrentButton(arrOfCurrButtons[arrOfCurrButtons.length-3] + 1) 
    }
    else if (currentButton === dotsRight) {
      setCurrentButton(arrOfCurrButtons[3] + 2)
    }
 
    else if (currentButton === dotsLeft) {
      setCurrentButton(arrOfCurrButtons[3] - 2)
    }
 
    setArrOfCurrButtons(tempNumberOfPages)
    setCurrentPage(currentButton)
  }, [currentButton])
 
 
  return (
    <div className="pagination-container">
      <a
        href="!#"
        className={`${currentButton === 1 ? 'disabled' : ''}`}
        onClick={() => setCurrentButton(prev => prev <= 1 ? prev : prev - 1)}
      > 
        Prev
      </a>
 
      {arrOfCurrButtons.map(((item, index) => {
        return <a
          href="!#"
          key={index}
          className={`${currentButton === item ? 'active' : ''}`}
          onClick={() => setCurrentButton(item)}
        >
          {item}
        </a>
      }))}
 
      <a
        href="!#"
        className={`${currentButton === numberOfPages.length ? 'disabled' : ''}`}
        onClick={() => setCurrentButton(prev => prev >= numberOfPages.length ? prev : prev + 1)}
      >
        Next
      </a>
    </div>
  );
}
export default Pagination
react-js\myreactdev\src\App.css
//
body {
  margin: 0;
  background-color: #ffffff;
}

.pagination-container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  font-weight: 500;
  font-size: 15px;
}
 
.pagination-container a {
  display: flex;
  justify-content: center;
  align-items: center;
  color: black;
  height: 40px;
  width: 40px;
  text-decoration: none;
  transition: background-color .2s;
  border: 1px solid #ddd;
  cursor: pointer;
   
}
 
.pagination-container a.active {
  background-color: #007bff;
  color: white;
  border: 1px solid #7cbddb;
}
 
.pagination-container a.disabled { 
  opacity: 0.2;
}
.pagination-container a:hover:not(.active) {background-color: rgb(238, 238, 238);}
react-js\myreactdev\public\index.html

//react-js\myreactdev\public\index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"/>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
Run C:\react-j\myreactdev>npm start
http://localhost:3000/

Related Post