ripasso: fixes

This commit is contained in:
2026-03-01 20:49:28 +01:00
parent a83d57e0f5
commit dd1fb79c7c
4 changed files with 1239 additions and 1 deletions

View File

@@ -2,7 +2,7 @@
## Obiettivo
Questi due esercizi coprono **TUTTO** il programma JavaScript, dal primo `console.log` fino a `localStorage`.
Questi esercizi coprono **TUTTO** il programma JavaScript, dal primo `console.log` fino alle `API`.
Sono pensati per essere fatti **in autonomia**, senza bisogno di aiuto, a patto di seguire le istruzioni passo passo e di non saltare nessun concetto.
---
@@ -18,6 +18,7 @@ Sono pensati per essere fatti **in autonomia**, senza bisogno di aiuto, a patto
7. **Non saltare nessun step** — ogni step usa ciò che hai fatto prima
8. Salva e ricarica la pagina per vedere i risultati
9. Quando hai finito il primo esercizio, ripeti i passaggi 3-8 per il secondo esercizio
10. Per il **terzo esercizio** (API), prima avvia il server: apri il terminale nella cartella `server-api` e lancia `npm start`
---
@@ -74,6 +75,31 @@ Se ti blocchi, torna all'Esercizio 1 per rivedere il concetto.
---
### Esercizio 3 — "La Biblioteca Online" (API)
Un esercizio dedicato alla **comunicazione con un server API** usando `fetch()`.
Copre le operazioni CRUD:
| Step | Operazione | Metodo HTTP | Cosa fa |
| ---- | ---------- | ----------- | ---------------------------- |
| 1 | READ | GET | Caricare tutti i libri |
| 2 | READ | GET | Cercare un libro per ID |
| 3 | CREATE | POST | Aggiungere un nuovo libro |
| 4 | UPDATE | PATCH | Segnare un libro come letto |
| 5 | DELETE | DELETE | Eliminare un libro |
| 6 | BONUS | — | Caricamento auto, filtri, ricerca |
**Prerequisito:** avviare il server API prima di iniziare (chiedere al docente).
Per ogni step trovi:
- 📖 **Spiegazione** del concetto (fetch, async/await, metodi HTTP)
- 💡 **Esempio** di sintassi
- ✏️ **TODO** — il codice che devi scrivere tu
-**Verifica** — come controllare che hai fatto giusto
---
## Regola d'oro
> **Se non ricordi come si fa qualcosa, rileggilo nell'Esercizio 1.**\

View File

@@ -0,0 +1,473 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Esercizio 3 — La Biblioteca Online (API)</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@700&family=Inter:wght@400;500;600;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', 'Segoe UI', sans-serif;
background: linear-gradient(145deg, #fdf8f0 0%, #f5f0e8 40%, #faf5ed 100%);
color: #2d2418;
padding: 24px;
line-height: 1.6;
min-height: 100vh;
}
.container {
max-width: 880px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(12px);
border-radius: 20px;
padding: 36px;
box-shadow:
0 1px 3px rgba(0,0,0,0.04),
0 8px 32px rgba(139,90,43,0.08);
border: 1px solid rgba(255,255,255,0.6);
}
h1 {
text-align: center;
font-family: 'Merriweather', Georgia, serif;
font-size: 2.2em;
margin-bottom: 6px;
background: linear-gradient(135deg, #8b5a2b, #a0522d);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sottotitolo {
text-align: center;
color: #94867a;
font-size: 0.9em;
font-weight: 500;
margin-bottom: 10px;
}
.avviso-server {
text-align: center;
background: linear-gradient(135deg, #fef3c7, #fef9c3);
border: 1px solid #fbbf24;
color: #92400e;
padding: 12px 18px;
border-radius: 12px;
font-size: 0.88em;
font-weight: 500;
margin-bottom: 28px;
}
.avviso-server code {
background: rgba(0,0,0,0.08);
padding: 2px 6px;
border-radius: 4px;
}
/* ─── STATISTICHE ─── */
#stats {
display: flex;
justify-content: center;
gap: 16px;
margin-bottom: 28px;
flex-wrap: wrap;
}
.stat-box {
background: linear-gradient(145deg, #fff, #faf5ed);
border: 1px solid #e8ddd0;
border-radius: 14px;
padding: 18px 28px;
text-align: center;
min-width: 140px;
position: relative;
overflow: hidden;
transition: all 0.25s ease;
}
.stat-box::after {
content: '';
position: absolute;
bottom: 0; left: 0; right: 0;
height: 3px;
background: linear-gradient(90deg, #8b5a2b, #cd853f);
opacity: 0;
transition: opacity 0.25s;
}
.stat-box:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(139,90,43,0.1);
}
.stat-box:hover::after { opacity: 1; }
.stat-box .label {
font-size: 0.75em;
color: #94867a;
text-transform: uppercase;
letter-spacing: 0.8px;
font-weight: 600;
}
.stat-box .valore {
font-size: 2em;
font-weight: 700;
color: #8b5a2b;
}
/* ─── LOADING ─── */
#loading {
display: none;
text-align: center;
padding: 30px;
}
#loading.visibile { display: block; }
.spinner {
width: 40px; height: 40px;
border: 4px solid #e8ddd0;
border-top: 4px solid #8b5a2b;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin: 0 auto 12px;
}
@keyframes spin { to { transform: rotate(360deg); } }
#loading p { color: #94867a; font-size: 0.9em; }
/* ─── SEZIONI ─── */
.sezione {
background: linear-gradient(145deg, #fdf8f0, #faf5ed);
border: 1px solid #e8ddd0;
border-radius: 14px;
padding: 26px;
margin-bottom: 24px;
}
.sezione h2 {
font-size: 1.15em;
margin-bottom: 18px;
color: #8b5a2b;
font-weight: 700;
}
/* ─── FORM ─── */
.form-riga {
display: flex;
gap: 12px;
margin-bottom: 12px;
flex-wrap: wrap;
}
.form-riga input,
.form-riga select {
flex: 1;
min-width: 140px;
padding: 11px 16px;
border: 1.5px solid #e0d5c7;
border-radius: 10px;
font-size: 0.95em;
font-family: inherit;
background: rgba(255,255,255,0.85);
color: #2d2418;
transition: all 0.25s ease;
}
.form-riga input:focus,
.form-riga select:focus {
outline: none;
border-color: #8b5a2b;
box-shadow: 0 0 0 3px rgba(139,90,43,0.1);
background: #fff;
}
.form-riga input::placeholder { color: #b8a898; }
/* ─── BOTTONI ─── */
.bottoni {
display: flex;
gap: 10px;
margin-bottom: 18px;
flex-wrap: wrap;
}
button {
padding: 10px 22px;
border: none;
border-radius: 10px;
font-size: 0.9em;
font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: all 0.25s ease;
}
.btn-primario {
background: linear-gradient(135deg, #8b5a2b, #a0522d);
color: white;
box-shadow: 0 2px 10px rgba(139,90,43,0.25);
}
.btn-primario:hover {
box-shadow: 0 4px 16px rgba(139,90,43,0.35);
transform: translateY(-1px);
}
.btn-secondario {
background: rgba(255,255,255,0.7);
color: #5d4e37;
border: 1.5px solid #e0d5c7;
}
.btn-secondario:hover {
background: #fff;
border-color: #8b5a2b;
transform: translateY(-1px);
}
.btn-danger {
background: #fff5f5;
color: #c53030;
border: 1.5px solid #fcc;
padding: 6px 14px;
font-size: 0.85em;
border-radius: 8px;
}
.btn-danger:hover {
background: #fed7d7;
border-color: #c53030;
}
/* ─── LISTA LIBRI ─── */
#lista-libri {
list-style: none;
padding: 0;
margin-top: 16px;
}
#lista-libri li {
background: #fff;
border: 1px solid #e8ddd0;
border-left: 4px solid #8b5a2b;
border-radius: 0 10px 10px 0;
padding: 14px 18px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.2s ease;
}
#lista-libri li:hover {
background: #fdf8f0;
box-shadow: 0 2px 8px rgba(139,90,43,0.08);
transform: translateX(4px);
}
#lista-libri li .info-libro { flex: 1; }
#lista-libri li .titolo-libro {
font-weight: 700;
color: #8b5a2b;
font-size: 1.05em;
}
#lista-libri li .dettagli-libro {
color: #94867a;
font-size: 0.85em;
margin-top: 2px;
}
.letto-badge {
display: inline-block;
padding: 2px 10px;
border-radius: 12px;
font-size: 0.78em;
font-weight: 600;
}
.letto-si { background: #d1fae5; color: #065f46; }
.letto-no { background: #fee2e2; color: #991b1b; }
/* ─── DETTAGLIO ─── */
#dettaglio-libro {
background: #fff;
border: 1px solid #e8ddd0;
border-radius: 12px;
padding: 22px;
margin-top: 16px;
display: none;
}
#dettaglio-libro.visibile {
display: block;
animation: slideIn 0.3s ease;
}
#dettaglio-libro h3 {
color: #8b5a2b;
font-size: 1.2em;
margin-bottom: 8px;
}
#dettaglio-libro .info {
color: #5d4e37;
font-size: 0.95em;
margin-bottom: 4px;
}
/* ─── MESSAGGIO ─── */
#messaggio {
display: none;
padding: 14px 18px;
border-radius: 10px;
margin: 16px 0;
font-weight: 600;
text-align: center;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(-8px); }
to { opacity: 1; transform: translateY(0); }
}
.msg-successo {
background: linear-gradient(135deg, #d1fae5, #a7f3d0);
color: #065f46;
border: 1px solid #6ee7b7;
}
.msg-errore {
background: linear-gradient(135deg, #fee2e2, #fecaca);
color: #991b1b;
border: 1px solid #fca5a5;
}
/* ─── EMPTY STATE ─── */
.empty-state {
text-align: center;
padding: 40px 20px;
color: #94867a;
}
.empty-state .emoji { font-size: 3em; margin-bottom: 10px; }
</style>
</head>
<body>
<div class="container">
<h1>📚 La Biblioteca Online</h1>
<p class="sottotitolo">Ripasso API — fetch, async/await, CRUD</p>
<div class="avviso-server">
⚠️ Prima di iniziare, assicurati che il server sia avviato e funzionante.
</div>
<!-- MESSAGGIO -->
<div id="messaggio"></div>
<!-- STATISTICHE -->
<div id="stats">
<div class="stat-box">
<div class="label">Totale Libri</div>
<div class="valore" id="stat-totale">0</div>
</div>
<div class="stat-box">
<div class="label">Già Letti</div>
<div class="valore" id="stat-letti">0</div>
</div>
<div class="stat-box">
<div class="label">Da Leggere</div>
<div class="valore" id="stat-da-leggere">0</div>
</div>
</div>
<!-- LOADING -->
<div id="loading">
<div class="spinner"></div>
<p>Caricamento in corso...</p>
</div>
<!-- SEZIONE 1: CARICA LISTA -->
<div class="sezione" id="sezione-lista">
<h2>📋 Lista Libri</h2>
<div class="bottoni">
<button class="btn-primario" id="btn-carica-tutti">📥 Carica Tutti i Libri</button>
</div>
<ul id="lista-libri">
<div class="empty-state">
<div class="emoji">📚</div>
<p>Clicca "Carica Tutti i Libri" per iniziare</p>
</div>
</ul>
</div>
<!-- SEZIONE 2: DETTAGLIO SINGOLO -->
<div class="sezione" id="sezione-dettaglio">
<h2>🔎 Cerca Libro per ID</h2>
<div class="form-riga">
<input type="number" id="input-id" placeholder="Inserisci ID libro (1-20)" min="1" max="20">
<button class="btn-primario" id="btn-dettaglio">Cerca</button>
</div>
<div id="dettaglio-libro"></div>
</div>
<!-- SEZIONE 3: AGGIUNGI LIBRO -->
<div class="sezione" id="sezione-aggiungi">
<h2> Aggiungi Nuovo Libro</h2>
<div class="form-riga">
<input type="text" id="input-titolo" placeholder="Titolo">
<input type="text" id="input-autore" placeholder="Autore">
</div>
<div class="form-riga">
<select id="select-genere">
<option value="">-- Genere --</option>
<option value="Fantasy">🧙 Fantasy</option>
<option value="Giallo">🔍 Giallo</option>
<option value="Fantascienza">🚀 Fantascienza</option>
<option value="Narrativa">📖 Narrativa</option>
<option value="Horror">👻 Horror</option>
<option value="Romanzo">💕 Romanzo</option>
<option value="Umorismo">😂 Umorismo</option>
</select>
<input type="number" id="input-pagine" placeholder="Numero pagine" min="1">
</div>
<button class="btn-primario" id="btn-aggiungi"> Aggiungi Libro</button>
</div>
<!-- SEZIONE 4: SEGNA COME LETTO -->
<div class="sezione" id="sezione-modifica">
<h2>✏️ Segna come Letto / Non Letto</h2>
<div class="form-riga">
<input type="number" id="input-modifica-id" placeholder="ID del libro" min="1">
<select id="select-letto">
<option value="true">✅ Letto</option>
<option value="false">❌ Non letto</option>
</select>
<button class="btn-primario" id="btn-modifica">Aggiorna</button>
</div>
</div>
<!-- SEZIONE 5: ELIMINA -->
<div class="sezione" id="sezione-elimina">
<h2>🗑️ Elimina Libro</h2>
<div class="form-riga">
<input type="number" id="input-elimina-id" placeholder="ID del libro da eliminare" min="1">
<button class="btn-danger" id="btn-elimina">🗑️ Elimina</button>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,577 @@
// ╔══════════════════════════════════════════════════════════════════════════════╗
// ║ 📚 LA BIBLIOTECA ONLINE — ESERCIZIO API (GUIDATO) ║
// ║ ║
// ║ Questo esercizio ti insegna a comunicare con un SERVER usando fetch(). ║
// ║ Imparerai le operazioni CRUD: ║
// ║ ║
// ║ 📥 GET — leggere dati dal server ║
// ║ 📤 POST — inviare/creare nuovi dati ║
// ║ ✏️ PATCH — modificare dati esistenti ║
// ║ 🗑️ DELETE — eliminare dati ║
// ║ ║
// ║ COME FUNZIONA QUESTO ESERCIZIO: ║
// ║ Per ogni step trovi: ║
// ║ 1. 💡 ESEMPIO — codice GIÀ FUNZIONANTE che gira davvero ║
// ║ 2. ✏️ TODO — devi RIFARE la stessa cosa per un caso diverso ║
// ║ ║
// ║ PRIMA DI INIZIARE: ║
// ║ 1. Apri un terminale nella cartella "server-api" ║
// ║ 2. Lancia: npm start ║
// ║ 3. Il server sarà attivo su http://localhost:5000/api ║
// ║ 4. I libri sono su http://localhost:5000/api/books ║
// ║ ║
// ║ STRUTTURA DI UN LIBRO NEL DATABASE: ║
// ║ { ║
// ║ "id": 1, ║
// ║ "titolo": "Il nome della rosa", ║
// ║ "autore": "Umberto Eco", ║
// ║ "genere": "Giallo", ║
// ║ "pagine": 512, ║
// ║ "letto": true ║
// ║ } ║
// ╚══════════════════════════════════════════════════════════════════════════════╝
// ============================================================================
// SETUP — URL BASE E SELEZIONE ELEMENTI (già fatto, controlla URL)
// ============================================================================
const BASE_URL = "http://localhost:5000/api";
// Elementi della pagina
const btnCaricaTutti = document.querySelector("#btn-carica-tutti");
const btnDettaglio = document.querySelector("#btn-dettaglio");
const btnAggiungi = document.querySelector("#btn-aggiungi");
const btnModifica = document.querySelector("#btn-modifica");
const btnElimina = document.querySelector("#btn-elimina");
const listaLibri = document.querySelector("#lista-libri");
const dettaglioLibro = document.querySelector("#dettaglio-libro");
const loading = document.querySelector("#loading");
const messaggio = document.querySelector("#messaggio");
const statTotale = document.querySelector("#stat-totale");
const statLetti = document.querySelector("#stat-letti");
const statDaLeggere = document.querySelector("#stat-da-leggere");
// ============================================================================
// FUNZIONI HELPER (già fatte, NON modificare)
// ============================================================================
/**
* Mostra lo spinner di caricamento
*/
function mostraLoading() {
loading.classList.add("visibile");
}
/**
* Nasconde lo spinner di caricamento
*/
function nascondiLoading() {
loading.classList.remove("visibile");
}
/**
* Mostra un messaggio di feedback (successo o errore)
* @param {string} testo - Il testo del messaggio
* @param {string} tipo - "successo" oppure "errore"
*/
function mostraMessaggio(testo, tipo) {
messaggio.textContent = testo;
messaggio.className = "";
messaggio.classList.add("msg-" + tipo);
messaggio.style.display = "block";
setTimeout(() => {
messaggio.style.display = "none";
}, 3000);
}
/**
* Aggiorna le statistiche nel DOM
* @param {Array} libri - L'array di libri ricevuto dal server
*/
function aggiornaStatistiche(libri) {
const totale = libri.length;
const letti = libri.filter((libro) => libro.letto === true).length;
const daLeggere = totale - letti;
statTotale.textContent = totale;
statLetti.textContent = letti;
statDaLeggere.textContent = daLeggere;
}
/**
* Crea l'HTML di un singolo libro nella lista
* @param {Object} libro - L'oggetto libro dal server
* @returns {string} - L'HTML della riga
*/
function creaRigaLibro(libro) {
const badgeLetto = `
<span class="letto-badge ${libro.letto ? "letto-si" : "letto-no"}">
${libro.letto ? "✅ Letto" : "📖 Da leggere"}
</span>`;
return `
<li>
<div class="info-libro">
<div class="titolo-libro">${libro.titolo}</div>
<div class="dettagli-libro">
✍️ ${libro.autore} | 📄 ${libro.pagine} pagine | ${badgeLetto}
</div>
</div>
<div style="color:#94867a; font-size:0.82em;">ID: ${libro.id}</div>
</li>`;
}
// ============================================================================
// STEP 1 — GET: LEGGERE DATI DAL SERVER
// ============================================================================
// ┌──────────────────────────────────────────────────────────────────────────┐
// │ 📖 CONCETTI CHIAVE │
// │ │
// │ • fetch(url) fa una richiesta al server (di default è GET) │
// │ • fetch è ASINCRONO → usiamo async/await per aspettare la risposta │
// │ • risposta.ok → true se il server ha risposto con successo │
// │ • risposta.json() → converte la risposta in un oggetto/array JS │
// │ • try/catch → gestisce gli errori (server spento, URL sbagliato, ecc.) │
// └──────────────────────────────────────────────────────────────────────────┘
// ── 💡 ESEMPIO FUNZIONANTE ──────────────────────────────
// Questa funzione FUNZIONA GIÀ. Carica il libro con ID 1
// e stampa i suoi dati in console.
// Apri la Console (F12) per vedere il risultato.
async function esempioCaricaUnLibro() {
try {
const risposta = await fetch(BASE_URL + "/books/1");
if (!risposta.ok) {
throw new Error("Errore dal server: " + risposta.status);
}
const libro = await risposta.json();
console.log("--- ESEMPIO GET singolo ---");
console.log("Titolo:", libro.titolo); // → "Il nome della rosa"
console.log("Autore:", libro.autore); // → "Umberto Eco"
console.log("Oggetto completo:", libro);
} catch (errore) {
console.error("Errore nell'esempio:", errore.message);
}
}
// Eseguiamo l'esempio automaticamente all'apertura della pagina:
esempioCaricaUnLibro();
// ── ✏️ ORA TOCCA A TE ──────────────────────────────────
// Hai visto come caricare UN libro.
// Adesso devi caricare TUTTI i libri e mostrarli nella lista.
//
// L'URL per tutti i libri è: BASE_URL + "/books"
// (senza /1 alla fine → restituisce un ARRAY invece di un oggetto)
//
// Devi:
// 1. Chiamare mostraLoading()
// 2. try/catch come nell'esempio sopra
// 3. Dentro il try:
// a. fetch GET a BASE_URL + "/books"
// b. Controllare risposta.ok
// c. Convertire in JSON → sarà un ARRAY di libri
// d. Svuotare la lista: listaLibri.innerHTML = ""
// e. Per ogni libro, aggiungere una riga:
// for (const libro of libri) {
// listaLibri.innerHTML += creaRigaLibro(libro);
// }
// f. Aggiornare le statistiche: aggiornaStatistiche(libri)
// g. mostraMessaggio("Libri caricati!", "successo")
// 4. Dentro il catch:
// mostraMessaggio(errore.message, "errore")
// 5. Alla fine (dopo il catch): nascondiLoading()
async function caricaTuttiILibri() {
// 👇 SCRIVI QUI IL TUO CODICE
}
// ✅ VERIFICA: Clicca "Carica Tutti i Libri" sulla pagina → appaiono 20 libri nella lista
// Le statistiche mostrano: 20 totali, 10 letti, 10 da leggere
// ============================================================================
// STEP 2 — GET CON ID: LEGGERE UN SINGOLO ELEMENTO
// ============================================================================
// ── 💡 ESEMPIO FUNZIONANTE ──────────────────────────────
// Questa funzione FUNZIONA GIÀ. Carica il libro con ID 2
// e lo mostra nel div #dettaglio-libro.
async function esempioMostraDettaglio() {
try {
const risposta = await fetch(BASE_URL + "/books/2");
if (!risposta.ok) {
throw new Error("Libro non trovato (errore " + risposta.status + ")");
}
const libro = await risposta.json();
// Mostriamo il dettaglio nella pagina
dettaglioLibro.innerHTML = `
<h3>${libro.titolo}</h3>
<p class="info">✍️ Autore: ${libro.autore}</p>
<p class="info">📂 Genere: ${libro.genere}</p>
<p class="info">📄 Pagine: ${libro.pagine}</p>
<p class="info">Stato: ${libro.letto ? "Letto ✅" : "Da leggere 📖"}</p>
`;
dettaglioLibro.classList.add("visibile");
console.log("--- ESEMPIO GET per ID ---");
console.log("Caricato:", libro.titolo);
} catch (errore) {
console.error("Errore nell'esempio:", errore.message);
}
}
// Eseguiamo l'esempio (mostra il libro 2 nel dettaglio):
esempioMostraDettaglio();
// ── ✏️ ORA TOCCA A TE ──────────────────────────────────
// Hai visto come caricare un libro con un ID fisso (2).
// Adesso devi fare la stessa cosa, ma leggendo l'ID dall'input.
//
// Per leggere l'input:
// const id = document.querySelector("#input-id").value;
//
// ⚠️ .value restituisce una STRINGA. Per validare:
// if (!id || isNaN(id)) { mostraMessaggio("Inserisci un ID valido!", "errore"); return; }
//
// Devi:
// 1. Leggere l'ID dall'input
// 2. Validare che sia un numero
// 3. mostraLoading()
// 4. try/catch come nell'esempio sopra, ma con l'ID dinamico:
// fetch(BASE_URL + "/books/" + id)
// 5. Nel catch: mostraMessaggio(errore.message, "errore")
// 6. Alla fine: nascondiLoading()
async function cercaLibroPerId() {
// 👇 SCRIVI QUI IL TUO CODICE
}
// ✅ VERIFICA: Inserisci "3" nella pagina HTML → vedi "Il piccolo principe"
// Inserisci "99" → vedi un messaggio di errore
// ============================================================================
// STEP 3 — POST: CREARE UN NUOVO ELEMENTO
// ============================================================================
// ── 💡 ESEMPIO FUNZIONANTE ──────────────────────────────
// Questa funzione FUNZIONA GIÀ. Aggiunge un libro fisso
// ("Moby Dick") al server e lo stampa in console.
// Il server gli assegna un ID automaticamente.
async function esempioAggiungiLibro() {
try {
const risposta = await fetch(BASE_URL + "/books", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
titolo: "Moby Dick",
autore: "Herman Melville",
genere: "Narrativa",
pagine: 635,
letto: false
})
});
if (!risposta.ok) {
throw new Error("Errore nella creazione: " + risposta.status);
}
const libroCreato = await risposta.json();
console.log("--- ESEMPIO POST ---");
console.log("Libro creato:", libroCreato);
console.log("ID assegnato dal server:", libroCreato.id);
} catch (errore) {
console.error("Errore nell'esempio POST:", errore.message);
}
}
// Lo eseguiamo per vedere il risultato in console:
esempioAggiungiLibro();
// ── ✏️ ORA TOCCA A TE ──────────────────────────────────
// Hai visto come fare una POST con dati fissi.
// Adesso devi fare la stessa cosa, ma leggendo i dati dal FORM.
//
// Per leggere i campi:
// const titolo = document.querySelector("#input-titolo").value;
// const autore = document.querySelector("#input-autore").value;
// const genere = document.querySelector("#select-genere").value;
// const pagine = parseInt(document.querySelector("#input-pagine").value);
//
// Devi:
// 1. Leggere i valori dal form
// 2. Validare che titolo e autore non siano vuoti:
// if (!titolo || !autore) { mostraMessaggio("Compila titolo e autore!", "errore"); return; }
// 3. mostraLoading()
// 4. try/catch:
// a. Creare l'oggetto: { titolo, autore, genere: genere || "Narrativa", pagine: pagine || 0, letto: false }
// b. Fare la fetch POST (come nell'esempio sopra, ma con i dati del form)
// c. Controllare risposta.ok
// d. mostraMessaggio("Libro aggiunto!", "successo")
// e. Svuotare gli input (metti .value = "")
// f. Ricaricare la lista: caricaTuttiILibri()
// 5. Nel catch: mostraMessaggio(errore.message, "errore")
// 6. Alla fine: nascondiLoading()
async function aggiungiLibro() {
// 👇 SCRIVI QUI IL TUO CODICE
}
// ✅ VERIFICA: Compila il form, clicca "Aggiungi Libro"
// → Il nuovo libro appare nella lista, gli input si svuotano
// ============================================================================
// STEP 4 — PATCH: MODIFICARE UN ELEMENTO ESISTENTE (UPDATE PARZIALE)
// ============================================================================
// ┌──────────────────────────────────────────────────────────────────────────┐
// │ 📖 CONCETTI CHIAVE │
// │ │
// │ Per MODIFICARE un dato esistente, usiamo PATCH (o PUT). │
// │ │
// │ DIFFERENZA: │
// │ • PUT → sostituisce TUTTO l'oggetto (devi mandare tutti i campi) │
// │ • PATCH → modifica SOLO i campi che vuoi (più comodo!) │
// │ │
// │ Esempio: voglio cambiare solo "letto" da false a true. │
// │ Con PATCH: mando solo { letto: true } │
// │ Con PUT: dovrei mandare { id, titolo, autore, genere, pagine, letto } │
// │ │
// │ Per specificare QUALE elemento modificare → l'ID va nell'URL. │
// └──────────────────────────────────────────────────────────────────────────┘
// ── 💡 ESEMPIO FUNZIONANTE ──────────────────────────────
// Questa funzione FUNZIONA GIÀ. Segna il libro con ID 4 ("1984")
// come "letto" e stampa il risultato in console.
//
// PATCH modifica SOLO i campi che mandi nel body.
// Non serve mandare tutto l'oggetto, solo ciò che cambia.
async function esempioModificaLibro() {
try {
const risposta = await fetch(BASE_URL + "/books/4", {
method: "PATCH",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
letto: true // modifica SOLO il campo "letto"
})
});
if (!risposta.ok) {
throw new Error("Errore nella modifica: " + risposta.status);
}
const libroModificato = await risposta.json();
console.log("--- ESEMPIO PATCH ---");
console.log("Libro modificato:", libroModificato.titolo);
console.log("Ora letto:", libroModificato.letto); // → true
} catch (errore) {
console.error("Errore nell'esempio PATCH:", errore.message);
}
}
// Lo eseguiamo:
esempioModificaLibro();
// ── ✏️ ORA TOCCA A TE ──────────────────────────────────
// Hai visto come fare una PATCH con ID e valore fissi.
// Adesso devi fare la stessa cosa, ma leggendo ID e stato dal form.
//
// Per leggere i campi:
// const id = document.querySelector("#input-modifica-id").value;
// const selectLetto = document.querySelector("#select-letto").value;
//
// ⚠️ ATTENZIONE: selectLetto sarà la STRINGA "true" o "false"
// Per convertirla in booleano:
// const letto = selectLetto === "true";
//
// Devi:
// 1. Leggere ID e stato dal form
// 2. Validare che l'ID sia un numero
// 3. mostraLoading()
// 4. try/catch:
// a. Fare la fetch PATCH a BASE_URL + "/books/" + id
// con body: { letto: letto }
// b. Controllare risposta.ok
// c. mostraMessaggio("Libro aggiornato!", "successo")
// d. Ricaricare la lista: caricaTuttiILibri()
// 5. Nel catch: mostraMessaggio(errore.message, "errore")
// 6. Alla fine: nascondiLoading()
async function modificaLibro() {
// 👇 SCRIVI QUI IL TUO CODICE
}
// ✅ VERIFICA: Inserisci ID 5, seleziona "Letto", clicca "Aggiorna"
// → "Il Signore degli Anelli" passa a "Letto ✅"
// ============================================================================
// STEP 5 — DELETE: ELIMINARE UN ELEMENTO
// ============================================================================
// ── 💡 ESEMPIO FUNZIONANTE ──────────────────────────────
// Questa funzione FUNZIONA GIÀ. Elimina il libro con ID 20
// ("Il diario di Anna Frank") e stampa il risultato.
//
// DELETE è il metodo più semplice: solo method e URL, niente body.
async function esempioEliminaLibro() {
try {
const risposta = await fetch(BASE_URL + "/books/20", {
method: "DELETE"
});
if (!risposta.ok) {
throw new Error("Errore nell'eliminazione: " + risposta.status);
}
console.log("--- ESEMPIO DELETE ---");
console.log("Libro con ID 20 eliminato con successo!");
} catch (errore) {
console.error("Errore nell'esempio DELETE:", errore.message);
}
}
// Lo eseguiamo:
esempioEliminaLibro();
// ── ✏️ ORA TOCCA A TE ──────────────────────────────────
// Hai visto come fare una DELETE con un ID fisso.
// Adesso devi fare la stessa cosa, ma leggendo l'ID dall'input
// e chiedendo conferma all'utente.
//
// Per chiedere conferma:
// if (!confirm("Sei sicuro di voler eliminare il libro " + id + "?")) {
// return; // l'utente ha cliccato "Annulla"
// }
//
// Devi:
// 1. Leggere l'ID da #input-elimina-id
// 2. Validare che sia un numero
// 3. Chiedere conferma con confirm()
// 4. mostraLoading()
// 5. try/catch:
// a. Fare la fetch DELETE a BASE_URL + "/books/" + id
// b. Controllare risposta.ok
// c. mostraMessaggio("Libro eliminato!", "successo")
// d. Svuotare l'input
// e. Ricaricare la lista: caricaTuttiILibri()
// 6. Nel catch: mostraMessaggio(errore.message, "errore")
// 7. Alla fine: nascondiLoading()
async function eliminaLibro() {
// 👇 SCRIVI QUI IL TUO CODICE
}
// ✅ VERIFICA: Inserisci un ID, clicca "Elimina"
// → Compare un confirm, se confermi il libro sparisce
// ============================================================================
// COLLEGAMENTO EVENTI (già fatto, NON modificare)
// ============================================================================
btnCaricaTutti.addEventListener("click", caricaTuttiILibri);
btnDettaglio.addEventListener("click", cercaLibroPerId);
btnAggiungi.addEventListener("click", aggiungiLibro);
btnModifica.addEventListener("click", modificaLibro);
btnElimina.addEventListener("click", eliminaLibro);
// ============================================================================
// STEP 6 — BONUS (facoltativo)
// ============================================================================
// ┌──────────────────────────────────────────────────────────────────────────┐
// │ Se hai completato tutti gli step, prova queste sfide extra! │
// └──────────────────────────────────────────────────────────────────────────┘
// ─────────────────────────────────────────────────────────
// ✏️ BONUS 1 — Carica i libri automaticamente all'apertura
// ─────────────────────────────────────────────────────────
// Chiama caricaTuttiILibri() quando la pagina si carica.
// Suggerimento: basta una riga di codice qui sotto!
// 👇 SCRIVI QUI IL TUO CODICE (Bonus 1)
// ─────────────────────────────────────────────────────────
// ✏️ BONUS 2 — Filtra i libri per stato (letti / da leggere)
// ─────────────────────────────────────────────────────────
// Il server supporta i filtri via query string:
// /api/books?letto=true → solo i libri letti
// /api/books?letto=false → solo quelli da leggere
//
// Crea una funzione "caricaPerStato" che riceve un parametro booleano
// e fa una fetch con il filtro. Poi collegala a due nuovi bottoni
// (puoi aggiungerli nell'HTML o crearli via JS).
// 👇 SCRIVI QUI IL TUO CODICE (Bonus 2)
// ─────────────────────────────────────────────────────────
// ✏️ BONUS 3 — Ricerca libri per titolo
// ─────────────────────────────────────────────────────────
// Il server supporta la ricerca:
// /api/books?q=harry → cerca "harry" in tutti i campi
//
// Aggiungi un evento "input" sull'input di ricerca che filtra
// i libri in tempo reale dal server.
// 👇 SCRIVI QUI IL TUO CODICE (Bonus 3)
// ✅ Se hai completato tutto, complimenti!
// Hai imparato a fare GET, POST, PATCH e DELETE con fetch + async/await! 🎉

View File

@@ -789,5 +789,167 @@
"immagine": "https://media.startech.com/cms/products/gallery_large/laptop-stand-silver.main.jpg",
"disponibilita": true
}
],
"books": [
{
"id": 1,
"titolo": "Il nome della rosa",
"autore": "Umberto Eco",
"genere": "Giallo",
"pagine": 512,
"letto": true
},
{
"id": 2,
"titolo": "Harry Potter e la pietra filosofale",
"autore": "J.K. Rowling",
"genere": "Fantasy",
"pagine": 293,
"letto": true
},
{
"id": 3,
"titolo": "Il piccolo principe",
"autore": "Antoine de Saint-Exupéry",
"genere": "Narrativa",
"pagine": 96,
"letto": true
},
{
"id": 4,
"titolo": "1984",
"autore": "George Orwell",
"genere": "Fantascienza",
"pagine": 328,
"letto": false
},
{
"id": 5,
"titolo": "Il Signore degli Anelli",
"autore": "J.R.R. Tolkien",
"genere": "Fantasy",
"pagine": 1216,
"letto": false
},
{
"id": 6,
"titolo": "Orgoglio e pregiudizio",
"autore": "Jane Austen",
"genere": "Romanzo",
"pagine": 432,
"letto": true
},
{
"id": 7,
"titolo": "Fahrenheit 451",
"autore": "Ray Bradbury",
"genere": "Fantascienza",
"pagine": 158,
"letto": false
},
{
"id": 8,
"titolo": "Diario di una schiappa",
"autore": "Jeff Kinney",
"genere": "Umorismo",
"pagine": 217,
"letto": true
},
{
"id": 9,
"titolo": "IT",
"autore": "Stephen King",
"genere": "Horror",
"pagine": 1138,
"letto": false
},
{
"id": 10,
"titolo": "Il codice da Vinci",
"autore": "Dan Brown",
"genere": "Giallo",
"pagine": 597,
"letto": true
},
{
"id": 11,
"titolo": "Percy Jackson e gli dei dell'Olimpo",
"autore": "Rick Riordan",
"genere": "Fantasy",
"pagine": 375,
"letto": true
},
{
"id": 12,
"titolo": "Assassinio sull'Orient Express",
"autore": "Agatha Christie",
"genere": "Giallo",
"pagine": 256,
"letto": false
},
{
"id": 13,
"titolo": "Hunger Games",
"autore": "Suzanne Collins",
"genere": "Fantascienza",
"pagine": 374,
"letto": true
},
{
"id": 14,
"titolo": "Il giovane Holden",
"autore": "J.D. Salinger",
"genere": "Narrativa",
"pagine": 214,
"letto": false
},
{
"id": 15,
"titolo": "Shining",
"autore": "Stephen King",
"genere": "Horror",
"pagine": 447,
"letto": true
},
{
"id": 16,
"titolo": "Il vecchio e il mare",
"autore": "Ernest Hemingway",
"genere": "Narrativa",
"pagine": 127,
"letto": false
},
{
"id": 17,
"titolo": "Maze Runner - Il labirinto",
"autore": "James Dashner",
"genere": "Fantascienza",
"pagine": 384,
"letto": false
},
{
"id": 18,
"titolo": "Frankenstein",
"autore": "Mary Shelley",
"genere": "Horror",
"pagine": 280,
"letto": true
},
{
"id": 19,
"titolo": "Le cronache di Narnia",
"autore": "C.S. Lewis",
"genere": "Fantasy",
"pagine": 767,
"letto": false
},
{
"id": 20,
"titolo": "Il diario di Anna Frank",
"autore": "Anna Frank",
"genere": "Narrativa",
"pagine": 283,
"letto": true
}
]
}