Async form submit con spinner

Può capitare che l’invio dei dati di una form in una pagina html richieda del tempo per ottenere il risultato.
Durante lo sviluppo di una web app con Django e Bootstrap dovevo generare dei report sotto forma di file Excel. Questa operazione richiedeva un’attesa prolungata .

Mi è parso eccessivo scomodare frontend framework come React, Angular o Vue per migliorare il feedback utente.

Inizialmente ho usato JQuery, ma visto che (fortunatamente) MS Explorer non era un requisito, ho deciso che potevo fare a meno di qualsiasi libreria, usando del semplice e leggero Javascript (ECMAScript 2017).

Questa soluzione non richiede cambiamenti nel backend. E’ solo necessaria l’esistenza di un campo ID nella form e uno nel button per il submit della stessa (‘submit_form’ e ‘submit_btn’ nell’esempio).

Il funzionamento si basa sull’agganciare all’evento ‘submit’ generato dalla form, una funzione asincrona che invia i campi presenti al suo interno (nel caso di Django anche il csrf_token) e successivamente effettua il download del file generato nel backend.

Potendo controllare la fase di ricezione del file, la funzione è in grado di visualizzare uno spinner (Bootstrap) all’interno del pulsante che ha invocato il submit.

Ho dovuto ricorrere alla generazione di un link temporaneo per poter mantenere il nome del file impostato nel backend. Purtroppo, utilizzando il più conciso window.location.assign("url") in Chrome viene generato un hash al posto del filename.

document.getElementById('submit_form').addEventListener('submit', async function (event) {
    event.preventDefault();
    let sub_btn = document.getElementById('submit_btn');
    sub_btn.setAttribute('disabled', '');
    let sub_btn_html = sub_btn.innerHTML;
    sub_btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> ' + sub_btn_html;
    try {
        let response = await fetch(this.action, { method:'post', body: new FormData(this) });
        if (response.ok) {
            let [, filename] = response.headers.get('content-disposition').split('filename=');
            let blob = await response.blob();
            let downloadLink = document.createElement("a");
            downloadLink.download = filename;
            downloadLink.href = window.URL.createObjectURL(blob);
            downloadLink.style.display = "none";
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);
        }
        else throw new Error("Network response was not OK.");
    } catch (err) {
        console.log('Fetch error: ' + err);
    } finally {
        sub_btn.removeAttribute('disabled');
        sub_btn.innerHTML = sub_btn_html; 
    }
});
Subscribe
Notificami

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.

0 Commenti
Inline Feedbacks
View all comments