ripasso: fixes
This commit is contained in:
@@ -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.**\
|
||||
|
||||
473
javascript/ripasso_completo/esercizio_3_api/index.html
Normal file
473
javascript/ripasso_completo/esercizio_3_api/index.html
Normal 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>
|
||||
577
javascript/ripasso_completo/esercizio_3_api/script.js
Normal file
577
javascript/ripasso_completo/esercizio_3_api/script.js
Normal 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! 🎉
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user