Sommario
< Home
Stampa

Evento, azione, reazione

Best practices e worst practices

Javascript è in circolazione da un trentennio, e con le sue evoluzioni e il suo principale ambito di applicazione, ovvero la programmazione web, è probabilmente il linguaggio di programmazione più utilizzato al mondo. In particolare è un linguaggio che per buona parte della sua vita è stato utilizzato da persone con poche e nulle competenze di programmazione e da smanettoni senza competenze. Il risultato è che buona parte dei siti Internet ha codice Javascript di pessima qualità, e soprattutto che si sono diffuse pratiche di sviluppo poco sostenibili, poi riprese dalle varie IA che ne riproducono, per pura forza statistica, i più comuni errori.

Nel frattempo però nell’ultimo decennio, con la maturazione di Javascript, si sono evolute però anche delle buone pratiche che vogliamo riprendere in questa lezione.

I principi che guidano una buona applicazione sono i seguenti:

  • vi è una netta separazione tra HTML, Javascript e CSS:
    • nell’HTML, sebbene sia tecnicamente possibile, non c’è codice JS;
    • nel JS non vi è manipolazione diretta del DOM, ma solo generazione di testo HTML;
    • non si applicano stili in linea in HTML ma si creano classi CSS.
  • l’applicazione ruota intorno al concetto di evento, azione e reazione:
    • l’evento è l’azione dell’utente, ad esempio il click su un pulsante;
    • l’azione: il codice Javascript legge l’azione dell’utente e svolge una qualche azione sui dati;
    • la reazione: dopo l’azione, viene aggiornata, se necessario, la pagina HTML e quindi l’interfaccia utente.

Con queste best practices è possibile scrivere applicazioni ben leggibili, con componenti riusabili altrove, dove è facile effettuare debugging e correggere gli errori, e dove è semplice aggiungere funzionalità. Queste buone pratiche, insieme alla programmazione per componenti, costituiscono il principio di funzionamento dei principali framework Javascript (come Angular, React, Vue).

Generazione di HTML da Javascript

Un sistema veloce, semplice e soprattutto efficiente per creare html consiste nel creare una stringa di testo e “iniettarla” nella pagina, usando la proprietà element.innerHTML. In pratica il codice HTML viene inserito come se fosse testo, ed automaticamente il browser esegue il “rendering” in html e quindi modifica la pagina web.

Ad esempio se abbiamo questo elemento:

<div id="container">

</div>

Ed eseguiamo questo codice[2]:

const container = document.getElementById("container");
container.innerHTML = `
 <div>Ciao</div>
`;

Otterremo questo html:

<div id="container">
    <div>
      Ciao
    </div>
  </div>

L’html una volta generato si comporta esattamente come l’html scritto in fase di coding. Quindi è a sua volta possibile eseguire il binding in oggetti DOM e manipolarlo. 

Quando dobbiamo scrivere html in modo ripetitivo, ovvero ad esempio righe di una tabella dove la struttura del codice è sempre la stessa, ma cambiano solo i dati, conviene usare delle stringhe con dei segnaposto da modificare, ovvero dei template.

Un esempio di applicazione

Facciamo un esempio concreto. Vogliamo creare una web app che contiene una form con 3 campi (nome, cognome, età), un pulsante di conferma e una tabella (inizialmente vuota) con 3 colonne (nome, cognome, ed età). La nostra web app, ogni volta che l’utente inserisce i valori nella form e preme su continua, aggiornerà la lista dei record inseriti ed aggiornerà la tabella con tutti i dati.

Vediamo il codice.

Qui l’html:

<div class="container">
      <div class="row">
        <div class="col-3">
          <label for="inputValue1" class="form-label">Nome</label>
          <input id="inputValue1" type="text" class="form-control" />
          <label for="inputValue2" class="form-label">Cognome</label>
          <input id="inputValue2" type="text" class="form-control" />
          <label for="inputValue3" class="form-label">Età</label>
          <input id="inputValue3" type="text" class="form-control" />
        </div>
      </div>
      <div class="row mt-2">
        <div class="col-3">
          <button type="button" id="button" class="btn btn-primary">
            Invio
          </button>
        </div>
      </div>
      <table id="table" class="table mt-4">
      </table>
    </div>

Come si vede dall’esempio la tabella è inizialmente vuota. 

Passiamo al Javascript. Prima di tutto eseguiamo il binding degli oggetti del DOM:

const button = document.getElementById("button");
const table = document.getElementById("table");
const inputValue1 = document.getElementById("inputValue1");
const inputValue2 = document.getElementById("inputValue2");
const inputValue3 = document.getElementById("inputValue3");

Fatta l’inizializzazione veniamo alle funzionalità.

Alla pressione del pulsante (cioè l’evento), quello che ci aspettiamo che faccia l’applicazione è:

1) aggiungere i nuovi valori inseriti in una variabile lista (che chiamiamo “data”), ovvero l’azione;

2) aggiornare la tabella sulla base dei dati inseriti, ovvero la reazione.

Qui il codice dell’azione che aggiunge il nuovo elemento alla pressione del pulsante (evento ed azione sono quindi collegati):

const data = [];
button.onclick = () => {
  const nome = inputValue1.value;
  const cognome = inputValue2.value;
  const età = inputValue3.value;
  data.push({
    nome: nome,
    cognome: cognome,
    età: età
  });
  render();
};

A un certo punto viene richiamata una funzione render non ancora definita. Questa funzione esegue la reazione: ovvero aggiorna la pagina web creando una tabella html a partire dalla variabile data.

Per fare questo abbiamo però bisogno di creare un template: uno per l’header e uno per la singola riga, dove mettiamo dei segnaposto al posto dei dati, che useremo per tutte le righe inserite.

const tableHeader = `
  <tr>
    <th>Nome</th>
    <th>Cognome</th>
   <th>Età</th>
  </tr>
`;

const template = `
  <tr>
    <td>%NOME</td>
    <td>%COGNOME</td>
    <td>%ETA</td>
 </tr>
`;

A questo punto possiamo scrivere la funzione render, che riscrive tutta la tabella e sostituisce l’html ogni volta che viene eseguita.

Tutto il lavoro consiste in manipolazione di stringhe:

– prima si inserisce il table header sopra definito;
– poi per ogni riga, si trasforma il template in una stringa contenente effettivo codice html coi dati;
– infine si inserisce la stringa creata al posto dell’html esistente.

const render = () => {
  let html = tableHeader;
  data.forEach((element) => {
    let rowHtml = template.replace("%NOME", element.nome);
    rowHtml = rowHtml.replace("%COGNOME", element.cognome);
    rowHtml = rowHtml.replace("%ETA", element.età);
    console.log(rowHtml);
    html += rowHtml;
  });
  table.innerHTML = html;
};

Ricapitolando vediamo il meccanismo di funzionamento:

1) L’utente compila i campi e fa click su “aggiungi” (evento).

2) La funzione “button.onclick” fa partire l’aggiunta del nuovo elemento alla lista (azione).

3) La funzione render() legge la lista e ricrea tutta la tabella da zero (reazione).

Con questo sistema  non si manipola quindi l’html precedente, ma lo si cancella e lo si ricrea. Questo sistema è più efficiente di altri sistemi, come per esempio quello di aggiungere solo l’html di una riga alla volta: in questo caso dovremmo analizzare l’html già generato analizzando quindi il DOM ed eseguendo il binding. Questa tecnica è ormai oggi considerata deprecata perché molto inefficiente nei browser moderni. E’ sempre più efficiente invece cancellare e rigenerare l’html, operazione che viene agevolata dai motori di visualizzazione dei moderni browsers.

Scrivere il codice in questo modo aiuta a tenere separati i due ambiti principali dell’applicazione:

  • i dati sono memorizzati in modo efficiente usando array, dizionari, ecc. e sono usati dal Javascript, in una parola, il modello dati;
  • l’interfaccia utente è invece in HTML e JS crea solo delle stringhe di testo HTML tramite una funzione render: JS non manipola MAI l’HTML, ma esegue il binding solo delle parti della pagina da leggere (es. form) o da cui intercettare eventi (es. pulsanti);

Trasformazioni dai dati all’HTML

E’ possibile usare map() e join() per trasformare velocemente una tabella con dei dati in html. Per generare un elenco di colonne di tabella HTML si può fare così:

const list = [1,2,3];
const htmlElements = list.map(element => "<td>" + element + "</td>");
// Otterremo: ["<td>1</td>", "<td>2</td>", "<td>3</td>"]

Da qui è poi possibile usare join():

const htmlRow = htmlElements.join(""); 
// "<td>1</td><td>2</td><td>3</td>"

La stessa tecnica si può replicare anche per le righe.

const data = [[1,2,3], [4,5,6]];
let htmlTable = "<table>";
htmlTable += data.map((row) => "<tr>"
  + row.map((col) => "<td>"+ col + "</td>").join("") 
  + "</tr>").join(""));
htmlTable += "</table";