19 KiB
Costruire un'app bancaria Parte 3: Metodi per recuperare e utilizzare i dati
Quiz Pre-Lezione
Introduzione
Al centro di ogni applicazione web c'è il dato. I dati possono assumere molte forme, ma il loro scopo principale è sempre quello di mostrare informazioni all'utente. Con le app web che diventano sempre più interattive e complesse, il modo in cui l'utente accede e interagisce con le informazioni è ora una parte fondamentale dello sviluppo web.
In questa lezione, vedremo come recuperare dati da un server in modo asincrono e utilizzare questi dati per mostrare informazioni su una pagina web senza ricaricare l'HTML.
Prerequisiti
È necessario aver costruito la parte Modulo di Login e Registrazione dell'app web per questa lezione. Inoltre, è necessario installare Node.js e eseguire l'API del server localmente per ottenere i dati dell'account.
Puoi verificare che il server stia funzionando correttamente eseguendo questo comando in un terminale:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
AJAX e recupero dei dati
I siti web tradizionali aggiornano il contenuto mostrato quando l'utente seleziona un link o invia dati tramite un modulo, ricaricando l'intera pagina HTML. Ogni volta che è necessario caricare nuovi dati, il server web restituisce una nuova pagina HTML che deve essere elaborata dal browser, interrompendo l'azione corrente dell'utente e limitando le interazioni durante il caricamento. Questo flusso di lavoro è anche chiamato Applicazione Multi-Pagina o MPA.
Quando le applicazioni web hanno iniziato a diventare più complesse e interattive, è emersa una nuova tecnica chiamata AJAX (JavaScript e XML Asincrono). Questa tecnica consente alle app web di inviare e recuperare dati da un server in modo asincrono utilizzando JavaScript, senza dover ricaricare la pagina HTML, risultando in aggiornamenti più rapidi e interazioni più fluide. Quando vengono ricevuti nuovi dati dal server, la pagina HTML corrente può essere aggiornata con JavaScript utilizzando l'API DOM. Nel tempo, questo approccio si è evoluto in ciò che ora viene chiamato Applicazione Singola Pagina o SPA.
Quando AJAX è stato introdotto per la prima volta, l'unica API disponibile per recuperare dati in modo asincrono era XMLHttpRequest
. Tuttavia, i browser moderni ora implementano anche l'API Fetch
, più comoda e potente, che utilizza le promesse ed è più adatta per manipolare dati JSON.
Sebbene tutti i browser moderni supportino l'API
Fetch
, se desideri che la tua applicazione web funzioni su browser legacy o vecchi, è sempre una buona idea controllare prima la tabella di compatibilità su caniuse.com.
Compito
Nella lezione precedente abbiamo implementato il modulo di registrazione per creare un account. Ora aggiungeremo il codice per effettuare il login utilizzando un account esistente e recuperare i suoi dati. Apri il file app.js
e aggiungi una nuova funzione login
:
async function login() {
const loginForm = document.getElementById('loginForm')
const user = loginForm.user.value;
}
Qui iniziamo recuperando l'elemento del modulo con getElementById()
, e poi otteniamo il nome utente dall'input con loginForm.user.value
. Ogni controllo del modulo può essere accessibile tramite il suo nome (impostato nell'HTML utilizzando l'attributo name
) come proprietà del modulo.
In modo simile a quanto fatto per la registrazione, creeremo un'altra funzione per eseguire una richiesta al server, ma questa volta per recuperare i dati dell'account:
async function getAccount(user) {
try {
const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user));
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}
Utilizziamo l'API fetch
per richiedere i dati in modo asincrono dal server, ma questa volta non abbiamo bisogno di parametri extra oltre all'URL da chiamare, poiché stiamo solo interrogando i dati. Per impostazione predefinita, fetch
crea una richiesta HTTP GET
, che è ciò che ci serve qui.
✅ encodeURIComponent()
è una funzione che esegue l'escape dei caratteri speciali per l'URL. Quali problemi potremmo avere se non chiamiamo questa funzione e utilizziamo direttamente il valore user
nell'URL?
Ora aggiorniamo la nostra funzione login
per utilizzare getAccount
:
async function login() {
const loginForm = document.getElementById('loginForm')
const user = loginForm.user.value;
const data = await getAccount(user);
if (data.error) {
return console.log('loginError', data.error);
}
account = data;
navigate('/dashboard');
}
Poiché getAccount
è una funzione asincrona, dobbiamo abbinarla alla parola chiave await
per attendere il risultato del server. Come per qualsiasi richiesta al server, dobbiamo anche gestire i casi di errore. Per ora aggiungeremo solo un messaggio di log per mostrare l'errore e ci torneremo più avanti.
Dobbiamo poi salvare i dati da qualche parte per poterli utilizzare successivamente per mostrare le informazioni della dashboard. Poiché la variabile account
non esiste ancora, creeremo una variabile globale all'inizio del nostro file:
let account = null;
Dopo che i dati dell'utente sono stati salvati in una variabile, possiamo navigare dalla pagina login alla dashboard utilizzando la funzione navigate()
che abbiamo già.
Infine, dobbiamo chiamare la nostra funzione login
quando il modulo di login viene inviato, modificando l'HTML:
<form id="loginForm" action="javascript:login()">
Verifica che tutto funzioni correttamente registrando un nuovo account e provando a effettuare il login utilizzando lo stesso account.
Prima di passare alla parte successiva, possiamo anche completare la funzione register
aggiungendo questo alla fine della funzione:
account = result;
navigate('/dashboard');
✅ Sapevi che per impostazione predefinita puoi chiamare le API del server solo dal dominio e porta della pagina web che stai visualizzando? Questo è un meccanismo di sicurezza imposto dai browser. Ma aspetta, la nostra app web è in esecuzione su localhost:3000
mentre l'API del server è in esecuzione su localhost:5000
, perché funziona? Utilizzando una tecnica chiamata Condivisione delle Risorse tra Origini Diverse (CORS), è possibile eseguire richieste HTTP cross-origin se il server aggiunge intestazioni speciali alla risposta, consentendo eccezioni per domini specifici.
Scopri di più sulle API seguendo questa lezione
Aggiornare l'HTML per mostrare i dati
Ora che abbiamo i dati dell'utente, dobbiamo aggiornare l'HTML esistente per mostrarli. Sappiamo già come recuperare un elemento dal DOM utilizzando, ad esempio, document.getElementById()
. Dopo aver ottenuto un elemento base, ecco alcune API che puoi utilizzare per modificarlo o aggiungere elementi figli:
-
Utilizzando la proprietà
textContent
puoi cambiare il testo di un elemento. Nota che modificare questo valore rimuove tutti i figli dell'elemento (se presenti) e li sostituisce con il testo fornito. Pertanto, è anche un metodo efficiente per rimuovere tutti i figli di un dato elemento assegnando una stringa vuota''
. -
Utilizzando
document.createElement()
insieme al metodoappend()
puoi creare e allegare uno o più nuovi elementi figli.
✅ Utilizzando la proprietà innerHTML
di un elemento è anche possibile modificare i suoi contenuti HTML, ma questa dovrebbe essere evitata poiché è vulnerabile agli attacchi di cross-site scripting (XSS).
Compito
Prima di passare alla schermata della dashboard, c'è un'altra cosa che dovremmo fare nella pagina login. Attualmente, se provi a effettuare il login con un nome utente che non esiste, viene mostrato un messaggio nella console, ma per un utente normale nulla cambia e non si sa cosa stia succedendo.
Aggiungiamo un elemento segnaposto nel modulo di login dove possiamo mostrare un messaggio di errore, se necessario. Un buon posto sarebbe appena prima del <button>
di login:
...
<div id="loginError"></div>
<button>Login</button>
...
Questo elemento <div>
è vuoto, il che significa che nulla verrà mostrato sullo schermo fino a quando non aggiungiamo del contenuto. Gli diamo anche un id
per poterlo recuperare facilmente con JavaScript.
Torna al file app.js
e crea una nuova funzione helper updateElement
:
function updateElement(id, text) {
const element = document.getElementById(id);
element.textContent = text;
}
Questa funzione è piuttosto semplice: dato un id dell'elemento e un testo, aggiornerà il contenuto testuale dell'elemento DOM con l'id
corrispondente. Utilizziamo questo metodo al posto del precedente messaggio di errore nella funzione login
:
if (data.error) {
return updateElement('loginError', data.error);
}
Ora, se provi a effettuare il login con un account non valido, dovresti vedere qualcosa del genere:
Ora abbiamo un testo di errore che appare visivamente, ma se lo provi con un lettore di schermo noterai che nulla viene annunciato. Per fare in modo che il testo aggiunto dinamicamente a una pagina venga annunciato dai lettori di schermo, sarà necessario utilizzare qualcosa chiamato Live Region. Qui utilizzeremo un tipo specifico di live region chiamato alert:
<div id="loginError" role="alert"></div>
Implementa lo stesso comportamento per gli errori della funzione register
(non dimenticare di aggiornare l'HTML).
Mostrare informazioni sulla dashboard
Utilizzando le stesse tecniche appena viste, ci occuperemo anche di mostrare le informazioni dell'account sulla pagina della dashboard.
Questo è l'aspetto di un oggetto account ricevuto dal server:
{
"user": "test",
"currency": "$",
"description": "Test account",
"balance": 75,
"transactions": [
{ "id": "1", "date": "2020-10-01", "object": "Pocket money", "amount": 50 },
{ "id": "2", "date": "2020-10-03", "object": "Book", "amount": -10 },
{ "id": "3", "date": "2020-10-04", "object": "Sandwich", "amount": -5 }
],
}
Nota: per semplificarti la vita, puoi utilizzare l'account
test
preesistente che è già popolato con dati.
Compito
Iniziamo sostituendo la sezione "Saldo" nell'HTML per aggiungere elementi segnaposto:
<section>
Balance: <span id="balance"></span><span id="currency"></span>
</section>
Aggiungeremo anche una nuova sezione appena sotto per mostrare la descrizione dell'account:
<h2 id="description"></h2>
✅ Poiché la descrizione dell'account funziona come un titolo per il contenuto sottostante, è contrassegnata semanticamente come intestazione. Scopri di più su come la struttura delle intestazioni sia importante per l'accessibilità e osserva criticamente la pagina per determinare cos'altro potrebbe essere un'intestazione.
Successivamente, creeremo una nuova funzione in app.js
per riempire il segnaposto:
function updateDashboard() {
if (!account) {
return navigate('/login');
}
updateElement('description', account.description);
updateElement('balance', account.balance.toFixed(2));
updateElement('currency', account.currency);
}
Per prima cosa, verifichiamo di avere i dati dell'account necessari prima di procedere. Poi utilizziamo la funzione updateElement()
che abbiamo creato in precedenza per aggiornare l'HTML.
Per rendere la visualizzazione del saldo più gradevole, utilizziamo il metodo
toFixed(2)
per forzare la visualizzazione del valore con 2 cifre decimali.
Ora dobbiamo chiamare la nostra funzione updateDashboard()
ogni volta che la dashboard viene caricata. Se hai già completato l'assegnazione della lezione 1 questo dovrebbe essere semplice, altrimenti puoi utilizzare la seguente implementazione.
Aggiungi questo codice alla fine della funzione updateRoute()
:
if (typeof route.init === 'function') {
route.init();
}
E aggiorna la definizione delle rotte con:
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: updateDashboard }
};
Con questa modifica, ogni volta che la pagina della dashboard viene mostrata, viene chiamata la funzione updateDashboard()
. Dopo un login, dovresti quindi essere in grado di vedere il saldo dell'account, la valuta e la descrizione.
Creare righe della tabella dinamicamente con template HTML
Nella prima lezione abbiamo utilizzato template HTML insieme al metodo appendChild()
per implementare la navigazione nella nostra app. I template possono anche essere più piccoli e utilizzati per popolare dinamicamente parti ripetitive di una pagina.
Utilizzeremo un approccio simile per mostrare l'elenco delle transazioni nella tabella HTML.
Compito
Aggiungi un nuovo template nel <body>
dell'HTML:
<template id="transaction">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</template>
Questo template rappresenta una singola riga della tabella, con le 3 colonne che vogliamo popolare: data, oggetto e importo di una transazione.
Poi, aggiungi questa proprietà id
all'elemento <tbody>
della tabella all'interno del template della dashboard per renderlo più facile da trovare utilizzando JavaScript:
<tbody id="transactions"></tbody>
Il nostro HTML è pronto, passiamo al codice JavaScript e creiamo una nuova funzione createTransactionRow
:
function createTransactionRow(transaction) {
const template = document.getElementById('transaction');
const transactionRow = template.content.cloneNode(true);
const tr = transactionRow.querySelector('tr');
tr.children[0].textContent = transaction.date;
tr.children[1].textContent = transaction.object;
tr.children[2].textContent = transaction.amount.toFixed(2);
return transactionRow;
}
Questa funzione fa esattamente ciò che il suo nome implica: utilizzando il template che abbiamo creato prima, crea una nuova riga della tabella e ne riempie i contenuti utilizzando i dati della transazione. Utilizzeremo questa funzione nella nostra updateDashboard()
per popolare la tabella:
const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
const transactionRow = createTransactionRow(transaction);
transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);
Qui utilizziamo il metodo document.createDocumentFragment()
che crea un nuovo frammento DOM su cui possiamo lavorare, prima di allegarlo finalmente alla nostra tabella HTML.
C'è ancora una cosa che dobbiamo fare prima che questo codice possa funzionare, poiché la nostra funzione updateElement()
attualmente supporta solo contenuti testuali. Modifichiamo un po' il suo codice:
function updateElement(id, textOrNode) {
const element = document.getElementById(id);
element.textContent = ''; // Removes all children
element.append(textOrNode);
}
Utilizziamo il metodo append()
poiché consente di allegare sia testo che Nodi DOM a un elemento padre, perfetto per tutti i nostri casi d'uso.
Se provi a utilizzare l'account test
per accedere, ora dovresti vedere un elenco di transazioni nella dashboard 🎉.
🚀 Sfida
Collaborate per far sì che la pagina della dashboard sembri una vera app bancaria. Se hai già stilizzato la tua app, prova a utilizzare media queries per creare un design responsivo che funzioni bene sia su dispositivi desktop che mobili.
Ecco un esempio di una pagina dashboard stilizzata:
Quiz post-lezione
Compito
Refattorizza e commenta il tuo codice
Disclaimer:
Questo documento è stato tradotto utilizzando il servizio di traduzione automatica Co-op Translator. Sebbene ci impegniamo per garantire l'accuratezza, si prega di notare che le traduzioni automatiche possono contenere errori o imprecisioni. Il documento originale nella sua lingua nativa dovrebbe essere considerato la fonte autorevole. Per informazioni critiche, si consiglia una traduzione professionale eseguita da un traduttore umano. Non siamo responsabili per eventuali fraintendimenti o interpretazioni errate derivanti dall'uso di questa traduzione.