Prerequisites: All past steps
Acum ca avem un endpoint configurat, il putem conecta la o interfata pentru utilizatorii de pe internet.
1. Setups:
In folderul /js adaugam folderul /components, pe acelasi nivel cu folderul /utils
In folderul /components cream fisierele MainPage.jsx si InsertPage.jsx si initializam un function component pt fiecare
Copy // js/components/MainPage.jsx
export default function MainPage() {
return (
<div></div>
)
}
Copy // js/components/InsertPage.jsx
export default function InsertPage() {
return (
<div></div>
)
}
In folderul /pages, vom modifica index.js si vom adauga componenta insert.jsx. In ele vom importa componentele definite mai sus pentru a fi desenate in pagina pe rutele /, respectiv /insert din aplicatia noastra web
Copy // pages/index.js
import MainPage from "@/js/components/MainPage";
export default function Home() {
return (
<MainPage/>
)
}
Copy // pages/insert.js
import InsertPage from "@/js/components/InsertPage";
export default function Insert() {
return (
<InsertPage/>
)
}
In fisierul tailwind.config.js mai adaugam o linie ('./js/components/**/*.{js,ts,jsx,tsx}') in content
Copy // tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./js/components/**/*.{js,ts,jsx,tsx}'
],
.........
}
2. Pagina principala
Ca exemplu, vom construi o aplicatie care afiseaza curiozitati pe pagina principala (index). Curiozitatile sunt stocate in baza de date din cloud. Pe pagina /insert, utilizatorii pot introduce curiozitati in baza de date prin intermediul unui formular.
Pentru a adauga functionalitati si a stiliza pagina principala, accesam componenta MainPage - MainPage.jsx
Pentru ca lista de curiozitati va fi adusa asincron din baza de date, definim un state gol in function component
Accesam API-ul creat anterior si aducem curiozitatile atunci cand un utilizator acceseaza pagina
Copy // js/components/MainPage.jsx
import {useEffect, useState} from "react";
export default function MainPage() {
const [records, setRecords] = useState([]);
useEffect(() => {
try{
fetch('/api/records', {
method: 'GET',
})
.then(response => response.json())
.then(json => setRecords(json.data));
}
catch (error) {
console.log(error);
}
}, []);
return (
<div></div>
)
}
Adaugam componente vizuale stilizate cu tailwind si afisam fiecare curiozitate folosind functia map.
Adaugam si un buton prin care sa stergem o inregistrare din baza de date la click + functionalitatea aferenta
Copy // js/components/MainPage.jsx
import {useEffect, useState} from "react";
export default function MainPage() {
.......
const deleteRecord = (event) => {
event.preventDefault();
const id = event.target.id;
try {
fetch(`/api/records?id=${id}`, {
method: 'DELETE',
})
.then(response => response.json())
.then(json => {
setRecords(records.filter(record => record._id !== id));
});
}
catch (error) {
console.log(error);
}
}
return (
<section className="bg-white dark:bg-gray-900">
<div className="container px-6 py-10 mx-auto">
<h1 className="w-[500px] mx-auto text-center text-6xl">Fun facts app</h1>
<p className="w-[1000px] mx-auto text-center mt-4 text-3xl">This is an app that showcases fun facts</p>
<div className="grid grid-cols-1 gap-8 mt-8 xl:mt-12 xl:gap-12 sm:grid-cols-2 xl:grid-cols-4 lg:grid-cols-3">
{records.map(record => (
<div
key={record._id}
className="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<h5 className="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
{record.title}
</h5>
<p className="font-normal text-gray-700 dark:text-gray-400">
{record.description}
</p>
<div className={"flex justify-center mt-4"}>
<button type="button"
id={record._id}
onClick={deleteRecord}
className="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900">Delete
</button>
</div>
</div>
))}
</div>
</div>
</section>
)
}
La final, componenta are urmatorul cod:
Copy // js/components/MainPage.jsx
import {useEffect, useState} from "react";
export default function MainPage() {
const [records, setRecords] = useState([]);
useEffect(() => {
try{
fetch('/api/records', {
method: 'GET',
})
.then(response => response.json())
.then(json => setRecords(json.data));
}
catch (error) {
console.log(error);
}
}, []);
const deleteRecord = (event) => {
event.preventDefault();
const id = event.target.id;
try {
fetch(`/api/records?id=${id}`, {
method: 'DELETE',
})
.then(response => response.json())
.then(json => {
setRecords(records.filter(record => record._id !== id));
});
}
catch (error) {
console.log(error);
}
}
return (
<section className="bg-white dark:bg-gray-900">
<div className="container px-6 py-10 mx-auto">
<h1 className="w-[500px] mx-auto text-center text-6xl">Fun facts app</h1>
<p className="w-[1000px] mx-auto text-center mt-4 text-3xl">This is an app that showcases fun facts</p>
<div className="grid grid-cols-1 gap-8 mt-8 xl:mt-12 xl:gap-12 sm:grid-cols-2 xl:grid-cols-4 lg:grid-cols-3">
{records.map(record => (
<div
key={record._id}
className="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<h5 className="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
{record.title}
</h5>
<p className="font-normal text-gray-700 dark:text-gray-400">
{record.description}
</p>
<div className={"flex justify-center mt-4"}>
<button type="button"
id={record._id}
onClick={deleteRecord}
className="focus:outline-none text-white bg-red-700 hover:bg-red-800 focus:ring-4 focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 mr-2 mb-2 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-900">Delete
</button>
</div>
</div>
))}
</div>
</div>
</section>
)
}
Aplicatia arata acum astfel (dupa ce am introdus manual cateva date in BD):
3. Pagina /insert
In componenta InsertPage introducem un formular cu 2 campuri si un buton de submit
Copy // js/components/InsertPage.jsx
export default function InsertPage() {
return (
<section className="bg-white dark:bg-gray-900">
<div className="container px-6 py-10 mx-auto">
<h1 className="w-[500px] mx-auto text-center text-6xl">Fun facts app</h1>
<p className="w-[1000px] mx-auto text-center mt-4 text-3xl">This is an app that showcases fun facts</p>
<form>
<div className="mb-6">
<label htmlFor="title" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Fact title</label>
<input type="text" id="title"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="name@flowbite.com" required/>
</div>
<div className="mb-6">
<label htmlFor="description"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Fact description</label>
<textarea id="description"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
required/>
</div>
<button type="submit"
onClick={ insertRecord }
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Submit
</button>
</form>
</div>
</section>
)
}
Introducem logica de introducere a datelor la click pe butonul submit
La final, componenta InsertPage contine urmatorul cod:
Copy // js/components/InsertPage.jsx
export default function InsertPage() {
const insertRecord = (event) => {
event.preventDefault();
const title = document.getElementById("title").value;
const description = document.getElementById("description").value;
const data = {title, description};
fetch("/api/records", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}).then(() => {
console.log("New record inserted");
document.getElementById("title").value = "";
document.getElementById("description").value = "";
});
}
return (
<section className="bg-white dark:bg-gray-900">
<div className="container px-6 py-10 mx-auto">
<h1 className="w-[500px] mx-auto text-center text-6xl">Fun facts app</h1>
<p className="w-[1000px] mx-auto text-center mt-4 text-3xl">This is an app that showcases fun facts</p>
<form>
<div className="mb-6">
<label htmlFor="title" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Fact title</label>
<input type="text" id="title"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="name@flowbite.com" required/>
</div>
<div className="mb-6">
<label htmlFor="description"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Fact description</label>
<textarea id="description"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
required/>
</div>
<button type="submit"
onClick={ insertRecord }
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Submit
</button>
</form>
</div>
</section>
)
}
Pagina /insert arata acum astfel:
Trimitem totul in repository-ul remote git.
Acum ca avem o aplicatie functionala, o putem lansa pe internet folosing un serviciu in cloud!