Esercizio 11 API

This commit is contained in:
2026-02-05 11:57:28 +01:00
parent f4d3e40059
commit 86611fd6e2
37 changed files with 5057 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>API e Asincronia</title>
</head>
<body>
<div class="controls">
<h1>Tutorial: API e Asincronia</h1>
<button id="btn-esegui">▶️ Esegui Codice</button>
<button id="btn-reset">🧹 Pulisci Console</button>
<a href="../index.html" style="position: absolute; top: 20px; left: 20px; text-decoration: none; color: #555; font-weight: bold;">← Dashboard</a>
</div>
<script>
function mostraOutput(stepNumero, messaggio) {
const elemento = document.getElementById(`output-${stepNumero}`);
if (elemento) {
messaggio = messaggio || "❌";
let isObject = (typeof messaggio === 'object' && messaggio !== null);
elemento.textContent = isObject ? JSON.stringify(messaggio, null, 2) : String(messaggio);
elemento.classList.remove("loading");
elemento.style.transition = "";
elemento.style.backgroundColor = "#666";
setTimeout(() => {
elemento.style.transition = "background-color 1s ease";
elemento.style.backgroundColor = "#2d3436";
}, 0);
}
}
[
{
title: "1. Il concetto di Attesa (Simulazione)",
description: "Le API sono lente. Simuliamo un ritardo di 2 secondi prima di mostrare il messaggio.",
outputLabel: "Output Step 1:"
},
{
title: "2. Prima chiamata Fetch (GET)",
description: "Recuperiamo un oggetto JSON reale da un server pubblico.",
outputLabel: "Dati ricevuti:"
},
{
title: "3. Estrazione Dati (JSON)",
description: "La risposta grezza non basta. Dobbiamo convertirla ed estrarre il titolo.",
outputLabel: "Titolo del Post:"
},
{
title: "4. Gestione Errori (Try/Catch)",
description: "Proviamo a chiamare un sito che non esiste per vedere se il codice sopravvive.",
outputLabel: "Stato operazione:"
},
{
title: "5. Lavorare con le Liste (Array)",
description: "Scarichiamo 10 utenti e mostriamo solo i loro nomi.",
outputLabel: "Elenco Utenti:"
},
{
title: "6. Invio Dati (POST)",
description: "Simuliamo l'invio di un nuovo post al server.",
outputLabel: "Risposta Server:"
}
].forEach((step, i) => {
document.write(`
<div class="step-card">
<div class="step-header">
<div class="step-title">${step.title}</div>
</div>
<div class="step-desc">${step.description}</div>
<span class="label">${step.outputLabel}</span>
<div id="output-${i + 1}" class="output-box"></div>
</div>
`);
});
</script>
<script src="script.js"></script>
<script>
document.getElementById('btn-reset').addEventListener('click', () => {
localStorage.clear();
location.reload();
});
document.getElementById('btn-esegui').addEventListener('click', () => {
const el = document.querySelectorAll('[id^="output-"]');
el.forEach(box => box.className = "output-box loading");
eseguiTutorial();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,115 @@
// TUTORIAL INTERATTIVO: API, Fetch e Async/Await
// NON TOCCARE
// Funzioni di supporto per il tutorial
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // Simula server lento
// Funzione pricipale del tutorial
async function eseguiTutorial() {
/**
* ===========================================
* === 1. Il Concetto di Attesa ===
* JavaScript di solito corre veloce. Con i server, deve aspettare.
* La keyword 'await' serve a dire "Fermati qui finché l'operazione non finisce".
*/
// Questa funzione simula un server che ci mette 2 secondi a rispondere
// TODO: Aggiungi 'await' prima di wait(2000) per far funzionare l'attesa.
wait(2000);
mostraOutput(1, "Operazione completata!");
/**
* ===========================================
* === 2. Fetch Base (GET) ===
* Usiamo `fetch(url)` per chiamare un server vero.
*/
// TODO: crea una costante 'urlBase' con l'indirizzo dettato a lezione
// TODO: Esegui una fetch a quell'URL.
// Ricorda di mettere 'await' davanti a fetch!
const urlBase = "https://sito-finto.com";
const rispostaGrezza = null;
mostraOutput(2, rispostaGrezza ? "Risposta ricevuta (Response Object)" : "");
/**
* ===========================================
* === 3. Estrarre il JSON ===
* La risposta grezza contiene status code, headers, ecc.
* A noi servono i dati. Dobbiamo usare il metodo .json().
* ANCHE .json() è asincrono e vuole 'await'.
*/
let nomeUtente = "";
if (rispostaGrezza) {
// TODO: Estrai i dati usando: await rispostaGrezza.json()
const dati = {};
// TODO: Assegna a nomeUtente il valore di dati.nome
nomeUtente = "NOME MANCANTE";
}
mostraOutput(3, nomeUtente);
/**
* ===========================================
* === 4. Gestione Errori (Try / Catch) ===
* Se il server è giù o l'URL è sbagliato, fetch esplode.
* Usiamo try/catch per gestire il problema.
*/
try {
// TODO: Prova a fare una fetch a un URL sbagliato (es. 'https://sito-finto.com')
// Ricorda l'await!
mostraOutput(4);
} catch (errore) {
mostraOutput(4, errore.message);
}
/**
* ===========================================
* === 5. Liste di Dati (Array) ===
* Spesso riceviamo un array di oggetti. Dobbiamo ciclarlo.
*/
// Scarichiamo 5 utenti
const responseUtenti = await fetch(`${urlBase}/users?_limit=5`);
const listaUtenti = await responseUtenti.json();
// TODO: Usa .map() o un ciclo for per creare un array contenente SOLO i nomi degli utenti.
// Esempio: const nomi = listaUtenti.map(u => u.name);
const soloNomi = [];
mostraOutput(5, soloNomi);
/**
* ===========================================
* === 6. Inviare Dati (POST) ===
* Per inviare dati, fetch accetta un secondo parametro di opzioni.
*/
// TODO: Completa l'oggetto con i dati mancanti
const nuovoUtente = {
nome: "",
cognome: "",
dataNascita: "", // Formato: 'YYYY-MM-DD'
comune: "",
email: "",
attivo: true,
avatar: "https://ui-avatars.com/api/?name=Nome+Cognome"
};
// TODO: Completa la fetch.
// Aggiungi method: 'POST'
// Aggiungi headers: { 'Content-Type': 'application/json' }
// Aggiungi body: JSON.stringify(nuovoUtente)
const rispInvio = await fetch(`${urlBase}/posts`, {
});
const risultatoInvio = await rispInvio.json();
mostraOutput(6, risultatoInvio);
}

View File

@@ -0,0 +1,116 @@
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f2f5;
color: #333;
max-width: 900px;
margin: 0 auto;
}
h1 {
text-align: center;
color: #1a73e8;
}
.controls {
text-align: center;
margin-bottom: 30px;
position: sticky;
top: 0px;
z-index: 100;
background-color: white;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
}
button {
padding: 12px 24px;
font-size: 16px;
cursor: pointer;
background-color: #1a73e8;
color: white;
border: none;
border-radius: 10px;
margin: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.1s;
}
button:hover {
transform: scale(1.05);
}
button:active {
transform: scale(0.95);
}
button#btn-reset {
background-color: #ea4335;
}
.step-card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-left: 6px solid #1a73e8;
margin: 20px;
}
.step-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.step-title {
font-weight: bold;
font-size: 1.2em;
color: #1557b0;
}
.step-desc {
font-size: 0.95em;
color: #5f6368;
margin-bottom: 15px;
line-height: 1.5;
}
.output-box {
background: #2d3436;
color: #00c900;
padding: 10px;
font-family: 'Courier New', monospace;
border-radius: 6px;
min-height: 20px;
white-space: pre-wrap;
overflow-x: auto;
position: relative;
}
.output-box.loading::after {
content: "⏳ Attesa server...";
position: absolute;
top: 5px;
right: 5px;
font-size: 0.8em;
color: #e5c07b;
}
.output-box.error {
border: 2px solid #e06c75;
color: #e06c75;
}
.output-box.success {
border-left: 4px solid #98c379;
}
.label {
font-size: 0.8em;
color: #bbbbbb;
margin-bottom: 5px;
display: block;
font-weight: bold;
letter-spacing: 0.5px;
}