diff --git a/javascript/ripasso_completo/README.md b/javascript/ripasso_completo/README.md index 449737a..cf4fa97 100644 --- a/javascript/ripasso_completo/README.md +++ b/javascript/ripasso_completo/README.md @@ -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.**\ diff --git a/javascript/ripasso_completo/esercizio_3_api/index.html b/javascript/ripasso_completo/esercizio_3_api/index.html new file mode 100644 index 0000000..c8ebed8 --- /dev/null +++ b/javascript/ripasso_completo/esercizio_3_api/index.html @@ -0,0 +1,473 @@ + + +
+ + +Ripasso API β fetch, async/await, CRUD
+ +Caricamento in corso...
+Clicca "Carica Tutti i Libri" per iniziare
+βοΈ Autore: ${libro.autore}
+π Genere: ${libro.genere}
+π Pagine: ${libro.pagine}
+Stato: ${libro.letto ? "Letto β " : "Da leggere π"}
+ `; + + 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! π diff --git a/server-api/database/db.json b/server-api/database/db.json index f63aa1a..adfffd1 100644 --- a/server-api/database/db.json +++ b/server-api/database/db.json @@ -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 + } ] } \ No newline at end of file