Gestione template HTML in JavaScript
Sempre più spesso i siti web caricano dati nella pagina in modo asincrono, ad esempio quando i dati sono paginati.
Questa gestione può essere con dei framework quali JQuery, Angular, React, ecc..
Se però l'esigenza è solo quella di renderizzare dell'html, si può utilizzare un altro approccio usando solo JavaScript senza scaricare pesanti librerie.
Per prima cosa va inserito in pagina un placeholder dove verrà inserito l'html caricato in modo asincrono:
Un possibile approccio gestire i dati è questo, dove l'html viene generando concatenando delle stringhe che rappresentano i template l'html risultante:
pur funzionando perfettamente presenta alcune limitazioni:
Per sopperire a queste limitazioni si può implementare un semplice gestore di template html.
In questo caso il template html viene inserito nella pagina all'interno di alcuni tag script, con l'accortezza di impostare l'attributo type a text/template:
A questo punto serve solo una piccola libreria JavaScript con le funzioni per gestire i template. Le funzioni sono:
a questo punto l'esempio iniziale diventa:
Come si vede il codice JavaScript risulta più leggibile, ma soprattutto è più semplice capire qual'é l'html risultante in quando è visibile in pagina all'interno dei tag script.
Anche la manutenzione successiva oppure l'uso di template più comlessi è resa più agevole da questo approccio.
Per l'esempio ho utilizzato questo file json ./sgart-template-examples.json:
il risultato è questo:
Questa gestione può essere con dei framework quali JQuery, Angular, React, ecc..
Se però l'esigenza è solo quella di renderizzare dell'html, si può utilizzare un altro approccio usando solo JavaScript senza scaricare pesanti librerie.
Per prima cosa va inserito in pagina un placeholder dove verrà inserito l'html caricato in modo asincrono:
HTML
<div id="wrapper-esempio-1"></div>
Un possibile approccio gestire i dati è questo, dove l'html viene generando concatenando delle stringhe che rappresentano i template l'html risultante:
JavaScript
function example1() {
// id dell'elemento html in cui verrà inserito l'html risultante
var elementId = 'wrapper-esempio-1';
var element = document.getElementById(elementId);
element.innerHTML = 'Attendi ...';
sgart.jsonGet('./sgart-template-examples.json', function (response) {
// renderizzo i template con i dati
var s = '';
// ciclo su ogni elemento del json ritornato e renderizzo gli item
response.forEach(function (value, index) {
var flagOdd = index % 2 === 1; // gestisco una classe diversa per le righe pari e dispari
// aggiungo gli items
s += '<li class="item item-1 ' + (flagOdd ? 'odd' : 'even') + '">'
+ '<a href="' + value.url + '">'
+ ' | ' + index + ' - <strong>' + value.title.htmlEncode() + '</strong> |'
+ '</a>'
+ '</li>'
});
// unisco i due template
var sComplete = '<div class="container-main">'
+ '<h4>Esempio solo JavaScript</h4>'
+ '<ul>' + s + '</ul>'
+ '</div>';
// scrivo l'html risultate nella pagina
element.innerHTML = sComplete;
});
}
- l'html risultante non è molto leggibile
- con template html più complessi la leggibilità peggiora
- va gestito l'escape delle virgolette singole o doppie quando il testo viene inserito negli attributi
- va gestito l'escape dei caratteri maggiore o minore quando questi fanno parte del testo da visualizzare
Per sopperire a queste limitazioni si può implementare un semplice gestore di template html.
In questo caso il template html viene inserito nella pagina all'interno di alcuni tag script, con l'accortezza di impostare l'attributo type a text/template:
HTML
<div id="wrapper-esempio-2"></div>
<script id="template-main" type="text/template">
<div class="container-main">
<h4>Esempio con template</h4>
<ul>
{{items}}
</ul>
</div>
</script>
<script id="template-item-1" type="text/template">
<li class="item item-1 {{class}}">
<a href="{{url}}">{{sep}}{{index}} - <strong>{{title}}</strong>{{sep}}</a>
</li>
</script>
per convenzione uso le doppie parentesi graffe per racchiudere le parti che fungeranno da placeholder (o tag) in cui verranno sostituiti i valori
A questo punto serve solo una piccola libreria JavaScript con le funzioni per gestire i template. Le funzioni sono:
- sgart.getTemplate: per leggere il template dalla pagina e convertirlo in stringa
- String.prototype.replaceTag...: per sostituire i tag nel template presenti nella foma {{nomeTag}}
- String.prototype.htmlEncode: per fare l'escape del testo html e delle virgolette doppie e singole
- String.prototype.attrEncode: per fare l'escape solo delle virgolette doppie e singole
- String.prototype.writeToHtml: come helper per scrivere l'html risultante
JavaScript
"use strict";
var sgart = sgart || {};
// legge il template dalla pagina html identifiato da uno specifico "id"
sgart.getTemplate = function (templateId) {
var element = document.getElementById(templateId);
if (element === null) {
throw new Error("Html template with id \"" + templateId + "\" not found.");
}
return element.innerHTML;
};
// sostituisce un TAG senza applicare nessun escape
String.prototype.replaceTag = function (tagName, value) {
var re = new RegExp("\{\{" + tagName + "\}\}", "gi");
return this.replace(re, value);
};
// sostituisce un TAG facendo l'escape delle virgolette singole e doppie
String.prototype.replaceTagAttr = function (tagName, value) {
var re = new RegExp("\{\{" + tagName + "\}\}", "gi");
return this.replace(re, value.attrEncode());
};
// sostituisce un TAG facendo l'encoding HTML + l'escape delle virgolette singole e doppie
String.prototype.replaceTagHtml = function (tagName, value) {
var re = new RegExp("\{\{" + tagName + "\}\}", "gi");
return this.replace(re, value.htmlEncode());
};
// encode dei caratteri in html
String.prototype.htmlEncode = function () {
var node = document.createTextNode(this);
return document.createElement("a").appendChild(node).parentNode.innerHTML.replace(/'/g, "'").replace(/"/g, """);
};
// encode delle virgolette singole e doppie
String.prototype.attrEncode = function () {
return this.replace(/'/g, "'").replace(/"/g, """);
};
// scrive la stringa nell'elemento html identificato da un "id"
String.prototype.writeToHtml = function (elementId) {
var element = document.getElementById(elementId);
if (element === null) {
throw new Error("Html element with id \"" + elementId + "\" not found.");
}
element.innerHTML = this;
};
JavaScript
function example2() {
// id dell'elemento html in cui verrà inserito l'html risultante
var elementId = "wrapper-esempio-2";
"Attendi ...".writeToHtml(elementId);
sgart.jsonGet("./sgart-template-examples.json", function (response) {
// recupero i template
var sTemplateMain = sgart.getTemplate("template-main");
var sTemplateItem = sgart.getTemplate("template-item-1");
// renderizzo i template con i dati
var s = '';
response.forEach(function (value, index) {
var flagOdd = index % 2 === 1;
// aggiungo gli items
s += sTemplateItem
.replaceTag("class", flagOdd ? "odd" : "even")
.replaceTag("sep", " | ")
.replaceTag("index", index + 1)
.replaceTagHtml("title", value.title)
.replaceTagAttr("url", value.url);
});
// unisco i due template
var sComplete = sTemplateMain.replaceTag("items", s);
// scrivo l'html risultate nella pagina
sComplete.writeToHtml(elementId);
});
}
Anche la manutenzione successiva oppure l'uso di template più comlessi è resa più agevole da questo approccio.
Per l'esempio ho utilizzato questo file json ./sgart-template-examples.json:
JavaScript
[
{
"title": "Animazione di elementi HTML con CSS3",
"url": "https://www.sgart.it/IT/informatica/animazione-di-elementi-html-con-css3/post"
},
{
"title": "SharePoint: Errors were found when compiling the workflow.",
"url": "https://www.sgart.it/IT/informatica/sharepoint-errors-were-found-when-compiling-the-workflow/post"
},
{
"title": "Generare un certificato self signed da PowerShell",
"url": "https://www.sgart.it/IT/informatica/convertire-i-numeri-da-cifre-a-lettere-new/post"
},
{
"title": "Prova con tag <a href=\"\">html</a>",
"url": "https://www.sgart.it/IT/informatica/generare-un-certificato-self-signed-da-powershell/post"
}
]