Solitamente il primo pensiero di chi sente parlare per la prima volta di template JavaScript è: E cosa me ne faccio di un sistema di template per JavaScript?. Questo finché non si ritrova a dover caricare porzioni di HTML in modo dinamico, magari attraverso AJAX.
Nella pratica comune la gestione della composizione di codice HTML come risposta ad una chiamata AJAX è stata affidata alle applicazioni lato server.
Lo svantaggio principale di questa tecnica è che la stringa ricevuta in risposta non è facilmente interpretabile dall'applicazione JavaScript: il server ci sta fornendo una lista formattata di dati oppure un box con un messaggio di errore?
Inoltre, il fatto di restituire una stringa contenente testo, tag e attributi comporta un aumento nella pesantezza della richiesta che si traduce in più banda consumata e maggior tempo d'attesa.
JSON è la risposta
La soluzione migliore a questi problemi è quella di rispondere ad una richiesta AJAX fornendo i dati in formato JSON e lasciare all'applicazione JavaScript il compito di interpretarli e, se necessario, tradurli in HTML.
JSON è un formato ormai standard di interscambio dei dati, creato da Douglas Crockford e basato sulla struttura degli oggetti e degli array in JavaScript. Ecco un esempio di risposta JSON alla richiesta di una lista di post:
{
stato : 'success',
totale : 10,
posts : [
{ titolo : 'Primo Post', testo : 'Questo è il secondo testo'},
{ titolo : 'Secondo Post', testo : 'Questo è il secondo testo'}
// ...
]
}
Lato server, la maggior parte dei linguaggi è in grado di gestire la conversione dei dati in JSON nativamente oppure attraverso librerie dedicate.
Bisogna sottolineare che i dati restituiti saranno comunque sempre sotto forma di stringa, che andrà perciò interpretata per essere fruibile in JavaScript. Nelle versioni più recenti dei browser è possibile usare il metodo nativo JSON.parse()
, mentre la compatibilità con i browser più datati è garantita in tutte le librerie più diffuse o da uno script universale scaricabile attraverso questo link.
Da JSON ad HTML: template engines
Per tradurre un oggetto JSON in HTML si fa spesso ricorso ai template engine. Questi script prevedono la definizione di un blocco di HTML (detto appunto template) nel quale identificare dei segnaposto (placeholders) da sostituire con i dati da visualizzare. Nel caso di JSON, i segnaposto sono solitamente rappresentati dalle proprietà dell'oggetto, che verranno sostituite con i relativi valori. La forma più semplice di template engine per tradurre un oggetto JSON in HTML può essere realizzata con una funzione del tipo:
function parseTemplate(tmpl, data) {
var regexp;
for (placeholder in data) {
// useremo le parantesi graffe per
// identificare i placeholder
regexp = new RegExp('{' + placeholder + '}', 'g');
tmpl = tmpl.replace(regexp, data[placeholder]);
}
return tmpl;
}
Su questa pagina potete vedere la funzione all'opera; tuttavia, per convertire oggetti complessi, è meglio far riferimento a soluzioni più complete.
Librerie disponibili
Visto il crescente peso della programmazione lato client nelle applicazioni web, negli ultimi anni sono stati realizzati vari template engine, diversi per funzionalità, performance ed integrazione con i framework più diffusi. I più conosciuti al momento sono Mustache/Handlebars, jQuery Template, Pure ed EJS. In questo articolo vedremo alcune caratteristiche dei primi due.
Mustache ed Handlebars
Mustache (e la sua estensione Handlebars) hanno sempre riscosso molto successo fra gli sviluppatori per la semplicità della sintassi e le feature offerte. La libreria è indipendente da framework specifici ma esistono plugin per l'integrazione con jQuery, Dojo e YUI.
I template per Mustache possono essere assegnati come stringa ad una variabile e i placeholder sono identificati da due parentesi graffe, ad esempio: {{miopplaceholder}}
. Una delle feature più interessanti della libreria è il supporto a valori enumerabili (array) e condizionali (boolean) che permettono di realizzare strutture HTML complesse. Ecco uno script di esempio.
A parte estenderne le funzionalità (ad esempio supportando selettori discendenti del tipo oggetto.proprietà
), la differenza principale fra Handlebars e Mustache sta nel fatto che la prima permette di compilare i template, riducendo il tempo di rendering quando un blocco di codice viene creato più di una volta.
Nel caso di applicazioni particolarmente dinamiche o basate su database esterni (Twitter, Facebook, ecc) Handlebars presenta quindi notevoli vantaggi relativamente alla reattività delle pagine ed al carico sul browser.
Un'altra caratteristica avanzata di Handlebars è il supporto ai partials. Con tale termine si indica la possibilità di innestare template l'uno nell'altro, permettendone la riusabilità e la realizzazione di codice più ordinato:
// il placeholder per i partials è
// {{> nomepartial}}
var tmpl = '<ul>{{#citta}}<li>{{> item}}</li>{{/citta}}</ul>';
//definisco il template per il partial
var itemTmpl = '<strong>{{nome}}</strong> ({{sigla}})';
Handlebars.registerPartial('item', itemTmpl);
var template = Handlebars.compile(tmpl);
var data = {
citta: [
{nome: 'Milano', sigla: 'MI'},
{nome: 'Roma', sigla: 'RM'}
]
};
var html = template(data);
jQuery Template
Divenuto plugin ufficiale jQuery dalla versione 1.4.3, jquery-tmpl è un template engine molto robusto e versatile. Nonostante il suo sviluppo sia ancora in fase beta, la stretta integrazione con la libreria lo rende un'ottima scelta per tutti i progetti basati su jQuery.
Diversamente da Mustache i placeholder vengono identificati da ${nomeplaceholder}
, mentre le doppie graffe sono utilizzate per identificare i marcatori logici nel template, come condizioni o cicli:
<li>Come si nota dall'esempio precedente, jquery-tmpl permette di inserire in placeholder e marcatori logici anche delle espressioni, che possono rappresentare funzioni, proprietà dell'oggetto, nonché variabili specifiche offerte dalla libreria. Ecco come potrebbe essere realizzata la lista di città vista in precedenza:
{{if sigla}}
${nome} (${sigla}).
{{else}}
${nome}.
{{/if}}
</li>
{{if citta.length}}
<ul>
{{each citta}}
<li> ${nome} (${sigla}). </li>
{{/each}}
<ul>
{{else}}
<p>Nessuna città trovata.</p>
{{/if}}
Già da questi esempi si capisce l'enorme versatilità del plugin che, unita alla possibilità di compilare i template, garantisce anche performance molto elevate. Per approfondire le caratteristiche di jquery-tmpl potete fare riferimento all'esauriente documentazione online.
Template: dove e come
Finora abbiamo presentato i vantaggi e le potenzialità dei template engine, tuttavia ci sono alcune problematiche comuni a tutte le soluzioni che vanno prese in considerazione al momento di implementare l'una o l'altra libreria.
Definire il template inline
Nei primi esempi abbiamo definito il template sotto forma di stringa. Nonostante questo sia il metodo più performante per scrivere template JavaScript, è anche chiaro come possa risultare poco leggibile e flessibile con markup complessi.
La soluzione adottata da Handlebars e jquery-tmpl è quella di utilizzare un tag script
con un attributo type
diverso da text/javascript
(in modo da evitare che il codice venga interpretato ed eseguito) ed una funzione che estragga il contenuto dello script per passarlo al template engine:
<script id="mioTemplate" type="text/handlebars-tmpl">
// codice del template
</script>
<script type="text/javascript">
function getTemplate (scriptId) {
var el = document.getElementById(scriptId);
return el.innerHTML;
}
var template = Handlebars.compile( getTemplate('mioTemplate') );
</script>
L'unico problema con questo approccio è che la pagina sarà valida in HTML5 ma non in XHTML, in quanto quest'ultimo non prevede un attributo id
per i tag script
.
Nel caso doveste utilizzare XHTML, un'altra soluzione potrebbe essere quella di utilizzare un tag textarea
che preserva l'HTML inserito al suo interno. Per evitare che sia visibile o che il template venga inviato accidentalmente insieme ad altri dati bastano alcuni piccoli accorgimenti:
<textarea id="mioTemplate" disabled="disabled" style="display: none">
// codice del template
</textarea>
Caricamento dinamico dei template
Un altro problema riguarda quando richiamare il template durante la vita di un'applicazione. Utilizzando i metodi precedenti, i template vanno necessariamente caricati insieme alla pagina causando un maggior peso ed una commistione di logica e presentazione.
In uno scenario meglio strutturato, il codice del template non dovrebbe mai risultare visibile nel codice HTML e magari dovrebbe essere caricato dinamicamente via JavaScript.
Si potrebbe pensare di precaricare il file di template con un tag script del tipo:
<script id="mioTemplate" type="text/handlebars-tmpl" src="/templates/mioTemplate.js">
Tuttavia alcuni browser non caricano risorse esterne in tag script con attributo type
non conferme. Per ovviare al problema vi sono alcune interessanti tecniche che permettono il precaricamento delle risorse esterne JavaScript garantendo una buona compatibilità con tutti i browser.
L'alternativa più semplice è comunque quella di salvare il template in un file separato e caricarlo via AJAX (è la soluzione usata da EJS). L'unico svantaggio di questa tecnica sta nel fatto che la visualizzazione dei dati sarà ritardata fino al caricamento del file esterno.
Approfondimenti
Il tema dei template engine lato client è molto ampio e le soluzioni disponibili online sono numerose. Ecco alcuni link utili con approfondimenti e test: