Esempio AJAX con tendina dinamica in JavaScript vanilla, PHP e JSON

Si vuole realizzare un esempio di uso di AJAX più realistico rispetto all’esempio visto qui, dove visualizziamo una tendina con una serie di categorie. Al click di una categoria dalla tendina, la tabella sottostante mostra gli articoli afferenti la categoria scelta nella tendina. Usiamo la tecnologia AJAX in Javascript puro per la gestione della chiamata asincrona, PHP per la gestione backend della query e JSON per esporre i dati. Per chi volesse leggere qualcosa di simile ma realizzato con l’ausilio di JQuery può cliccare qui. Quest’ultima tecnologia sentiamo il dovere di sconsigliarla poiché largamente ritenuta non più vincete per la realizzazione di applicativi web.

La pagina di partenza

Lo script della pagina iniziale è molto semplice. La pagina carica gli elementi e class di Bootstrap per rendere più piacevole la grafica in laboratorio, ma sono accessori, soprattutto per l’alunno che deve scrivere codice su carta all’esame di stato o compito. La tendina, il cuore dello scatenamento js, viene inizializzata con normale codice HTML. Qui abbiamo deciso di caricarla attraverso un semplice script php per completezza e realismo dell’esercitazione. Potrebbe tranquillamente essere scritta con codice HTML secco senza la dinamicità del database. Al posto della tendina, ci potrebbero essere altri elementi scatenanti un ajax. Lasciamo un po’ alla fantasia del lettore ed altre esercitazioni intuire cosa potrebbe accadere se ci fosse un bottone e un campo di input che possa servire da filtro per una ricerca mirata sul database ad esempio. Segue quindi una tabella anche qui semplice HTML arricchita dalle classi di Bootstrap per migliorarne l’aspetto visivo. Fondamentale riportare gli id sia del body della tabella che della tendina categorie. Saranno la porta di accesso per javascript. Ricordiamoci di include il file js articolipercategoria.js in fondo alla pagina php.

<!DOCTYPE html>
<html lang="it">
    <head>
        <title>Prodotti</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    </head>

    <body>
        <div class="container">
        <h1>Carica prodotti con AJAX</h1>
        <!-- FRAMMENTO DI FORM/CODICE CHE SCTENA AJAX AL CAMBIO -->
        <label>Scegli una categoria: </label>
        <select id="categoria">
            <option>Scegli...</option>
            <?php include "categorie.php" ?>
        </select>
        <br><br>
        <!-- VISUALIZZO IL RISULTATO AJAX -->
        <table class="table table-striped">
            <thead>
                <th scope="col">Codice</th>
                <th scope="col">Nome</th>
                <th scope="col">Descrizione</th>
                <th scope="col">Prezzo</th>
                <th scope="col">Qta</th>
            </thead>
            <tbody id="prodotti">

            </tbody>
        </table>
        </div>        
    </body>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
    <script src="js/articolipercategoria.js"></script>
</html>

Tendina categorie

Lo script per caricare le categorie è banalissimo, ottimo esempio di codice anche in chiave prova di esame.

<?php
require ("config.db.php");

$connessione = mysqli_connect($mysql_host, 
                    $mysql_user,
                    $mysql_pass, 
                    $mysql_db);

$query = "SELECT * FROM categoria";
$res   = mysqli_query($connessione, $query) or die("Errore query");
while ($row = mysqli_fetch_assoc($res))
{
    $id = $row['id'];
    echo "<option value='$id'>$id</option>";
}
mysqli_close($connessione);

Lo script del require contiene semplicemente le variabili di connessione al db, che siamo soliti separare dal codice vero e proprio per portare il codice anche su altri server/db

<?php
 
 $mysql_host = "localhost";
 $mysql_user = "root";
 $mysql_pass = "";
 $mysql_db = "test";

Lo script AJAX

Lo script intercetta il cambio sulla tendina categorie cliccabile. Allo scatenarsi dell’evento, viene richiamata la funzione AJAX che prepara la tabella. Viene richiamato lo script PHP prodotti.php a cui viene passato come GET il parametro categoria corrispondente al valore scelto nella tendina e prelevato proprio nella prima riga della funzione ajax(). A questo punto se la connessione allo script ha avuto esito positivo, viene prelevato l’oggetto dom del corpo tabella, lo resetto brutalmente con innerHTML (esistono modi più consoni ma questo è rapido ed indolore) e mi metto n ascolto del JSON proveniente dallo script articoli. Un semplice ciclo scorre il vettore associativo risultante e crea gli oggetti DOM della tabella con righe, colonne e relativi contenuti tutti “appesi” per creare la gerarchia corretta.

const tendinaCategoria= document.querySelector("#categoria");
tendinaCategoria.addEventListener("change", ajax, true);

function ajax()
{
    let categoria= tendinaCategoria.value;
    //console.log(categoria);   per debug

    let xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = 
        function()
        {
            if (this.readyState == 4 && this.status == 200)        
            {
                prodotti= document.querySelector("#prodotti");
                prodotti.innerHTML = ""; // resetta il corpo della tabella, non l'intestazione
                dati = JSON.parse(this.response);  
                for (let chiave in dati)
                {

                    let tr = document.createElement("tr");   
                    let td1 = document.createElement("td"); 
                    let td2= document.createElement("td"); 
                    let td3 = document.createElement("td"); 
                    let td4 = document.createElement("td");
                    let td5 = document.createElement("td");                    
                    
                    let contenuto1 = document.createTextNode(dati[chiave].id);
                    let contenuto2 = document.createTextNode(dati[chiave].nome);
                    let contenuto3 = document.createTextNode(dati[chiave].descrizione);
                    let contenuto4 = document.createTextNode(dati[chiave].prezzo);
                    let contenuto5 = document.createTextNode(dati[chiave].qta);

                    td1.appendChild(contenuto1);
                    td2.appendChild(contenuto2);
                    td3.appendChild(contenuto3);
                    td4.appendChild(contenuto4);
                    td5.appendChild(contenuto5);

                    tr.appendChild(td1);
                    tr.appendChild(td2);
                    tr.appendChild(td3);
                    tr.appendChild(td4);
                    tr.appendChild(td5);                    

                    prodotti.appendChild(tr);

                }
            }

            if (this.readyState == 4 && this.status != 200)
            {
                  console.log("Script PHP non trovato");
            }

        };

        xmlhttp.open("GET", "http://localhost/centinaro/ajax/prodotti.php?categoria="+categoria, true);
        xmlhttp.send();
}   

Lo script articoli

Lo script in realtà non ha molto di complesso rispetto al primo che caricava la tendina iniziale. Qui c’è da indicare al server/browser che si sta restituendo non codice html semplice ma un contenuto JSON e come tale il browser deve comportarsi. Il passaggio di parametri come categoria funziona come per qualsiasi esempio lo studente possa incontrare. Una variabile superglobale $_GET o $_POST preleva il valore passato, qui per pigrizia non distinguiamo e usiamo direttamente $_REQUEST.

<?php
header("Content-type: application/json");

require ("config.db.php");

$connessione = mysqli_connect($mysql_host, 
                    $mysql_user,
                    $mysql_pass, 
                    $mysql_db);

//prelevo il parametro dalla chiamata AJAX GET/SEND
if (isset($_REQUEST["categoria"]))
    $categoria = $_REQUEST["categoria"];
else
   die;

$query = "SELECT * FROM prodotti WHERE id_categoria = '$categoria'";
$res   = mysqli_query($connessione, $query) or die("Errore query");
mysqli_close($connessione);

$jsonarray = array();
while ($row = mysqli_fetch_assoc($res))
{
    $jsonarray[] = $row;
}

echo json_encode($jsonarray);

Dump database

L’esempio è decisamente didattico e poggia i suoi riferimenti su una classica tabella prodotti e categoria. Aggiungiamo il dump del dabase per riprodurre l’esempio di questo post in modo agevole. Valutare se non sia il caso di copiare/ricostruire solo le tabelle in un database esistente, avendo cura di modifica lo script dei parametri mysql precedentemente mostrsato

-- phpMyAdmin SQL Dump
-- version 5.2.0
-- https://www.phpmyadmin.net/
--
-- Host: 127.0.0.1:3306
-- Creato il: Mar 16, 2023 alle 09:47
-- Versione del server: 8.0.31
-- Versione PHP: 8.0.26

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

--
-- Database: `test`
--

-- --------------------------------------------------------

--
-- Struttura della tabella `categoria`
--

DROP TABLE IF EXISTS `categoria`;
CREATE TABLE IF NOT EXISTS `categoria` (
  `id` char(6) NOT NULL,
  `descrizione` varchar(120) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dump dei dati per la tabella `categoria`
--

INSERT INTO `categoria` (`id`, `descrizione`) VALUES
('CANCEL', 'Materiale di cancelleria'),
('ELE000', 'Materiale elettronico di consumo'),
('ELE001', 'Materiale elettronico durevole'),
('ELE002', 'Componenti hardware pc'),
('INF000', 'Prodotti informatici');

-- --------------------------------------------------------



--
-- Struttura della tabella `prodotti`
--

DROP TABLE IF EXISTS `prodotti`;
CREATE TABLE IF NOT EXISTS `prodotti` (
  `id` int NOT NULL AUTO_INCREMENT,
  `nome` varchar(60) NOT NULL,
  `descrizione` varchar(120) NOT NULL,
  `prezzo` float NOT NULL,
  `qta` int NOT NULL,
  `id_categoria` char(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `id_categoria` (`id_categoria`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

--
-- Dump dei dati per la tabella `prodotti`
--

INSERT INTO `prodotti` (`id`, `nome`, `descrizione`, `prezzo`, `qta`, `id_categoria`) VALUES
(1, 'Penna BIC blu', 'Penna BIC blu consumabile', 1.2, 23, 'CANCEL'),
(2, 'VGA Nvidia 3060', 'Scheda video dedicata Nvidia 3060 8 GB DDR5', 800, 2, 'ELE002'),
(3, 'Windows 11 OEM', 'Copia digitale installatore di Window 11', 450, 1, 'INF000'),
(4, 'Adobe Suite', 'Licenza annuale prodotti Adobe', 1200, 1, 'INF000');

--
-- Limiti per le tabelle scaricate
--

--
-- Limiti per la tabella `prodotti`
--
ALTER TABLE `prodotti`
  ADD CONSTRAINT `prodotti_ibfk_1` FOREIGN KEY (`id_categoria`) REFERENCES `categoria` (`id`) ON DELETE SET NULL ON UPDATE CASCADE;
COMMIT;

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Ultima modifica 2 Maggio 2023