Articolo originale: How to Convert a Static HTML Table to a Dynamic JavaScript Data Grid

Le tabelle HTML sono semplici da usare per presentare piccole quantità di dati, ma per gli utenti può essere complicato usarle quando i dati sono tanti.

Funzionalità come ordinamento, filtri e paginazione rendono più semplice lavorare con molte righe di dati. Possiamo implementare facilmente queste funzionalità passando da una tabella HTML a un componente Data Grid JavaScript.

In questo articolo, useremo l'edizione community gratuita di AG Grid JavaScript Data Grid per convertire una lunga tabella HTML statica in un Data Grid interattivo e semplice da usare. Il codice JavaScript che ci serve è minimale e molto semplice.

Costruiremo il codice di esempio in tre step:

  • Presentare una lista statica contenente dati di elementi Todo (cose da fare) in una tabella HTML.
  • Caricare una lista di elementi Todo da una API REST e presentarla in tabella.
  • Convertire la tabella HTML in un Data Grid per consentire l'ordinamento, i filtri e la paginazione.

Come presentare i dati in tabelle HTML

La prima versione della nostra applicazione ci permetterà di creare la struttura di base di una pagina, assicurandoci di presentare i dati corretti all'utente.

Ho creato un semplice file index.html, mostrato qui sotto:

<!DOCTYPE html>
<html>

<head>
    <title>Table Example</title>
</head>

<body>

    <style>
        table {
            border-collapse: collapse;
            width: 100%;
        }

        td,
        th {
            border: 1px solid #000000;
            text-align: left;
            padding: 8px;
        }
    </style>

    <h1>TODO List</h1>

    <div id="data-table">
        <table id="html-data-table">
            <tr>
                <th>userId</th>
                <th>id</th>
                <th>title</th>
                <th>completed</th>
            </tr>
            <tr>
                <td>1</td>
                <td>1</td>
                <td>My todo 1</td>
                <td>false</td>
            </tr>
        </table>    
    </div>

</body>

</html>

In questo modo, stiamo presentando un singolo elemento Todo in una tabella.

Single Todo Item Shown in an HTML Table
Un singolo elemento Todo mostrato in una tabella HTML

Ecco la pagina di esempio con la tabella HTML statica.

Lo stile dell'elemento table è impostato in modo da avere una larghezza del 100% della pagina, usando width:100%, e le linee di bordo tra le celle della tabella sono state regolate per essere visualizzate come un bordo unico con border-collapse: collapse.

Senza il valore border-collapse la tabella avrebbe l'aspetto dell'immagine qui sotto:

Table Styled without border-collapse
Tabella senza border-collapse

Vantaggi delle tabelle HTML corte

Le tabelle HTML sono un modo molto rapido per presentare piccole quantità di dati in formato di tabella su una pagina.

Le tabelle richiedono delle impostazioni di stile dedicate, in quanto le impostazioni predefinite di un elemento table variano a seconda del browser e spesso fanno sì che non vengano mostrati i bordi, rendendo difficoltosa la consultazione dei dati.

Al momento, la nostra lista di elementi Todo è messa in codice in modo statico nella pagina. Per il prossimo passo recupereremo la lista tramite una API REST usando JavaScript.

Come leggere un JSON da una API e presentare i dati in una tabella HTML

Dato che caricheremo i dato da una API, non inseriremo alcun dato come hardcode in tabella. Per supportare la dinamica di caricamento, rimuoviamo semplicemente la riga di dati da table perché creeremo le righe di dati con JavaScript:

    <div id="data-table">
        <table id="html-data-table">
            <tr>
                <th>userId</th>
                <th>id</th>
                <th>title</th>
                <th>completed</th>
            </tr>
        </table>    
    </div>

Aggiungiamo JavaScript nella pagina index.html immediatamente prima del tag di chiusura body.

    <script type="text/javascript" charset="utf-8">
    </script>
</body>

Per iniziare, scriviamo il codice che legge i dati.

Utilizziamo l'applicazione API REST "{JSON} Placeholder" per questo esempio. Facendo una richiesta GET all'URL https://jsonplaceholder.typicode.com/todos riceveremo una risposta JSON che è una lista di elementi Todo.

Puoi provare tu stesso senza JavaScript, cliccando sul link qui sopra.

Il modo più semplice per fare una richiesta GET all'API è usare la funzione integrata di JavaScript fetch.

    <script type="text/javascript" charset="utf-8">

        fetch('https://jsonplaceholder.typicode.com/todos')
            .then(function (response) {
                return response.json();
            }).then(function (apiJsonData) {
                console.log(apiJsonData);
            })

    </script>
</body>

Per spiegare questo codice, descriviamolo in sezioni qui a seguire:

  • Facciamo una richiesta GET a https://jsonplaceholder.typicode.com/todos
fetch('https://jsonplaceholder.typicode.com/todos')
  • Poi, quando la richiesta è terminata, convertiamo la risposta in un oggetto JavaScript – nel nostro caso, in un array contenente elementi Todo.
.then(function (response) {
	return response.json();
})
  • Poi scriviamo l'oggetto JavaScript sulla console
.then(function (apiJsonData) {
	console.log(apiJsonData);
})

Con questo codice nella nostra applicazione, non vedremo nulla in tabella, ma vedremo un array nella console per sviluppatori del browser in cui potremo vedere i dati.

Data shown when console.log used
Dati mostrati usando console.log

La chiamata API restituisce 200 elementi, ognuno dei quali è un oggetto Todo:

  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  }

Il prossimo passo è presentare i dati nella tabella:

    <script type="text/javascript" charset="utf-8">

        fetch('https://jsonplaceholder.typicode.com/todos')
            .then(function (response) {
                return response.json();
            }).then(function (apiJsonData) {
                console.log(apiJsonData);
                renderDataInTheTable(apiJsonData);
            })

        function renderDataInTheTable(todos) {
            const mytable = document.getElementById("html-data-table");
            todos.forEach(todo => {
                let newRow = document.createElement("tr");
                Object.values(todo).forEach((value) => {
                    let cell = document.createElement("td");
                    cell.innerText = value;
                    newRow.appendChild(cell);
                })
                mytable.appendChild(newRow);
            });
        }
    </script>
</body>

La funzione renderDataInTheTable trova la tabella nel DOM in modo da potervi aggiungere delle righe, poi itera su tutti gli elementi Todo restituiti dalla chiamata API.

Per ogni elemento Todo, il codice crea un nuovo elemento tr, poi aggiunge ogni valore nell'elemento Todo alla tabella come un elemento td.

let newRow = document.createElement("tr");
Object.values(todo).forEach((value) => {
    let cell = document.createElement("td");
    cell.innerText = value;
    newRow.appendChild(cell);
})

Quando il codice fetch e renderDataInTheTable viene aggiunto all'applicazione e la pagina si carica, vedremo che la tabella HTML contiene tutti i 200 elementi Todo.

Dynamically loaded todo list
Lista Todo caricata dinamicamente

Ecco la pagina di esempio della tabella HTML dinamica.

Vantaggi e svantaggi delle tabelle HTML lunghe

Le tabelle HTML sono un modo semplice per presentare dati su una pagina ma non si prestano molto per lunghe liste di dati.

I dati possono essere difficili da trovare, anche se un utente può usare la funzionalità di ricerca integrata nel browser.

Dati che i dati sono presentati in una tabella HTML, un utente non ha nessun modo per ordinarli o filtrarli per mostrare solo le attività completate. Dovremo aggiungere altro codice per far sì che la nostra applicazione implementi le l'ordinamento e i filtri.

Le tabelle HTML crescono automaticamente man mano che vengono aggiunti dati. Ciò può renderle complicate da usare in una applicazione in cui vengono aggiunti molti dati.

Quando abbiamo tanti dati è desiderabile avere una paginazione per restringere la tabella di dati da mostrare fino a un certo numero di righe e permettere all'utente di cliccare sulla pagina successiva per vedere altri elementi. Di nuovo, questa è una funzionalità che per essere gestita richiede altro codice.

Quando la nostra applicazione raggiunge il punto in cui abbiamo bisogno di una maggiore interazione con l'utente, dovremmo considerare di usare un componente Data Grid.

Possiamo usarlo per aggiungere funzionalità aggiuntive come:

  • ordinamento
  • filtri
  • ridimensionamento delle colonne
  • paginazione

Componenti e librerie Data Grid

Esistono molto componenti Data Grid disponibili gratuitamente, ma per la maggior parte sono framework specifici quindi richiedono di scrivere il codice usando React, Angular o Vue.

Per questo esempio sto usando AG Grid, perché la versione gratuita può essere usata con JavaScript, TypeScript, React, Angular o Vue. "AG" sta per Agnostic, che vuol dire che può essere usata con qualsiasi framework.

Quando impari a usare AG Grid con un framework, la stessa API è disponibile per altri framework, facendo sì che la tua conoscenza sia trasferibile su altri progetti.

La versione gratuita di AG Grid può essere usata in applicazioni commerciali, quindi se dovessi espandere l'applicazione demo mostrata qui in una applicazione commerciale di gestione Todo List, potrai ancora usare AG Grid gratuitamente. Molte applicazioni commerciali sono state costruite usando la versione gratuita di AG Grid.

In aggiunta, AG Grid è richiesto di frequente come competenza in domande di assunzione, quindi vale la pena di provarlo.

La versione commerciale di AG Grid ha funzionalità extra, ad esempio, per esportare in Excel e creare grafici, ma non abbiamo bisogno di queste funzionalità in questa demo.

Usare un Data Grid vuol dire che configuriamo Data Grid, forniamo i dati da presentare e la griglia gestisce tutte le altre funzionalità come l'ordinamento, i filtri e la paginazione.

Con pochi cambiamenti, possiamo convertire il nostro codice esistente in modo da utilizzare AG Grid.

Come aggiungere AG Grid JavaScript e CSS

AG Grid è una libreria quindi includeremo il JavaScript richiesto.

Se stai usando strumenti come npm, i vari comandi npm install sono elencati nella documentazione Getting Started with AG Grid.

Stiamo usando semplice JavaScript, quindi possiamo includere lo script nella sezione head.

<head>
    <title>Data Grid Example</title>
    <script src="https://unpkg.com/ag-grid-community/dist/ag-grid-community.min.noStyle.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-grid.css">
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-theme-balham.css">
</head>

Questo include l'edizione community di AG Grid e il CSS necessario per presentare la griglia in modo appropriato.

Il nostro div data-table non ha più bisogno di un elemento table:

    <div id="data-table" class="ag-theme-balham">
    </div>

AG Grid creerà l'HTML per il Data Grid quando lo impostiamo. Aggiungiamo la classe in modo da utilizzare un tema AG Grid. In questo esempio stiamo usando il tema ag-theme-balham.

AG Grid richiede di impostare una larghezza e un'altezza per il div. Ho scelto di aggiungerle nell'elemento style:

    <style>
        #data-table {
            height: 500px;
            width: 100%;
        }
    </style>

La griglia sarà mostrata con un'altezza di 500 pixel e con una larghezza pari al 100% dello schermo, così da replicare lo stile di base che avevamo con la tabella HTML. Ma abbiamo anche i benefici di usare un Data Grid, infatti la dimensione della tabella presentata può essere facilmente controllata e le barre di scorrimento vengono aggiunti se necessario dalla griglia stessa.

Come configurare AG Grid e presentare i dati

La sezione script del codice cambia perché abbiamo bisogno di:

  • Configurare la griglia di dati.
  • Creare una nuova griglia di dati usando la configurazione.
  • Recuperare i dati e aggiungerli alla griglia.

Qui sotto, ecco la sezione iniziale script modificata e di cui spiegherò il senso nei prossimi paragrafi.

    <script type="text/javascript" charset="utf-8">

        const columnDefs = [
            { field: 'userId' },
            { field: 'id' },
            { field: 'title' },
            { field: 'completed' },
        ];

        const gridOptions = {
            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

        const eGridDiv = document.getElementById('data-table');
        new agGrid.Grid(eGridDiv, gridOptions);

        function renderDataInTheTable(api) {
            fetch('https://jsonplaceholder.typicode.com/todos')
                .then(function (response) {
                    return response.json();
                }).then(function (data) {
                    api.setRowData(data);
                    api.sizeColumnsToFit();
                })
        }
    </script>

Un Data Grid è guidato dai dati e dalla configurazione – non dobbiamo scrivere molto codice per creare un Data Grid funzionante.

Creiamo prima un array di oggetti colonna che definiamo come colonne nel Data Grid. Queste colonne avranno una corrispondenza con i dati.

I dati che stiamo ricevendo dalla chiamata API hanno quattro proprietà: "userId", "id", "title" e "completed":

  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  }

Per presentarli nel Data Grid come colonne, creiamo un oggetto con una proprietà field in cui il valore è il nome della proprietà nell'oggetto dati.

        const columnDefs = [
            { field: 'userId' },
            { field: 'id' },
            { field: 'title' },
            { field: 'completed' },
        ];

Poi creiamo l'oggetto gridOptions, che configura il Data Grid:

        const gridOptions = {
            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

Alla proprietà columnDefs è assegnato l'array di oggetti colonna che abbiamo definito precedentemente.

Alla proprietà onGridReady è assegnata una funzione che chiama la funzione renderDataInTheTable quando la griglia è stata creata e presentata nel DOM (cioè quando la griglia è pronta).

Per aggiungere la griglia alla pagina, troviamo l'elemento div che contiene la griglia, poi istanziamo un nuovo oggetto AG Grid per questo elemento e con le opzioni che abbiamo configurato:

        const eGridDiv = document.getElementById('data-table');
        new agGrid.Grid(eGridDiv, gridOptions);

La funzione per recuperare i dati e presentarli nella griglia è molto simile al codice fetch usato per la tabella HTML dinamica. La differenza è che la funzione renderDataInTheTable riceve un oggetto AG Grid API come parametro, consentendoci di chiamare le funzionalità AG Grid per impostare le righe di dati e regolare la dimensione delle colonne per adattarsi alla griglia:

        function renderDataInTheTable(api) {
            fetch('https://jsonplaceholder.typicode.com/todos')
                .then(function (response) {
                    return response.json();
                }).then(function (data) {
                    api.setRowData(data);
                    api.sizeColumnsToFit();
                })
        }

Quando questo codice viene eseguito, praticamente avremo replicato le funzionalità della tabella HTML dinamica, ma ora i dati sono mostrati in un Data Grid con una barra di scorrimento:

initial-data-grid

Per sfruttare i vantaggi di usare un Data Grid e consentire all'utente di ordinare, filtrare e navigare tra i dati, dobbiamo solo modificare la configurazione.

Come implementare l'ordinamento, i filtri e la paginazione

Ecco ciò che abbiamo configurato nel Data Grid finora:

  • quali campi di dati mostrare
  • quali dati usare

Per aggiungere l'ordinamento, i filtri, le colonne ridimensionabili e la paginazione, modifichiamo la configurazione di gridOptions:

        const gridOptions = {

            defaultColDef: {
                sortable: true,
                filter: 'agTextColumnFilter',
                resizable: true
            },

            pagination: true,

            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

Possiamo configurare le colonne in AG Grid individualmente aggiungendo proprietà aggiuntive agli oggetti columnDefs. Oppure, se la stessa funzionalità è richiesta di default in tutte le colonne possiamo configurare defaultColDef.

Ecco la configurazione in modo che il tutto sia ordinabile, filtrabile e ridimensionabile:

            defaultColDef: {
                sortable: true,
                filter: 'agTextColumnFilter',
                resizable: true
            },

Il filtro predefinito che abbiamo definito per tutte le colonne è il filtro di testo.

Per aggiungere la paginazione automatica alla griglia, aggiungiamo la proprietà pagination: true e AG Grid farà il resto per noi.

Data Grid with Sorting, Filtering and Pagination
Data Grid con ordinamento, filtri e paginazione

User-friendly Data Grid

Con il codice qui sopra, abbiamo creato un Data Grid che recupera dinamicamente i dati e li aggiunge in una griglia di dati che supporta l'ordinamento, i filtri e la paginazione.

Ecco la pagina HTML di esempio del Data Grid:

<!DOCTYPE html>
<html>

<head>
    <title>Data Grid Example</title>
    <script src="https://unpkg.com/ag-grid-community/dist/ag-grid-community.min.noStyle.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-grid.css">
    <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-theme-balham.css">
</head>

<body>
    <style>
        #data-table {
            height: 500px;
            width: 100%;
        }
    </style>

    <h1>TODO List</h1>

    <div id="data-table" class="ag-theme-balham">
    </div>

    <script type="text/javascript" charset="utf-8">

        const columnDefs = [
            { field: 'userId' },
            { field: 'id' },
            { field: 'title' },
            { field: 'completed' },
        ];

        const gridOptions = {

            defaultColDef: {
                sortable: true,
                filter: 'agTextColumnFilter',
                resizable: true
            },

            pagination: true,
            
            columnDefs: columnDefs,
            onGridReady: (event) =>{renderDataInTheTable(event.api)}
        };

        const eGridDiv = document.getElementById('data-table');

        new agGrid.Grid(eGridDiv, gridOptions);

        function renderDataInTheTable(api) {
            fetch('https://jsonplaceholder.typicode.com/todos')
                .then(function (response) {
                    return response.json();
                }).then(function (data) {
                    api.setRowData(data);
                    api.sizeColumnsToFit();
                })
        }
    </script>
</body>
</html>

Filtri numerici

Dato che le colonne userId e id sono numeriche, potremmo fare uso di un filtro numerico modificando columnDefs:

        const columnDefs = [
            { field: 'userId', filter: 'agNumberColumnFilter'},
            { field: 'id', filter: 'agNumberColumnFilter'},
            { field: 'title' },
            { field: 'completed' },
        ];

Ecco la pagina HTML di esempio con il Data Grid con filtri numerici.

Esistono tantissime opzioni di configurazione per le colonne, elencate nella documentazione AG Grid, ad esempio, per configurare larghezza, stile e rendere le celle modificabili.

Vantaggi di Data Grid

Per molti siti web, una semplice tabella HTML è un modo assolutamente ragionevole di presentare dati in formato di tabella. È veloce e semplice da capire, e con un po' di CSS la tabella può avere un bell'aspetto per gli utenti.

Quando le pagine diventano più complesse, contengono più dati o richiedono più interattività per l'utente, allora inizia ad avere senso usare un componente o una libreria Data Grid.

I Data Grid forniscono gran parte delle funzionalità di cui hanno bisogno gli utenti, senza dover scrivere troppo codice. Nell'esempio che abbiamo presentato in questo articolo, siamo passati da una tabella dinamica che legge i dati da una API, a un Data Grid che legge i dati da una API con ordinamento, filtri, paginazione e ridimensionamento delle colonne.

Si tratta di un sacco di funzionalità aggiuntive, ma il codice HTML è rimasto della stessa lunghezza e il codice JavaScript che abbiamo aggiunto è meno complicato perché il Data Grid fa tutto il lavoro di presentare i dati.

I Data Grid possono gestire centinaia di migliaia di righe e aggiornarsi velocemente, quindi possono essere usati in sistemi di trading finanziario con i prezzi nelle celle che si aggiornano nel giro di millisecondi.

Se stai usando React, allora in aggiunta a AG Grid puoi dare un'occhiata anche a Material UI o React Table. React Table è una tabella invece di un Data Grid quindi richiede un po' più di codice iniziale.

Sia Material UI che React Table sono disponibili solo per React. AG Grid è indipendente dal framework, quindi funziona con JavaScript, TypeScript, React, Angular e Vue.

Il codice sorgente usato in questo articolo è disponibile su questo repo GitHub nella cartella docs/html-table-to-data-grid.