Nell'articolo precedente abbiamo visto come sia possibile creare nuovi elementi della pagina con ExtJS utilizzando la classe statica Ext.DomHelper
. Nonostante la classe si adatti perfettamente a frammenti di HTML statici, essa presenta molte difficoltà nel caso in cui l'HTML da includere in pagina sia dipendente da eventuali parametri inseriti dall'utente o ricevuti dal server. Questa necessità non è per nulla trascurabile: sono frequentissimi infatti gli aggiornamenti della pagina sulla base di contenuti flessibili.
Per realizzare frammenti di HTML che rispondano appunto a questa necessità, ExtJS mette a disposizione due classi: Ext.Template
ed Ext.XTemplate
. Entrambe mettono a disposizione la possibilità di creare template, ovvero frammenti "astratti" di HTML che vengono successivamente "concretizzati" partendo da una struttura di dati. Questa metodologia di sviluppo si ispira al concetto Model-View-Controller con il quale ogni componente dell'applicazione deve essere autonomo rispetto agli altri. I template infatti rappresenteranno il componente View (ovvero la rappresentazione grafica) indipendente dal Model (struttura dei dati) e dal Controller (logica di business) che verranno implementati da altre classi.
Template di base con Ext.Template
La classe Ext.Template
premette di definire e di popolare template partendo da una struttura di dati sottoforma di oggetti Javascript o di vettori. La definizione di un template avviene a partire da una stringa all'interno della quale saranno presenti placeholder che verranno poi sostituiti (automaticamente dal motore di ExtJS) con i dati veri e propri.
Il primo template
Ecco il primo esempio:
<html>
<head>
<script src="../ext-base.js" type="text/javascript"></script>
<script src="../ext-all-debug.js" type="text/javascript"></script>
<link rel="stylesheet" href="../css/ext-all.css"></link>
<script type="text/javascript">
Ext.onReady(function() {
var template = new Ext.Template(
"<div class='person'>",
"<b>{name} {surname}</b><br/>",
"Residente in {address}<br/>",
"{city} {cap}",
"</div>"
);
var data = {
name: "Alberto",
surname: "Bottarini",
address: "Via Roma 10",
city: "Milano",
cap: "20100"
}
template.append("div01", data);
});
</script>
</head>
<body>
<div id='div01'></div>
</body>
</html>
La creazione di un oggetto Ext.Template
avviene passando al costruttore un elenco di stringhe per sfruttare la possibilità di indentazione e di ritorno a capo (passando un unica stringa questo non era possibile). Grazie al metodo append
inseriamo i dati contenuti all'interno dell'oggetto data
nel template e appendiamo il risultato all'interno del div01
. L'ultima cosa da notare rimangono i placeholder. Utilizzando la sintassi {nomeproprietà}
possiamo appunto far riferimento alla proprietà dell'oggetto utilizzato come modello dei dati.
I formats
Spesso è necessario formattare i dati da mostrare all'utente in quanto essi non sono rappresentabili direttamente in un formato comprensibile. Pensiamo per esempio ad un oggetto Date il quale presenta diverse possibilità di utilizzo e di visualizzazione. Per questa e per altre funzionalità ci vengono in soccorso i cosidetti formats ovvero delle piccole funzioni di utilità implementate direttamente all'interno dei nostri template. L'elenco di queste funzioni di utilità sono rappresentate dai metodi della classe Ext.util.Format
. Questo significa inoltre che è possibile aggiungere proprie funzioni di formattazione semplicemente aggiungendo nuovi metodi alla classe. Ecco un breve esempio dei formats:
<html>Durante la creazione di un template è possibile, oltre alle stringhe rappresentanti i frammenti di HTML, passare come parametro un oggetto di configurazione. Tra le configurazioni possibili è presente una proprietà
<head>
<script src="../ext-base.js" type="text/javascript"></script>
<script src="../ext-all-debug.js" type="text/javascript"></script>
<link rel="stylesheet" href="../css/ext-all.css"></link>
<script type="text/javascript">
Ext.onReady(function() {
var template = new Ext.Template(
"<div class='person'>",
"<b>{name:capitalize} {surname:uppercase}</b><br/>",
"Residente in {address:ellipsis(10)}<br/>",
"{city} {cap}<br/>",
"Nato il {birthdate:date('d/m/Y')}",
"</div>"
);
var data = {
name: "alberto",
surname: "bottarini",
address: "via roma 10",
city: "Milano",
cap: "20100",
birthdate: new Date()
}
template.append("div01", data);
});
</script>
</head>
<body>
<div id='div01'></div>
</body>
</html>
disableFormats
(di default settata a false
) che permette di disattivare l'utilizzo dei formats quando essi non sono utilizzati nel template per migliorare le performance di sostituzione delle variabili:
var template = new Ext.Template(
"<div class='person'>",
"<b>{name:capitalize} {surname:uppercase}</b><br/>",
"Residente in {address:ellipsis(10)}<br/>",
"{city} {cap}<br/>",
"Nato il {birthdate:date('d/m/Y')}",
"</div>", {
disableFormats: true
});
La compilazione
Nelle ultime versioni del framework è stata inserita la possibilità di compilare i template. La compilazione permette di sostituire la funzione di elaborazione classica del template con una più performante e rapida. Compilare un template è necessario nel momento in cui lo stesso verrò riutilizzato più volte nella stessa pagina.
Estendiamo Ext.Template: Ext.XTemplate
Nonostante formats e compilazione siano due funzionalità avanzate all'interno del concetto di template, spesso non sono sufficienti a gestire tutte le situazioni possibili, soprattutto quando il modello di dati da mostrare all'utente non è banale come quelli visti negli esempi in precedenza. Immaginiamo per esempio un oggetto di partenza composto non solo da primitivi (stringhe e numeri) ma anche da variabili composte come altri oggetti o vettori sui quali magari è necessario effettuare controlli condizionali o cicli perché non è noto a priori il conteggio.
In situazioni più complesse il framework ci mette a disposizione una tipologia di template più complessa, appunto Ext.XTemplate
. Se da un lato questa classe offre maggiori potenzialità, dall'altra è più dispendiosa in termini di risorse. È compito del programmatore utilizzare lo strumento che meglio si adatta alle sue esigenze: non ha senso utilizzare un Ext.XTemplate
per un template come quelli visti nei primi esempi. Utilizzando Ext.XTemplate
è consigliato usare l'apice singolo (') come divisore tra le varie righe di codice al posto del doppio apice (").
Gestione di array
All'interno della definizione di un template è possibile creare strutture dinamiche partendo da un vettore sia di elementi singoli che di elementi complessi. Il costrutto per creare cicli è <tpl for="nome_vettore">
. All'interno di questo elemento possiamo far riferimento all'elemento corrente con un punto (.) e all'indice con il simbolo cancelletto (#). Ecco un esempio chiarificatore:
<html>
<head>
<script src='../ext-base.js' type='text/javascript'></script>
<script src='../ext-all-debug.js' type='text/javascript'></script>
<link rel='stylesheet' href='../css/ext-all.css'></link>
<script type='text/javascript'>
Ext.onReady(function() {
var template = new Ext.XTemplate(
'<tpl for=".">',
'<div class="person">',
'<b>{name} {surname}</b><br/>',
'Skills:<br/>',
'<tpl for="skills">',
'{#} - {.}<br/>',
'</tpl>',
'</div>',
'</tpl>'
);
var data = [{
name: 'Alberto',
surname: 'Bottarini',
skills: [ 'JavaScript', 'ExtJS', 'jQuery']
}, {
name: 'Marco',
surname: 'Marchi',
skills: [ 'Java', 'Spring', 'Hibernate']
}, {
name: 'Luca',
surname: 'Luchi',
skills: [ '.NET', 'ASP']
}]
template.append('div01', data);
});
</script>
</head>
<body>
<div id='div01'></div>
</body>
</html>
Termina qui la prima parte dell'articolo. Nella seconda, online tra sette giorni, affronteremo i seguenti argomenti: condizioni, esecuzione di funzioni, codice inline.
Attivazioni condizionali
Utilizzando il costrutto <tpl if="condizione">
è possibile introdurre condizioni all'interno dei nostri template. Non esiste nessun operatore di tipo else
: è necessario utilizzare una seconda condizione opposta alla prima. Inoltre eventuali stringhe all'interno della condizione devono essere encodate utilizzando ". Ecco l'esempio:
<html>
<head>
<script src='../ext-base.js' type='text/javascript'></script>
<script src='../ext-all-debug.js' type='text/javascript'></script>
<link rel='stylesheet' href='../css/ext-all.css'></link>
<script type='text/javascript'>
Ext.onReady(function() {
var template = new Ext.XTemplate(
'<tpl for=".">',
'<tpl if="name== "Alberto"">',
'<div class="person" style="color:red">',
'</tpl>',
'<tpl if="name!= "Alberto"">',
'<div class="person">',
'</tpl>',
'<b>{name} {surname}</b><br/>',
'Skills:<br/>',
'<tpl for="skills">',
'{#} - {.}<br/>',
'</tpl>',
'</div>',
'</tpl>'
);
var data = [{
name: 'Alberto',
surname: 'Bottarini',
skills: [ 'JavaScript', 'ExtJS', 'jQuery']
}, {
name: 'Marco',
surname: 'Marchi',
skills: [ 'Java', 'Spring', 'Hibernate']
}, {
name: 'Luca',
surname: 'Luchi',
skills: [ '.NET', 'ASP']
}]
template.append('div01', data);
});
</script>
</head>
<body>
<div id='div01'></div>
</body>
</html>
Esecuzione di funzioni
All'interno dei nostri template è possibile definire funzioni che verranno richiamate durante la fase di append
. Le funzioni user-defined dovranno essere passate come se fossero configurazioni iniziali all'interno dell'oggetto JavaScript. Riscriviamo l'esempio di prima:
<html>
<head>
<script src='../ext-base.js' type='text/javascript'></script>
<script src='../ext-all-debug.js' type='text/javascript'></script>
<link rel='stylesheet' href='../css/ext-all.css'></link>
<script type='text/javascript'>
Ext.onReady(function() {
var template = new Ext.XTemplate(
'<tpl for=".">',
'<tpl if="this.isMe(name)">',
'<div class="person" style="color:red">',
'</tpl>',
'<tpl if="this.isNotMe(name)">',
'<div class="person">',
'</tpl>',
'<b>{name} {surname}</b><br/>',
'Skills:<br/>',
'<tpl for="skills">',
'{#} - {.}<br/>',
'</tpl>',
'</div>',
'</tpl>', {
isMe: function(name) {
return name == "Alberto";
},
isNotMe: function(name) {
return name != "Alberto";
}
});
var data = [{
name: 'Alberto',
surname: 'Bottarini',
skills: [ 'JavaScript', 'ExtJS', 'jQuery']
}, {
name: 'Marco',
surname: 'Marchi',
skills: [ 'Java', 'Spring', 'Hibernate']
}, {
name: 'Luca',
surname: 'Luchi',
skills: [ '.NET', 'ASP']
}]
template.append('div01', data);
});
</script>
</head>
<body>
<div id='div01'></div>
</body>
</html>
Esecuzione di codice inline
Una delle funzionalità che offre più flessibilità è senza ombra di dubbio la possibilità di eseguire parti di codice personalizzato all'interno del codice del template senza bisogno di definire funzioni o creare variabili particolari. Qualsiasi frammento di codice contenuto tra {[ e ]}
verrà eseguito normalmente come fosse codice Javascript e il risultato verrà inserito all'interno del template. Eventuali parametri sono disponibili all'interno dell'oggetto (automaticamente creato) values
. Oltre a quest'ultimo oggetto Ext.XTemplate
fornisce ulteriori informazioni: la documentazione ufficiale li elenca in maniera chiara su questa pagina. Ecco l'esempio:
<html>
<head>
<script src='../ext-base.js' type='text/javascript'></script>
<script src='../ext-all-debug.js' type='text/javascript'></script>
<link rel='stylesheet' href='../css/ext-all.css'></link>
<script type='text/javascript'>
Ext.onReady(function() {
var template = new Ext.XTemplate(
'<tpl for=".">',
'<div class="person"',
'{[ values.name == "Alberto" ? "style='color:red'" : "" ]}',
'>',
'<b>{name} {surname}</b><br/>',
'Skills:<br/>',
'<tpl for="skills">',
'{#} - {.}<br/>',
'</tpl>',
'</div>',
'</tpl>'
);
var data = [{
name: 'Alberto',
surname: 'Bottarini',
skills: [ 'JavaScript', 'ExtJS', 'jQuery']
}, {
name: 'Marco',
surname: 'Marchi',
skills: [ 'Java', 'Spring', 'Hibernate']
}, {
name: 'Luca',
surname: 'Luchi',
skills: [ '.NET', 'ASP']
}]
template.append('div01', data);
});
</script>
</head>
<body>
<div id='div01'></div>
</body>
</html>
Utilizzare i template per modificare componenti base del framework
Utilizzando ExtJS saranno molto rari i casi in cui sarà necessario creare un template da zero ed appenderlo a qualche elemento HTML già presente nella pagina. Spesso il loro utilizzo (sia nella forma di Ext.Template che in quella di Ext.XTemplate) sarà quello di personalizzare l'aspetto grafico dei componenti già presenti nella libreria. Molti di questi componenti infatti espongono la proprietà tpl che permetterà proprio di adattare il componente sulla base di un template definito da noi sviluppatori. Quest'aspetto è senza dubbio una delle caratteristiche vincenti di ExtJS che rendono il framework altamente e facilmente configurabile.
I principali componenti che possono essere personalizzati attraverso la proprietà tpl
sono:
- Ext.DataView
- Ext.ListView
- Ext.form.ComboBox
- Ext.grid.TemplateColumn
- Ext.grid.GroupingView
- Ext.menu.Item
- Ext.layout.MenuLayout
- Ext.ColorPalette
Nei prossimi articoli esamineremo nel dettaglio alcuni di essi e proveremo ad adattarli alla nostra applicazione proprio tramite template.
Conclusioni
In questo articolo abbiamo analizzato uno degli aspetti più importanti della libreria soprattutto perché rappresenta uno dei mattoni sui quali tutta l'infrastruttura si basa. Gli stessi componenti definiti dagli sviluppatori di ExtJS sono stati creati utilizzando le classi Ext.Template
e Ext.XTemplate
proprio per garantire una facilità di manutenibilità e personalizzazione.
Comprendere quindi questo concetto permette non solo di utilizzare al meglio la libreria, ma anche di recepire i meccanismi interni spesso troppo difficili da analizzare.