modulo 11 di 24 / Ore 21-40
Moduli 9-16 / Checkpoint 40 ore
Azioni UI: prestare, restituire, segnalare guasto
Come implementare nella Presentation i tre pulsanti principali del Registro con event listener che chiamano i casi d'uso senza contenere logica di dominio.
Azioni UI: prestare, restituire, segnalare guasto
Questa lezione appartiene al Modulo 11 — Presentation layer.
Il lavoro continua il progetto guida, il Registro del laboratorio scolastico. Implementi i tre pulsanti principali dell’interfaccia: Presta, Restituisci, Segnala Guasto — ognuno con un event listener che delega tutta la logica ai casi d’uso.
Obiettivo della lezione
Scrivi tre gestori di eventi nella Presentation che collegano i pulsanti dell’interfaccia ai casi d’uso apriPrestito, chiudiPrestito e segnalaGuasto, senza mettere logica di dominio nei gestori stessi.
Alla fine devi avere: tre addEventListener in presentation/, ognuno che legge l’input, chiama il caso d’uso, gestisce l’errore e aggiorna la UI.
Il ruolo della Presentation
La Presentation fa tre cose, e solo queste:
- raccoglie l’input dell’utente (da form, pulsanti, selezioni)
- chiama il caso d’uso corrispondente con i dati raccolti
- mostra il risultato o il messaggio di errore
La Presentation non deve mai contenere questo tipo di logica:
// SBAGLIATO — regola di dominio nella UI
btnPresta.addEventListener("click", () => {
if (strumento.stato !== "disponibile") {
mostraMessaggio("Non disponibile");
return;
}
strumento.stato = "in-prestito"; // modifica diretta dello stato
archivio.salva(strumenti);
mostraMessaggio("Prestito aperto");
});
Quella verifica sullo stato appartiene al dominio. La UI non deve sapere quali stati esistono.
Nel Registro del laboratorio
L’HTML della pagina principale ha tre pulsanti:
<!-- nella pagina index.html -->
<input type="text" id="inputStrumentoId" placeholder="ID strumento">
<button id="btnPresta">Presta</button>
<button id="btnRestituisci">Restituisci</button>
<button id="btnSegnalaGuasto">Segnala Guasto</button>
<p id="messaggioUscita"></p>
Il file presentation/azioni.js (o main.js) collega questi elementi ai casi d’uso.
Procedura guidata
- Recupera i riferimenti agli elementi HTML con
document.getElementById. - Scrivi la funzione
mostraMessaggio(testo)che aggiornamessaggioUscita. - Scrivi il gestore per “Presta” che chiama
apriPrestito. - Scrivi il gestore per “Restituisci” che chiama
chiudiPrestito. - Scrivi il gestore per “Segnala Guasto” che chiama
segnalaGuasto. - Ogni gestore deve gestire gli errori con un
try/catche mostrare un messaggio leggibile. - Verifica manualmente nel browser: presta uno strumento, poi restituiscilo, poi segnala il guasto.
Attività in classe
Compila questa scheda prima di scrivere il codice:
## Tre azioni UI
Pulsante: Presta
Dati raccolti dall'input:
Caso d'uso chiamato:
Messaggi di successo e errore:
Pulsante: Restituisci
Dati raccolti dall'input:
Caso d'uso chiamato:
Messaggi di successo e errore:
Pulsante: Segnala Guasto
Dati raccolti dall'input:
Caso d'uso chiamato:
Messaggi di successo e errore:
Spiegazione guidata per studiare
Il confine tra Presentation e Application si vede bene guardando i gestori di eventi. Un gestore ben scritto è sempre brevissimo: prende i valori dall’input, chiama una funzione, gestisce il catch. Se il gestore cresce oltre 15 righe, probabilmente contiene logica che appartiene altrove.
Il try/catch nel gestore serve a convertire gli errori tecnici (stringhe come "STRUMENTO_NON_TROVATO") in messaggi comprensibili per l’utente. Questa conversione è responsabilità della Presentation — non del caso d’uso, che lancia errori con codici interni, e non del dominio.
Passo per passo nel progetto
- Crea
src/presentation/azioni.js(o aggiungi amain.js). Importa i casi d’uso e gli archivi:
// presentation/azioni.js
import { apriPrestito } from "../application/apriPrestito.js";
import { chiudiPrestito } from "../application/chiudiPrestito.js";
import { segnalaGuasto } from "../application/segnalaGuasto.js";
import * as archivioStrumenti from "../infrastructure/archivioStrumentiLocalStorage.js";
import * as archivioPrestiti from "../infrastructure/archivioPrestitiLocalStorage.js";
const inputId = document.getElementById("inputStrumentoId");
const btnPresta = document.getElementById("btnPresta");
const btnRestituisci = document.getElementById("btnRestituisci");
const btnSegnalaGuasto = document.getElementById("btnSegnalaGuasto");
const messaggioUscita = document.getElementById("messaggioUscita");
function mostraMessaggio(testo) {
messaggioUscita.textContent = testo;
}
- Scrivi il gestore per “Presta”:
btnPresta.addEventListener("click", () => {
const strumentoId = inputId.value.trim();
if (!strumentoId) {
mostraMessaggio("Inserisci l'ID dello strumento.");
return;
}
try {
const prestito = apriPrestito(
{ strumentoId },
archivioStrumenti,
archivioPrestiti
);
mostraMessaggio(`Prestito aperto. ID prestito: ${prestito.id}`);
} catch (errore) {
const messaggi = {
"STRUMENTO_NON_TROVATO": "Strumento non trovato.",
"STRUMENTO_NON_DISPONIBILE": "Lo strumento non è disponibile.",
"ARCHIVIO_NON_DISPONIBILE": "Errore nel salvataggio. Riprova."
};
mostraMessaggio(messaggi[errore.message] ?? "Operazione non riuscita.");
}
});
- Scrivi il gestore per “Restituisci”:
btnRestituisci.addEventListener("click", () => {
const strumentoId = inputId.value.trim();
if (!strumentoId) {
mostraMessaggio("Inserisci l'ID dello strumento.");
return;
}
try {
chiudiPrestito({ strumentoId }, archivioStrumenti, archivioPrestiti);
mostraMessaggio("Prestito chiuso. Strumento restituito.");
} catch (errore) {
const messaggi = {
"PRESTITO_NON_TROVATO": "Nessun prestito aperto per questo strumento.",
"STRUMENTO_NON_TROVATO": "Strumento non trovato."
};
mostraMessaggio(messaggi[errore.message] ?? "Operazione non riuscita.");
}
});
- Scrivi il gestore per “Segnala Guasto”:
btnSegnalaGuasto.addEventListener("click", () => {
const strumentoId = inputId.value.trim();
if (!strumentoId) {
mostraMessaggio("Inserisci l'ID dello strumento.");
return;
}
try {
segnalaGuasto({ strumentoId }, archivioStrumenti);
mostraMessaggio("Guasto segnalato. Lo strumento è ora fuori uso.");
} catch (errore) {
const messaggi = {
"STRUMENTO_NON_TROVATO": "Strumento non trovato.",
"STRUMENTO_GIA_GUASTO": "Lo strumento è già segnalato come guasto."
};
mostraMessaggio(messaggi[errore.message] ?? "Operazione non riuscita.");
}
});
Esempio JS — il caso d’uso segnalaGuasto
Per completare l’esempio, ecco come segnalaGuasto potrebbe essere implementato in application/:
// application/segnalaGuasto.js
export function segnalaGuasto({ strumentoId }, archivio) {
const strumenti = archivio.caricaTutti();
const strumento = strumenti.find(s => s.id === strumentoId);
if (!strumento) throw new Error("STRUMENTO_NON_TROVATO");
if (strumento.stato === "guasto") throw new Error("STRUMENTO_GIA_GUASTO");
strumento.stato = "guasto";
archivio.salva(strumenti);
}
Il gestore nella UI non sa nulla degli stati "disponibile" o "guasto" — sa solo chiamare segnalaGuasto e gestire le eccezioni con messaggi leggibili.
Prova di comprensione
Rispondi per iscritto:
- Perché il controllo
if (strumento.stato !== "disponibile")non deve stare nel gestore del click? - Cosa fa esattamente
messaggi[errore.message] ?? "Operazione non riuscita."? - Se aggiungessi un quarto pulsante “Metti in manutenzione”, cosa cambieresti nella Presentation e cosa nell’Application?
- Come verificheresti che il click su “Restituisci” non modifica lo stato se lo strumento non ha prestiti aperti?
Errori da evitare
- scrivere condizioni
if (strumento.stato === ...)nei gestori degli eventi; - chiamare direttamente
archivio.salva()dalla Presentation; - mostrare all’utente messaggi come
"Error: STRUMENTO_NON_TROVATO"; - dimenticare il controllo sull’input vuoto prima di chiamare il caso d’uso;
- mettere tutta la logica in
main.jsinvece di separare la Presentation.
Prodotto da consegnare
I tre gestori di eventi funzionanti in presentation/azioni.js. Come verifica: nel browser, presta uno strumento, poi restituiscilo, poi segnala il guasto su un altro strumento — tutti e tre i flussi devono funzionare con messaggi chiari.
Checklist di chiusura
- i tre
addEventListenerchiamano solo casi d’uso, senza logica di dominio; - ogni gestore ha un
try/catchcon messaggi in italiano; - l’input viene validato (non vuoto) prima di chiamare il caso d’uso;
- nessun gestore chiama direttamente l’archivio;
- ho verificato tutti e tre i flussi nel browser.
Risultato atteso
Ho implementato i tre gestori nella Presentation.
Presta chiama apriPrestito, Restituisci chiama chiudiPrestito, Segnala Guasto chiama segnalaGuasto.
I messaggi di errore sono in italiano e leggibili.
Ho verificato manualmente tutti e tre i flussi nel browser.
Il prossimo passo: aggiornare la lista strumenti dopo ogni azione.