Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
js
Ext.js (http://extjs.com/) è un framework Javascript utile a realizzare GUI complesse per web
application, ma non solo. Con Ext.js (da qui in avanti solo Ext) possiamo creare pagine con layout
avanzati anche se non possediamo particolari competenze CSS; possiamo agganciare funzionalità
AJAX di aggiornamento per le nostre interfacce; creare moduli wizard o textarea con testo enriched
(formattato come lo formatterebbe un programma di videoscrittura); agganciare le nostre web
application a ulteriori strumenti (MooTools, JQuery, Prototype, Google Gears e molti altri) in modo
da avere rapidamente software decisamente vicini alle applicazioni desktop con cui lavoriamo
quotidianamente.
L'elenco delle possibilità che ci apre la conoscenza di Ext potrebbe estendersi ben oltre le righe di
questo articolo, pertanto lo scopo di queste pagine sarà ristretto a fornire una panoramica di tutte le
potenzialità che questo tool ci offre. Cercherò di trasmettere l'entusiasmo che si prova nel
completare in pochi semplici passi le prime interfacce applicative, andando ben oltre i consueti
'Hello World' d'esordio di un manuale, rendendo così i lettori capaci di leggere e interagire con le
GUI Ext.
Creeremo insieme, in pochi minuti, una home completa per un prodotto o un sito, utilizzando Ext.
Sfrutteremo poi lo stesso esempio per andare avanti nei prossimi articoli, in modo da arrivare a
realizzare la nostra riserva applicativa personale. (ecco l'esempio).
Passiamo ai fatti
Ormai siamo curiosi di andare avanti e vedere quanto di ciò che abbiamo detto fino ad ora
corrisponde al vero. Pertanto andremo sul sito Ext e visiteremo la sezione Download. Qui potremo
decidere se scaricare subito Ext.js completo di sorgenti, esempi e documentazione (scelta che
raccomando ai newbie), o se passare alla costruzione del nostro personale pacchetto Ext. In ogni
caso consiglio di visionare quanto meno la possibilità di costruirsi il proprio pacchetto, in quanto è
interessante vedere e lasciarsi ispirare dal wizard di download costruito tramite Ext stesso. Oltre alle
due versioni di download troviamo un terzo utile link: la documentazione Ext basata su AIR. Come
anticipato prima, avremo modo di vedere più in profondità applicazioni complete basate su Ext, ma
già questa documentazione da poter visionare offline fornirà ai lettori più curiosi e impazienti, la
possibilità di vedere come risultano gradevoli le applicazioni Ext/AIR.
Ora abbiamo Ext sul nostro pc e, attenendoci ai limiti e ai propositi di questo articolo, vogliamo
finalmente vedere quanto sia realmente semplice e quali sono le possibilità di questo tool senza, per
il momento, considerare architetture complete di server side. Andiamo quindi a visionare la
directory examples. Qui troviamo esempi che soddifano le nostre esigenze, in quanto partiamo da
semplici pannelli a web os senza troppa difficoltà. Ma per prendere possesso dello strumento che
abbiamo tra le mani dobbiamo cominciare a scrivere qualche riga che ci dia la soddisfazione
iniziale di un'interfaccia di media complessità e ci induca a continuare.
Creiamo una cartella allo stesso livello di dove abbiamo posizionato la cartella 'ext'. Inseriamo una
pagina di nome esempio.html e apriamo un qualsiasi editor di testo dove trascriveremo il codice
HTML che segue:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>EXAMPLE 1. SIMPLE EXT GUI...</title>
<link rel="stylesheet" type="text/css" href="../ext/resources/css/ext-all.css"
/>
<script type="text/javascript" src="../ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../ext/ext-all.js"></script>
</head>
<body>
<div style="width:100%;height:100%;">
<div style="width:50%;background:#9e9efe;">
<h1>BANNER DEL MIO PRODOTTO</h1></div>
Se dovessimo pensare di costruire una home page senza scrivere una riga di CSS, che sia gradevole,
che funzioni su ogni browser, che presenti quattro tab e una barra di navigazione o di news che
goda di funzionalità di close and expand, potremmo pensare di impiegare almeno mezza giornata di
sviluppo e debugging. Eppure nelle poche righe che abbiamo appena scritto abbiamo già messo
tutto quello che serve. Gran parte del lavoro è infatti svolto da Ext e dalla sua capacità di gestire in
maniera pulita il codice Javascript non intrusivo. Pertanto, aver incluso la terna minima per
sviluppare con Ext (ext-all.css, ext-base.js, ext-all.js) ci aiuta ad impostare la nostra pagina perché
possa usare a pieno le potenzialità del framework.
Nelle ultime righe del nostro codice HTML abbiamo istruito il browser perché vada a leggere il
codice Javascript che si trova nel file js/esempio.js. Creiamo quindi una cartella 'js' dentro alla
cartella in cui abbiamo inserito il file esempio.html, e inseriamo al suo interno un file esempio.js.
Il codice che scriveremo nella nostra pagina è reperibile qui. Le righe che, per questa volta,
analizziamo a fondo, sono solo queste:
var tabs = new Ext.TabPanel({
region: 'center',
margins:'3 3 3 0',
activeTab: 0,
defaults:{autoScroll:true},
bodyStyle:"background:#efefff",
items:[
{
title: 'Primo tab',
bodyStyle:"background:#efefff",
layout:'fit',
html: '<div>contenuto primo pannello</div>'
},{
title: 'Secondo tab',
bodyStyle:"background:#efefff",
layout:'fit',
html: '<div>contenuto secondo pannello</div>'
},{
title: 'Terzo tab',
bodyStyle:"background:#efefff",
layout:'fit',
html: '<div>contenuto terzo pannello</div>'
},{
title: 'Quarto tab',
bodyStyle:"background:#efefff",
layout:'fit',
html: '<div>contenuto quarto pannello</div>'
}]
});
Come vedete questo è normalissimo codice JSON che Ext interpreta e rende in maniera piuttosto
intuitiva. Andiamo a leggere quanto abbiamo descritto nell'oggetto tabs. La variabile tabs conterrà
un pannello adatto a mostrare ogni suo elemento come una tab/scheda (Ext.TabPanel).
Questo contenitore di tab sarà posizionato nella regione centrale di un pannello che avrà un layout
di tipo BorderLayout (analizzeremo nei prossimi articoli i layout). Abbiamo descritto i margini che
dovrà mantenere tabs, nella pagina, e istruito il browser perché renderizzi la prima tab come attiva
(activeTab:0).
Abbiamo descritto alcune caratteristiche grafiche delle nostre tab e poi abbiamo inserito
direttamente nell'array items, le quattro schede e i relativi codici HTML, con layout 'fit', in modo
che il contenuto della tab riempia tutta l'area a disposizione. Il risultato è il seguente.
Figura 1 - Screenshot dell'esempio
Concludendo
Abbiamo rispettato il nostro obiettivo e realizzato fin da subito qualcosa che va oltre i classici Hello
World degli inizi. Nei prossimi articoli impareremo a applicare diversi temi e colori alle nostre
applicazioni, a leggere le API e usare la localizzazione, a sfruttare al meglio la community, i plugin
e gli extra e a progettare le nostre GUI come fino ad ora il web non ci aveva mai concesso di fare.
Aggiungiamo un Xtheme-selector
Rinominiamo il nostro file esempio.html dell'articolo precedente in theme.html e il suo Javascript in
theme.js. Per rendere la nostra pagina capace di cambiare aspetto, dovremo inserire una finestra o
una semplice combo box che ci aiutino a scatenare l'evento di scelta del tema da parte dell'utente.
Non avendo visto gli elementi form di Ext o le Window (che analizzeremo nei prossimi articoli),
procederemo in maniera ibrida e immediata creando del codice HTML che faremo interpretare da
Ext in modalità non-obtrusive (il Javascript non-obtrusive è, in poche parole, codice di scripting
applicato una volta che il DOM della pagina è stato creato, in maniera da lasciare pulita la nostra
pagina HTML e scorporare lo strato della logica da quello della visualizzazione, secondo i più
moderni e funzionali pattern di sviluppo).
Pertanto procederemo con la creazione di una semplice zona (nell'esempio è il div con
id="exttheme") che conterrà successivamente una combo box all'interno del nostro file HTML
di esempio:
<div style="width:100%;height:100%;" >
…
<div id="content"></div>
</div>
<div id="exttheme" style="float:left"></div>
comboTheme.on('select',
function(combo){
Ext.util.CSS.swapStyleSheet('theme', combo.getValue());
}
, this);
Lanciando in un qualsiasi browser il file theme.html vedremo la nostra GUI a schede, con una
combobox che ci permette di cambiare a runtime tra lo stile grigio chiaro e quello blue di default
come in questa pagina.
Conclusioni e anticipazioni
Con questo articolo, abbiamo voluto fornire una panoramica su temi e layout e due esempi completi
e funzionanti che possono essere riutilizzati per i propri siti. In questo abbiamo preso ulteriormente
confidenza con la struttura JSON tipica delle strutture Ext, inoltre siamo pronti ad affrontare nei
prossimi articoli l'utilizzo più approfondito degli elementi che compongono le interfacce Ext.
Continuando la lettura genealogica del nostro oggetto, incontriamo quella che è la base per tutte le
interfacce grafiche Ext: l'oggetto Component.
Ogni Component possiede un attributo xtype, che è il suo Ext-specific type name, e i relativi
metodi per identifcarlo: getXType e isXType.
Di seguito una lista degli xtypes validi:
xtype Class
box Ext.BoxComponent
button Ext.Button
colorpalette Ext.ColorPalette
component Ext.Component
container Ext.Container
cycle Ext.CycleButton
dataview Ext.DataView
datepicker Ext.DatePicker
editor Ext.Editor
editorgrid Ext.grid.EditorGridPanel
grid Ext.grid.GridPanel
paging Ext.PagingToolbar
panel Ext.Panel
progress Ext.ProgressBar
propertygrid Ext.grid.PropertyGrid
slider Ext.Slider
splitbutton Ext.SplitButton
statusbar Ext.StatusBar
tabpanel Ext.TabPanel
treepanel Ext.tree.TreePanel
viewport Ext.Viewport
window Ext.Window
Toolbar components
toolbar Ext.Toolbar
tbbutton Ext.Toolbar.Button
tbfill Ext.Toolbar.Fill
tbitem Ext.Toolbar.Item
tbseparator Ext.Toolbar.Separator
tbspacer Ext.Toolbar.Spacer
tbsplit Ext.Toolbar.SplitButton
tbtext Ext.Toolbar.TextItem
Form components
form Ext.FormPanel
checkbox Ext.form.Checkbox
combo Ext.form.ComboBox
datefield Ext.form.DateField
field Ext.form.Field
fieldset Ext.form.FieldSet
hidden Ext.form.Hidden
htmleditor Ext.form.HtmlEditor
label Ext.form.Label
numberfield Ext.form.NumberField
radio Ext.form.Radio
textarea Ext.form.TextArea
textfield Ext.form.TextField
timefield Ext.form.TimeField
Trigger Ext.form.TriggerField
Risaliamo ancora la catena che ci porterà al nostro oggetto e incontriamo BoxComponent, una
specializzazione di Component. è la classe base per ogni tipo di Ext.Component che usa contenitori
che prendono il nome di box, ovvero forme che contengono HTML.
BoxComponent fornisce automatismi per la modellazione dei box come il dimensionamento o la
posizione. Tutti gli oggetti che contengono HTML devono essere figli (sottoclassi) di
BoxComponent in modo che le loro proprietà siano congrue e convivano con gli altri box nel
momento in cui andremo a nidificarli uno dentro l'altro per poter ottenere i layout più disparati, utili
ai nostri scopi.
Dal nonno, arriviamo finalmente al padre del nostro tanto atteso oggetto, ovvero: Container. Di
default, i Containers usano gli schemi di layout che abbiamo introdotto nei precedenti articoli e che
prendono il nome di ContainerLayout. In questo modo le componenti che vanno a nidificarsi l'una
nell'altra sfruttano i meccanismi di collocazione nella pagina e possiamo appenderli uno dopo l'altro
all'interno del Container, senza preoccuparci delle dimensioni o della convivenza cross-browser
degli elementi.
Arriviamo così, attraverso gli avi, alla componente che da oggi useremo ed estenderemo fino a farla
diventare il punto di partenza di ogni nostro disegno di interfaccia: il Panel.
Le scatole cinesi
Abbiamo un Panel, che contiene un Panel che ne contiene altri e così via. Gli attributi che possiamo
identificare sono pertanto un set che tende a ripetersi. In questo caso, ad esempio, vediamo:
id: identificatore univoco del layer, va a coincidere con l'id del div associato all'HTML generato da
Ext per renderizzare il nostro Ext.Panel – di default è un identificativo autogenerato dalla libreria -.
title: il testo del titolo che verrà visualizzato nell'intestazione del pannello (una stringa vuota è il
valore di default). Quando un titolo è specificato l'intestazione dell'elemento verrà automaticamente
creata e visualizzata a meno che non si dichiari esplicitamente l'attributo header:false.
layout: la disposizione da utilizzare in questo Container. Se non si specifica un
Ext.layout.ContainerLayout, verrà visualizzata un'impostazione predefinita. I valori validi sono:
absolute, accordion, anchor, border, card, column, fit, form and table.
width: la larghezza di questa componente in pixel (il default è auto).
height: l'altezza di questa componente in pixel (il default è auto).
layoutConfig: si tratta di un oggetto di configurazione contenente le proprietà specifiche per la
scelta del layout. Per maggiori dettagli per quanto riguarda le opzioni di configurazione validi per
ogni tipo di layout, consiglio di visualizzare le API di Ext in merito a:
Ext.layout.Absolute
Ext.layout.Accordion
Ext.layout.AnchorLayout
Ext.layout.BorderLayout
Ext.layout.CardLayout
Ext.layout.ColumnLayout
Ext.layout.FitLayout
Ext.layout.FormLayout
Ext.layout.TableLayout
frame: è impostato a true per renderizzare il pannello con uno stile personalizzato con bordi
arrotondati e integrato nella grafica generale del pannello; false per renderizzare un pannello
generico con il bordo quadrato di 1px (il valore di default è false).
items: un unico elemento o un insieme di componenti figlie che verranno aggiunte al Container
principale. Ogni elemento può essere qualsiasi tipo di oggetto che estende Ext.Component. Gli item
vengono specificati ognuno dentro a parentesi graffe e tutti racchiusi da due parentesi quadre, cosi':
[{…},{…},{…}].
Ogni elemento tra parentesi graffe, nel nostro esempio, è a sua volta un Ext.Panel. Si potrebbero
definire esternamente alla variabile journal i diversi Ext.Panel che conterranno i vari riquadri
della nostra pagina e in seguito inserirli nell'attributo items di journal, separandoli solo da virgole,
senza necessità delle parentesi graffe. In pratica items è un Array di oggetti contenuti
nell'Ext.Panel. A sua volta ogni oggetto figlio conterrà elementi in items oppure un attributo
HTML di seguito descritto.
html: un frammento di HTML, o un DomHelper di Ext (vedremo nei prossimi articoli come si
utilizzano queste utility per creare codice HTML direttamente dal Javascript).
Il risultato del nostro lavoro è visibile qui. Disponibile anche il pacchetto zip con il materiale
completo.
Attenzione a IE
Colgo l'occasione di questo articolo per presentare il primo di tanti errori curiosi che un
programmatore newbie non riuscirebbe a identificare molto semplicemente, data la natura poco
chiara del codice di errore che incontriamo nel visualizzare il nostro oggetto Ext in cui è presente il
seguente baco di programmazione.
Figura 2 - Errore
Conclusioni e anticipazioni
Abbiamo diviso il nostro articolo in due parti in modo che i lettori possano metabolizzare i concetti
introdotti e il primo esempio di oggetti nidificati uno nell'altro. Nel prossimo articolo esporremo un
esempio di oggetti Window che produrranno un effetto di bacheca di post-it.
Disegniamo la nostra pagina con un elemento input di tipo button, con id uguale a show-btn e
valore uguale Post It Generator. Il valore, come ben sappiamo, lo ritroveremo sullo schermo, nella
nostra pagina renderizzata dal browser. Ricordiamoci invece l'id. Lo rivedremo nel codice
Javascript che, come abbiamo detto prima, sarà in grado di rendere intelligente il nostro bottone a
fronte dell'evento click.
Nel file che chiameremo 'window.js' troviamo il seguente semplice codice:
Ext.onReady(function(){
var win;
var button = Ext.get('show-btn');
var bd = Ext.getBody();
Ext.util.CSS.swapStyleSheet('theme', '../ext/resources/css/xtheme-
postit.css');
var cnt=0;
var xPos=100;
var yPos=100;
button.on('click', function(){
// create the window on the first click and reuse on subsequent clicks
cnt++;
xPos+=50;
yPos+=50;
if (xPos>=300)
{
xPos=120;
yPos=100;
}
createPostIt(cnt,xPos,yPos);
});
var createPostIt=function(cnt,xPos,yPos){
var win = new Ext.Window({
title:'Postit #'+cnt,
id:'win'+cnt,
layout:'fit',
width:500,
height:300,
x:xPos,
y:yPos,
closeAction:'hide',
frame:true,
items: new Ext.Panel({
title: 'The best test in all site',
layout:'fit',
frame:true,
Html: '<p>Lorem Ipsum is simply dummy text of the printing and
typesetting industry. '+
'Lorem Ipsum has been the industry\'s standard dummy text ever since
the 1500s, '+
'when an unknown printer took a galley of type and scrambled it to
make a type '+
…
'(injected humour and the like).</p>',
}),
buttons: [{
text: 'Close',
handler: function(){
win.hide();
}
}]
});
win.show(this);
}
});
Cominciamo indicando al browser di partire a interpretare il nostro codice Javascript solo dopo che
la pagina è stata caricata (Ext.onReady). Dopo di che creiamo qualche variabile di shortcut per gli
elementi che utilizzeremo più spesso nel nostro codice. Si noti come abbiamo messo nella variabile
button l'oggetto input che abbiamo descritto nella nostra pagina HTML tramite l'id che avevamo
notato prima.
Associamo un'azione all'evento di click del pulsante rappresentato dalla variabile button. Qui
troviamo il codice con cui creiamo la nostra finestra, in modo che ad ogni click venga disegnata con
gli attributi rappresentati tra parentesi graffe, all'interno della dicitura new
Ext.Window({…});.
Ci siamo permessi di aggiungere una finezza, memorizzando le posizioni di x e y della finestra
precedente quando esiste, altrimenti le abbiamo inizializzate a 100, fuori dalla funzione handler
dell'evento. In questa maniera riusciamo a produrre un gradevole effetto cascade per ogni finestra
aperta in successione.
Infine aggiungiamo un pulsante alla finestra che permetterà di nascondere la finestra quando viene
premuto.
Il risultato del nostro lavoro è visibile qui. Disponibile anche il pacchetto zip con gli esempi.
Conclusioni e anticipazioni
Con questo esempio terminiamo l'articolo doppio su Panel e Window. Siamo riusciti a vedere come
impostare i due principali oggetti che compongono una web application e abbiamo continuato il
nostro cammino nelle tecniche di disegno di GUI crossbrowser utilizzando Ext.js. Negli articoli che
seguiranno vedremo cosa mettere dentro alle nostre finestre, partiremo analizzando Form e Griglie e
proseguiremo con TabPanel, Accordion e altre utilities che coinvolgeremo nei nostri disegni.
Validazioni e messaggistica
Analizzeremo insieme alcuni degli esempi rilasciati da Ext per la sezione Form e per i suoi controlli
complessi. In tutti questi form vedremo come Ext riesca a farci dimenticare i vecchi controlli
Javascript per validazione di date, numeri, presenza di un campo, lunghezza e quant’altro grazie ai
meccanismi di validazione associati ad ogni form.
Ogni elemento può provvedere a validarsi da sé per via dei controlli onBlur. Se questi sono
attivati, non appena il cursore abbandona (onBlur) il campo in questione si può vedere l’elemento
contrassegnato da un simbolo di errore nel caso in cui il dato inserito non sia corretto. Questa
proprietà viene attivata dal campo booleano (true/false) validateOnBlur.
Un’altra caratteristica fondamentale di ogni elemento è quella di poter comunque esplicitare un
proprio meccanismo di validazione attraverso l’attributo validator, ovvero una funzione
personalizzata di validazione che viene chiamata durante la validazione del campo o del form (il
default è null). Se disponibile, questa funzione sarà chiamata solo dopo che il validatori di base
ritornano true. Deve restituire boolean true se il valore è valido o una stringa di messaggio di errore
in caso contrario.
Immaginiamo ora di avere un oggetto Ext.form che si chiama dataForm. Per scatenare la
validazione del modulo è sufficiente scrivere la seguente riga di codice:
dataForm.getForm().isValid().
Se vogliamo che il form scateni l’evento di submit, basterà scrivere:
dataForm.getForm().getEl().dom.submit().
Come abbiamo detto negli articoli precedenti, ogni oggetto ha una catena di ereditarietà che ne può
spiegare i comportamenti. L’oggetto Ext.Form, in particolare, estende Ext.BasicForm. La
BasicForm è configurata utilizzando l’initialConfig dei FormPanel - che è l'oggetto di
configurazione passato al costruttore. Ciò significa che se si ha un FormPanel e si desidera
configurare il BasicForm, sarà necessario inserire delle opzioni di configurazione per il BasicForm
nelle proprietà di initialConfig.
FormPanel utilizza un Ext.layout.FormLayout implicito nella sua dichiarazione ed è necessario
perché i campi e le etichette funzionino correttamente all'interno del FormPanel.
Per impostazione predefinita, i moduli inviano tramite Ajax, utilizzando una Ext.form.Action. In
questa maniera si possono gestire i messaggi di ritorno dal server a fronte di un inserimento o di un
update e fare con il nostro panel ciò che i paradigmi Ajax ci hanno insegnato a fare negli ultimi
anni.
Esempi pratici
Su questa pagina possiamo recuperare alcuni form che ci aiutano a prendere subito confidenza con
questo oggetto. Analizzeremo ora i form presenti in questo esempio.
Figura 1 - Form 1 - Esempio semplice
Questo esempio ci aiuta a vedere quanto è semplice realizzare un form. Il codice che rappresenta il
form che vediamo in figura è:
var simple = new Ext.FormPanel({
labelWidth: 75,
url:'save-form.php',
frame:true,
title: 'Simple Form',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 230},
defaultType: 'textfield',
items: [{
fieldLabel: 'First Name',
name: 'first',
allowBlank:false
},{
fieldLabel: 'Last Name',
name: 'last'
},{
fieldLabel: 'Company',
name: 'company'
}, {
fieldLabel: 'Email',
name: 'email',
vtype:'email'
}, new Ext.form.TimeField({
fieldLabel: 'Time',
name: 'time',
minValue: '8:00am',
maxValue: '6:00pm'
})
],
buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});
simple.render(document.body);
Capiamo subito che il FormPanel si comporta esattamente come i panel che abbiamo visto negli
articoli precedenti. Nell’attributo items, infatti, vediamo succedersi i vari elementi del nostro form
e riusciamo a leggere semplicemente ciò che essi rappresenteranno. Alcune particolarità le vediamo
nell’oggetto Email che utilizza l’attributo vtype (virtual type) che serve a creare una validazione
per gli oggetti email (nella seconda parte di questo articolo vedremo nel dettaglio come si crea una
validazione virtuale). Inoltre vediamo il campo TimeField che descrive perfettamente un elenco
di orari dalle 8 del mattino alle 6 del pomeriggio. Fino a qualche anno fa, un campo del genere, in
autocomplete, non sarebbe stato di così rapida implementazione nemmeno per i più bravi esperti
Javascript.
Grazie all’attributo defaultType: 'textfield', gli elementi elencati in items che non
presentano caratteristiche di xtype (tipologia: date, time, number, etc) sono rappresentati
automaticamente da un textfield.
Figura 2 - Form 2 - Aggiungere fieldset
Raggruppare gli elementi di un form a seconda di ciò che rappresentano è una buona norma che ci
aiuta sia a mantenere puliti i nostri moduli oltre che a renderli più utilizzabili da parte dell’utente.
Un’altra buona norma è non far comparire ciò che è opzionale e renderlo disponibile solo se l’utente
sceglie di vederne il contenuto. Per questi motivi, l’utilizzo dei Fieldset di Ext ci aiuta a disegnare
form complessi che mantengono una pulizia e un’usabilità non indifferenti.
Il codice che rappresenta il form in figura 2 è il seguente:
var fsf = new Ext.FormPanel({
labelWidth: 75,
url:'save-form.php',
frame:true,
title: 'Simple Form with FieldSets',
bodyStyle:'padding:5px 5px 0',
width: 350,
items: [{
xtype:'fieldset',
checkboxToggle:true,
title: 'User Information',
autoHeight:true,
defaults: {width: 210},
defaultType: 'textfield',
collapsed: true,
items :[{
fieldLabel: 'First Name',
name: 'first',
allowBlank:false
},{
fieldLabel: 'Last Name',
name: 'last'
},{
fieldLabel: 'Company',
name: 'company'
}, {
fieldLabel: 'Email',
name: 'email',
vtype:'email'
}
]
},{
xtype:'fieldset',
title: 'Phone Number',
collapsible: true,
autoHeight:true,
defaults: {width: 210},
defaultType: 'textfield',
items :[{
fieldLabel: 'Home',
name: 'home',
value: '(888) 555-1212'
},{
fieldLabel: 'Business',
name: 'business'
},{
fieldLabel: 'Mobile',
name: 'mobile'
},{
fieldLabel: 'Fax',
name: 'fax'
}
]
}],
buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});
fsf.render(document.body);
Possiamo notare come all’interno di items vengano inseriti due fieldset che a loro volta
contengono un attributo items in cui si succedono gli oggetti del form. I fieldset possono essere
rappresentati da checkbox o da elementi che rendono espandibile il fieldset stesso e hanno
caratteristiche che possono far comparire già aperto o meno un gruppo di elementi.
Figura 3 - Form 3 - Un form complesso
Anche se i fieldset e i layout ci aiutano a disporre in maniera ordinate i nostri campi, ciò potrebbe
non bastare ai clienti più esigenti; ecco che quindi vediamo tramite questo esempio come si possano
disporre tramite un TableLayout gli elementi della nostra pagina. In questo esempio inoltre vediamo
il primo esempio di htmleditor. Il codice che rappresenta questo form in particolare è il seguente:
var top = new Ext.FormPanel({
labelAlign: 'top',
frame:true,
title: 'Multi Column, Nested Layouts and Anchoring',
bodyStyle:'padding:5px 5px 0',
width: 600,
items: [{
layout:'column',
items:[{
columnWidth:.5,
layout: 'form',
items: [{
xtype:'textfield',
fieldLabel: 'First Name',
name: 'first',
anchor:'95%'
}, {
xtype:'textfield',
fieldLabel: 'Company',
name: 'company',
anchor:'95%'
}]
},{
columnWidth:.5,
layout: 'form',
items: [{
xtype:'textfield',
fieldLabel: 'Last Name',
name: 'last',
anchor:'95%'
},{
xtype:'textfield',
fieldLabel: 'Email',
name: 'email',
vtype:'email',
anchor:'95%'
}]
}]
},{
xtype:'htmleditor',
id:'bio',
fieldLabel:'Biography',
height:200,
anchor:'98%'
}],
buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});
items: {
xtype:'tabpanel',
activeTab: 0,
defaults:{autoHeight:true, bodyStyle:'padding:10px'},
items:[{
title:'Personal Details',
layout:'form',
defaults: {width: 230},
defaultType: 'textfield',
items: [{
fieldLabel: 'First Name',
name: 'first',
allowBlank:false,
value: 'Jack'
},{
fieldLabel: 'Last Name',
name: 'last',
value: 'Slocum'
},{
fieldLabel: 'Company',
name: 'company',
value: 'Ext JS'
}, {
fieldLabel: 'Email',
name: 'email',
vtype:'email'
}]
},{
title:'Phone Numbers',
layout:'form',
defaults: {width: 230},
defaultType: 'textfield',
items: [{
fieldLabel: 'Home',
name: 'home',
value: '(888) 555-1212'
},{
fieldLabel: 'Business',
name: 'business'
},{
fieldLabel: 'Mobile',
name: 'mobile'
},{
fieldLabel: 'Fax',
name: 'fax'
}]
}]
},
buttons: [{
text: 'Save'
},{
text: 'Cancel'
}]
});
L’ultimo form dell’elenco è un mix di quanto abbiamo visto nei form precedenti. Il codice non ci è
permesso di scriverlo per limiti di spazio ma è possibile recuperare il codice Javascript di tutti e
cinque gli esempi su questa pagina.
In particolare troveremo caratteristiche speciali come:
l’attributo allowBlank:true/false, che determina se un campo è obbligatorio o meno.
allowDecimals, per i campi number. Possiamo descrivere se un numero accetta decimali e
quanti ne accetta.
allowNegative, per i campi number. Possiamo descrivere se un campo accetta numeri negativi.
invalidText: il testo che si può inserire come messaggio quando un campo risulta non valido.
Vedere tutte le caratteristiche in un solo articolo è impossibile. Ma in questo modo abbiamo
cominciato a muovere i nostri primi passi nell’interazione donataci dal form. Quando dobbiamo
utilizzare degli elementi e vogliamo verificarne le caratteristiche, possiamo andare a questo url
(http://extjs.com/deploy/dev/docs/) e troveremo piccoli esempi e descrizioni di ogni attributo che ci
possa sembrare utile ai nostri scopi.
Conclusioni e anticipazioni
Rimangono ancora da trattare alcuni argomenti perché si possa considerare chiusa questa
panoramica sui form. La seconda parte di questo articolo tratterà i seguenti argomenti: Advanced
Validation, checkbox/Radio Groups, multiSelect and ItemSelector, e combobox, Basic Combo
Box, ComboBox Templates.
In particolare notiamo come nell'attributo validator abbiamo definito una chiamata a una
funzione a partire dal valore che sta assumendo il nostro campo di testo al momento della
validazione messa in OR ( || ) con un messaggio di errore ('valore non valido'). In questo modo il
validator assume valore uguale al booleano true o al messaggio da riportare sul form per
segnalare l'invalidità del campo.
Un altro modo per testare la validità di un form è dato dall'istruzione form.isValid(). Per
utilizzarlo nei nostri applicativi potremmo scrivere una funzione Javascript con il codice che segue:
var msg, valid = true;
form.items.each(function(field){
msg = field.isValid(true);
if(msg !== true ){ //true to suppress invalidMarking
alert(field.fieldLabel + ' is invalid:' + msg );
valid = false;
}
});
return valid;
Esistono molti attributi che possiamo associare a un elemento Ext per poter gestire al meglio la
validazione. Alcuni campi speciali, come il campo DateField, o il TimeField, o il NumberField e
così via, hanno delle validazioni implicite dettate dal tipo di campo e dai valori che può accettare.
Queste funzionalità faranno sicuramente comodo a chi programma da diversi anni nel campo web: è
infatti molto piacevole potersi sbarazzare di centinaia di righe di funzioni che determinano se un
numero è valido o se una data è scritta correttamente o meno.
Vediamo ora, un rapido elenco di attributi, oltre a validator, che possiamo associare pressappoco a
tutti i campi di un form.
• allowBlank: se settato su false trasforma il campo in obbligatorio (il default è true);
• blankText: è il messaggio di testo da visualizzare se la validazione allowBlank
fallisce (il messaggio di default è "Questo campo è obbligatorio");
• invalidClass: la classe CSS da utilizzare quando si setta un campo non valido (di
default è "x-form-invalid") ;
• invalidText: è il messaggio di testo da visualizzare per settare l'errore nel caso in cui un
campo risulti non valido (il default è "Il valore in questo campo non è valido");
• maxLengthText: è il messaggio di testo da visualizzare se la lunghezza massima viene
superata (il default è "La lunghezza massima di questo campo è x");
• minLengthText: è il messaggio di testo da visualizzare se la lunghezza minima di
convalida non riesce (il default è "La lunghezza minima di questo campo è x");
• regex: un oggetto JavaScript RegExp con cui validare il contenuto di un campo (il default
è nullo). Se disponibile, questa regex sarà valutata solo dopo la che tutti i metodi validatori
ritornano vero. Se il test fallisce, il campo sarà segnato utilizzando regexText;
• regexText: è il messaggio di testo da visualizzare se fallisce il controllo regex (il default
è "");
• validateOnBlur: questo è un attributo molto utile e permette di effettuare delle
validazioni quando il campo perde il focus. Associando una funzione Ajax a questo campo
potremmo ad esempio lanciare un controllo lato server che verifica la presenza di un campo
in un database o cose simili (il default è true);
• validationDelay: il tempo in millisecondi che fa partire la validazione dopo l'input (il
default è 250);
• validationEvent: l'evento che dovrebbe avviare la validazione. Da impostare a false
per disattivare la validazione automatica (il default è "keyup").
Per terminare questo capitolo sulle validazioni semplici, riportiamo ancora due proprietà: format
e nanText. La prima è associata in particolare ai campi DateField e TimeField. In particolare
seguono la formattazione che trovate a questo link. Grazie a questa stringa si possono pilotare le
validazioni sul contenuto dei campi data ed ora. La proprietà nanText, invece, riguarda i campi
numerici ed è il testo del messaggio da visulizzare nel caso in cui il campo non risulti un numero
valido.
Validazione avanzata
Proviamo ora a fare un passo avanti e a vedere come si implementano dei meccanismi di
validazione più sofisticati. Ad esempio possiamo vedere su questa pagina due moduli in cui sono
stati implementati un controllo di validità di password e un controllo di validità su un periodo
temporale. Riportiamo di seguito il codice dei due moduli:
var dr = new Ext.FormPanel({
labelWidth: 125,
frame: true,
title: 'Date Range',
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {width: 175},
defaultType: 'datefield',
items: [{
fieldLabel: 'Start Date',
name: 'startdt',
id: 'startdt',
vtype: 'daterange',
endDateField: 'enddt' // id of the end date field
},{
fieldLabel: 'End Date',
name: 'enddt',
id: 'enddt',
vtype: 'daterange',
startDateField: 'startdt' // id of the start date field
}]
});
dr.render('dr');
pwd.render('pw');
Dal codice possiamo estrapolare le ultime due proprietà utili alla validazione:
• vtype: utile a richiamare un virtual type da associare al nostro campo;
• vtypeText: utile a mostrare il messaggio di errore a fronte della validazione sul virtual
type.
Implementare un virtual type è molto semplice. Nel codice che segue possiamo vedere come sono
stati implementati i controlli che settano la data minima ( end.setMinValue(date) ) e
massima ( start.setMaxValue(date) ) nei due calendari Ext; e come di seguito sia stato
implementato il vtype password che controlla che le due password digitate siano uguali (var
pwd = Ext.getCmp(field.initialPassField); return (val ==
pwd.getValue()); ).
Ext.apply(Ext.form.VTypes, {
daterange : function(val, field) {
var date = field.parseDate(val);
if(!date){
return;
}
if (field.startDateField && (!this.dateRangeMax || (date.getTime() !=
this.dateRangeMax.getTime()))) {
var start = Ext.getCmp(field.startDateField);
start.setMaxValue(date);
start.validate();
this.dateRangeMax = date;
}
else if (field.endDateField && (!this.dateRangeMin || (date.getTime() !=
this.dateRangeMin.getTime()))) {
var end = Ext.getCmp(field.endDateField);
end.setMinValue(date);
end.validate();
this.dateRangeMin = date;
}
return true;
},
Checkbox/Radio Groups
La nuova versione di Ext ha aggiunto alcune utility per la gestione dei pulsanti checkbox e radio.
Queste utility sono visualizzabili su questa pagina. L'esempio mostra gruppi di pulsanti radio e
checkbox raggruppati tra di loro in molti modi. Per definire un gruppo di pulsanti a scelta multipla
(checkbox) basta elencarli nel consueto attributo/array items e collegarli con un xtype di tipo
checkboxgroup:
{
// Use the default, automatic layout to distribute the controls
evenly
// across a single row
xtype: 'checkboxgroup',
fieldLabel: 'Auto Layout',
items: [
{boxLabel: 'Item 1', name: 'cb-auto-1'},
{boxLabel: 'Item 2', name: 'cb-auto-2', checked: true},
{boxLabel: 'Item 3', name: 'cb-auto-3'},
{boxLabel: 'Item 4', name: 'cb-auto-4'},
{boxLabel: 'Item 5', name: 'cb-auto-5'}
]
}
Nella struttura dati chiamata store, ottenuta tramite la creazione di un'istanza di SimpleStore,
vengono introdotti tre campi; questi tre campi vengono poi valorizzati da una lista di dati ottenuta
dal file states.js. A questo punto la struttura store viene utilizzata come elemento di store (appunto)
della nostra combobox, a cui vengono associate alcune caratteristiche interessanti. Ad esempio una
caratteristica molto utile è l'emptyText, ovvero il testo che viene inserito nella nostra combobox fino
a quando non si va e si comincia a scrivere il valore che stiamo cercando. Un'altra caratteristica che
possiamo notare è che la Ext.form.ComboBox viene associata nella nostra pagina HTML a un
elemento di tipo input type text. Nei prossimi articoli vedremo come si usano le griglie di Ext e
comprederemo piu' cose riguardo agli store di dati, in tale sede riprenderemo in considerazione le
combobox e alcune loro caratteristiche:
<input type="text" id="local-states" size="20"/>
Altre caratteristiche sono molto semplici e intuitive. Si possono comunque trovare elencate su
questa pagina.
ComboBox Templates
Un aspetto associato alle combobox che deve essere tenuto in considerazione è la capacità di queste
ultime di essere associate ad un template HTML. L'uso dei template è riccorrente in Ext. Per chi ha
lavorato con strumenti come Velocity di Apache o altri strumenti Java, la sintasssi dei template può
risultare piuttosto intuitiva. Anche chi non ha conoscenze di disegno di template può comprenderli
semplicemente. Basta capire che le parti variabili in un template sono racchiuse tra parentesi graffe
e che ciò che è contenuto nelle parentesi è un comando o una variabile. Possiamo vedere, per
esempio, il codice su questa pagina.
Vediamo infatti come qualsiasi campo possa risultare una combobox e come a un campo di testo si
possa associare un template complesseo per esporre i risultati di una ricerca Ajax. In particolare il
template utilizzato nell'esempio esposto nel link è il seguente:
// Custom rendering Template
var resultTpl = new Ext.XTemplate(
'<tpl for="."><div class="search-item">',
'<h3><span>{lastPost:date("M j, Y")}<br />by
{author}</span>{title}</h3>',
'{excerpt}',
'</div></tpl>'
);
Il risultato di questo template è un insieme di rige che hanno una formattazione del campo data
(lastPost), e a capo il nome dell'autore e il titolo del post associato.
Conclusione
In questi due articoli abbiamo avuto modo di visionare rapidamente i maggiori componenti di un
modulo. Abbiamo ritenuto utile soffermarci di più su alcuni aspetti come la validazione che
possono essere personalizzati a seconda delle nostre esigenze. Le altre componenti che abbiamo
visto sono molto simili tra loro e una volta comprese le caratteristiche comuni (dato che sono tutte
un'estensione di field) ogni utente potrà utilizzarle in maniera rapida nei propri moduli. La
componente che più potrà essere customizzata e resa unica a seconda del progetto o del sito che
stiamo sviluppando rimane la combobox, che potrà mostrarci liste, tabelle e dati che possono essere
formattati in maniera rapida, velocizzando il disegno delle nostre GUI e l'interazione tra utente e
parte server dei nostri applicativi.
Lo store
Il primo elemento utile a descrivere una grigliaè il suo contenuto. Come riempirla e come
formattare gli elementi che verranno mostrati in tabella, è compito dello store.
La classe Store incapsula una cache client side di record di oggetti che forniscono dati di input per i
componenti, come il GridPanel, la ComboBox, o il DataView. Un oggetto Store utilizza un oggetto
che viene chiamato DataProxy per accedere ai dati. Questa classe è una base astratta per
implementazioni che prevedono il recupero di dati non formattati. Le implementazioni di
DataProxy sono utilizzate in combinazione con un oggetto Ext.data.DataReader (costruito ad hoc a
seconda dell'oggetto che devono andara a leggere -ad esempio possono interpretare oggetti JSON o
XML). Quello che viene poi creato da un DataProxy è un blocco di Ext.data.Records che tornano
finalmente al nostro Ext.data.Store. Un oggetto Store non è a conoscenza del formato dei dati
restituiti dal proxy.
Il column model
Il secondo oggetto fondamentale nella costruzione di una griglia è il ColumnModel. Questa classe
viene inizializzata con un array di oggetti colonna utili alla configurazione dell'intera griglia.
Una particolare configurazione definisce la stringa di intestazione della colonna stessa, il campo
Ext.data.Record di dati da associare e una funzione opzionale di rendering personalizzata per
aggiustare date o per colorare il testo o lo sfondo a seconda di ciò che è presente nel dato, oppure
ancora per associare immagini a una cella a seconda di cosa contiene.
La capacità di applicare una classe CSS a tutte le celle di una colonna attraverso il suo id è un'altra
delle opzioni di configurazione con cui possiamo formattare il contenuto della nostra griglia in
maniera indipendente dai dati. In questo modo applicheremo a pieno il valido pattern MVC (model,
view, controller) utile a distinguere per bene dati, visualizzazione e informazioni di business, tra
loro.
var colModel = new Ext.grid.ColumnModel([
{header: "Settore", width: 60, sortable: true},
{header: "Azienda", width: 150, sortable: true},
{header: "Fatturato.", width: 100, sortable: true},
{header: "Previsioni", width: 100, sortable: true, renderer:
visualizzaCosti},
{header: "Impiegati", width: 100, sortable: true, resizable: false}
]);
Le opzioni di configurazione definite in questa classe possono comparire in ogni singola colonna,
altrimenti viene affidato loro il valore dio default. Ad esempio, sortable o hideable sono due
attributi che determinano se la colonna può lanciare un evento di ordinamento dei dati o se può
essere nascosta o meno; di default sono due attributi con valore true.
Un esempio pratico
Per cominciare ad andare più a fondo nella descrizione delle griglie, vedremo un esempio
relativamente semplice che ci aiuta a mostrare il contenuto di un file .xml strutturato come segue in
una tabella master-detail:
<?xml version="1.0" encoding="UTF-8"?>
<Articles>
<Article>
<PageUrl>
http://www.ictv.it/file/vedi/728/creare-un-home-page-a-tab-con-ext/
</PageUrl>
<UrlSnippet>
http://www.ictv.it/code_download/728_code.zip
</UrlSnippet>
<ArticleAttributes>
<Author>Nunzio Fiore</Author>
<Type>CODE-IT</Type>
<Title>Creare una homepage a tab con Ext</Title>
<Site>icTV</Site>
<Category>VIDEO</Category>
<HasSnippet>SI</HasSnippet>
<Lang>ITA</Lang>
<Description>Come creare un semplice layout Ext e
predisporre delle schede a tab per il nostro sito in pochi minuti.</Description>
</ArticleAttributes>
</Article>
...
</Articles>
Per poter utilizzare codice Ext nelle nostre pagine, basta includere le tre librerie principali che
definiscono oggetti e strutture CSS:
<link rel="stylesheet" type="text/css" href="../ext/resources/css/ext-
all.css" />
<script type="text/javascript" src="../ext/adapter/ext/ext-
base.js"></script>
<script type="text/javascript" src="../ext/ext-all.js"></script>
A questo punto ci affidiamo completamente alle capacità di Ext di aiutarci a generare codice non-
obtrusive e definiamo esclusivamente un div con un id specifico (ad es. binding-example) che
ci sarà utile nel codice Javascript per poter mappare il nostro oggetto dinamico con un layer che
abbiamo collocato, tramite CSS, nella nostra pagina, indipendentemente dal contenuto complesso
che andremo ad includervi:
<div id="binding-example"></div>
Nel file Javascript attraverseremo nel dettaglio tutte le fasi che abbiamo elencato utili alla creazione
della nostra prima griglia. Abbiamo scelto di costruire un esempio molto pulito e semplice in modo
da poter analizzare istruzione dopo istruzione ciò che avviene durante la creaszione di un oggetto
Ext.GridPanel. Coglieremo l'occasione per rinfrescare anche alcuni concetti che abbiamo visto nelle
scorse occasioni, e vedremo in questo modo le caratteristiche salienti di una griglia.
Cominciamo quindi ad istruire il nostro browser perché inizi a renderizzare le componenti Ext nel
momento in cui la pagina è stata caricata e l'oggetto Ext, creato grazie all'inclusione di ext-all.js e
ext-base.js, ci conferma di essere pronto:
Ext.onReady(function(){
Successivamente inseriamo una componente opzionale, ma molto utile per vedere un oggetto che
studieremo più avanti: Ext.state.Manager. Questo oggetto è utile a memorizzare delle caratteristiche
utili a descrivere gli oggetti Ext; in questo caso avremo una descrizione completa del columnModel,
la presenza, la dimenzione, la posizione delle colonne e molto altro ancora. Nella versione più
semplice e base di Ext, abbiamo l'Ext.state.CookieProvider. Questo oggetto ci permette di
inizializzare un cookie pe rogni oggetto memorizzato contentente informazioni che possono essere
decoficicate in Ext in un oggetto JSON. Nel nostro caso abbiamo istruito il CookieProvider perche'
scada dopo 10 anni:
var cp = new Ext.state.CookieProvider({
path: "/",
expires: new Date(new Date().getTime()+(1000*60*60*24*3650))
})
Ext.state.Manager.setProvider(cp);
Vediamo ora un'istruzione che abbiamo esaminato nell'articolo riguardante i temi e i layout. Con
Ext.util.CSS riusciamo a dire al browser di associare un tema particolare a ogni oggetto mostrato
nella nostra pagina. Il tema in questione è descritto completamente nel file che troviamo a questo
path ../ext/resources/css/xtheme-gray.css:
Ext.util.CSS.swapStyleSheet('theme', '../ext/resources/css/xtheme-
gray.css');
Dopo aver inizializzato quindi il nostro ambiente possiamo passare a descrivere subito l'oggetto che
sta alla base di una griglia, ovvero lo store.
Per inizializzarlo basta creare un'istanza di Ext.data.Store tramite la keyword new.
Uno dei primi attributi che andiamo a definire è url; qui facciamo puntare al file article.xml di cui
parlavamo prima. Subito dopo istruiamo l'oggetto a capire che dovrà leggere il contenuto di ciò che
trova in url come un file Xml. Pertanto associamo all'attributo reader un'istanza di
Ext.data.XmlReader. Nella descrizione degli attributi di XmlReader troviamo una completa
mappatura dei tag xml, in modo che lo store riesca a parserizzare il file xml e i suoi contenuti e
associ un oggetto Ext.data.Record ad ogni elemento del file xml:
A cui viene associato lo store creato precedentemente. Notiamo come la parola store compaia sia
dalla parte del nome dell'attributo che dalla parte in cui rappresenta il nome della variabile che
contiene lo store:
store: store,
Ci sono diverse caratteristiche di selezione che possono essere associate a un grid. Nel nostro
esempio abbiamo deciso di creare una tabella che permetta una selezione alla volta per ogni riga
della griglia. Infine abbiamo inserito le ultime caratteristiche utili a persponalizzare il nostro
esempio. Con forceFit uguale a true, istruiamo la tabella a occupare tutto lo spazio che le viene
messo a disposizione; gli altri attributi sono simili a quelli che abbiamo descritto quando abbiamo
parlato delle componenti panel e window:
sm: new Ext.grid.RowSelectionModel({singleSelect: true}),
viewConfig: {
forceFit: true
},
height:310,
split: true,
region: 'north'
});
La peculiarità del nostro esempio è data dal Template. Dato che stiamo costruendo una tabella
Master/Detail, abbiamo bisogno di un oggetto che descriva come distribuire all'interno del pannello
di dettaglio le informazioni contenute nel file xml e che non necessariamente compaiono in griglia.
La sintassi un template è molto semplice. Vengono utilizzate le parentesi graffe per racchiudere le
parti variabili del codice HTML che inseriamo nell'array del template, successivamente l'array viene
associato come argomento dell'istanza di Ext.Template:
// define a template to use for the detail view
var articleTplMarkup = [
'<b>Title</b>: <a href="{PageUrl}"
target="_blank">{Title}</a><br/>',
'<b>Author</b>: {Author}<br/>',
'<b>Lang</b>: {Lang}<br/>',
'<b>Type</b>: {Type}<br/>',
'<b>Site</b>: {Site}, {Category}<br/>',
'<b>Description</b>: {Description}<br/>',
'<b>Has a snippet for download</b>: {HasSnippet}<br/> ',
'<a href="{UrlSnippet}" target="_blank">{UrlSnippet}</a>',
];
Finalmente arriviamo a creare il Panel che verrà collegato tramite l'attributo renderTo con il div
con id binding-example che abbiamo visto in precedenza:
var ct = new Ext.Panel({
renderTo: 'binding-example',
frame: true,
title: 'Article List',
width:800,
height: 600,
layout: 'border',
Il pannello che abbiamo creato ha un layout di tipo border. La griglia che abbiamo descritto in
precedenza aveva un attributo region uguale a north, pertanto, associandolo al Panel ct, tramite
l'attributo items, riusciamo a posizionare in alto la griglia e in centro un pannello con id uguale a
detailPanel in cui andremo a sovrascrivere il nostro template a fronte di ogni selezione:
items: [
grid,
{
id: 'detailPanel',
region: 'center',
bodyStyle: {
background: '#fefefe',
padding: '7px'
},
html: '
Please select an article to see additional details.'
}
]
})
Per creare l'effetto di master/detail dobbiamo quindi recuperare l'evento rowselect associato al
selection model della nostra griglia e creare una funzione che sovrascriva il template articleTpl nel
corpo del pannello detailPanel, con i dati contenuti nel record r.data:
grid.getSelectionModel().on('rowselect', function(sm, rowIdx, r) {
var detailPanel = Ext.getCmp('detailPanel');
articleTpl.overwrite(detailPanel.body, r.data);
});
Ultima importante istruzione del nostro esempio è store.load(). Scateniamo così una lettura
dell'url descritto in store, i dati associati al record, il record associato al template e così via:
store.load();
Conclusione
In questo articolo abbiamo analizzato nel dettaglio le caratteristiche basilari di una griglia, nel
prossimo potremo affrontare argomenti leggermente piu' complessi come il paging, le griglie
editabili, il JsonReader, il GroupingView e il CheckboxSelectionModel.
L'oggetto DataReader
L'oggetto Ext.DataReader è una classe base astratta per la lettura di informazioni strutturate da una
fonte di dati. Questi vengono convertiti in un oggetto contenente Ext.data.Record che, come
abbiamo visto nell'articolo precedente, sono composti da dati e metadati (informazioni utili a
leggere le informazioni stesse, ad esempio il tipo di dato di una colonna) per l'uso da parte di un
Ext.data.Store. Questa classe è destinata ad essere estesa e non deve essere utilizzata direttamente.
Le implementazioni esistenti al momento sono l'Ext.data.ArrayReader, Ext.data.JsonReader e
Ext.data.XmlReader. Nell'esempio visto nel primo articolo abbiamo visto un esempio di
XmlReader, pertanto in questo vedremo il JsonReader.
JsonReader
È una particolare implementazione del DataReader. Anch'esso è una classe utile a creare un array di
oggetti Ext.data.Record da una risposta Ajax che contiene un oggetto JSON.
Codice di esempio:
var Employee = Ext.data.Record.create([
{name: 'firstname'}, // Map the Record's "firstname" field
to the row object's key of the same name
{name: 'job', mapping: 'occupation'} // Map the "job" field to the row
object's "occupation" key
]);
var myReader = new Ext.data.JsonReader({
totalProperty: "results", // The property which contains the
total dataset size (optional)
root: "rows", // The property which contains an
Array of row objects
id: "id" // The property within each row object
that provides an ID for the record (optional)
}, Employee);
Dall'esempio risulta chiaro quindi come una serie di Record di tipo Employee vengano restituiti dal
JsonReader durante la lettura del'oggetto Json. Di conseguenza l'array di record verrà poi utilizzato
dallo store della griglia per esporre i dati recuperati dal server.
GroupingView
Raggruppare i dati di una griglia è una delle tecniche più utili in termini di usabilità. Grazie ad Ext
possiamo farlo con pochissime istruzioni. Vedremo come sia possibile scegliere per quali dati
raggruppare le nostre infromazioni, anche a runtime, ovvero dopo che la griglia è stata renderizzata
nella pagina. Il grouping che si ottiene è, in parole povere, simile al grouping delle mail in Outlook,
dove possiamo scegliere se raggruppare per argomento, per data o per mittente. Vediamo un
esempio:
var grid = new Ext.grid.GridPanel({
// A groupingStore is required for a GroupingView
store: new Ext.data.GroupingStore({
reader: reader,
data: xg.dummyData,
sortInfo:{field: 'company', direction: "ASC"},
groupField:'industry'
}),
columns: [
{id:'company',header: "Company", width: 60, sortable: true, dataIndex:
'company'},
{header: "Price", width: 20, sortable: true, renderer:
Ext.util.Format.usMoney, dataIndex: 'price'},
{header: "Change", width: 20, sortable: true, dataIndex: 'change',
renderer: Ext.util.Format.usMoney},
{header: "Industry", width: 20, sortable: true, dataIndex: 'industry'},
{header: "Last Updated", width: 20, sortable: true, renderer:
Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
],
frame:true,
width: 700,
height: 450,
collapsible: true,
animCollapse: false,
title: 'Grouping Example',
iconCls: 'icon-grid',
renderTo: document.body
});
Rispetto al codice che abbiamo visto fino ad ora, possiamo notare che la differenza fondamentale
risiede nella dichiarazione dello store: store: new Ext.data.GroupingStore; e nella proprieta' view
della griglia: view: new Ext.grid.GroupingView. In quest'ultima notiamo la definizione di un
template per l'header del gruppo, in maniera che vengano scritti quanti record appartengono a un
gruppo e il nome del campo per cui stiamo raggruppando.
CheckboxSelectionModel
Esistono diverse utility che possono essere associate alle nostre griglie per aumentarne l'usabilità.
Una su tutte è il CheckboxSelectionModel che ci permette di associare una prima colonna di
checkbox per selezionare facilmente tutte o alcune righe della nostra griglia, a cui poi assoceremo
un'azione multipla sui record.
Per creare un semplice esempio dobbiamo innanzitutto creare il CheckboxSelectionModel:
var sm = new Ext.grid.CheckboxSelectionModel();
Fornire l'oggetto selectionModel come current selection model per la griglia grid. Per far ciò
scriveremo la seguente istruzione nella lista delle proprieta' della griglia:
selMode: sm
Paging
Una griglia con paginazione deve avere un elemento lato server per eseguire la scomposizione dei
dati in pagine.
Quale che sia il linguaggio utilizzato lato server e il database, l'importante è che venga restituito un
oggetto Json con il numero di record totali, con i dati della pagina corrente e che sia il risultato di
una selezione sul database limitata alla pagina in questione.
L'unica differenza nello Store è l'aggiunta di una proprietà totalProperty. Nel nostro esempio, si usa
'total', che arriva da uno nostro script lato server con il valore per il numero totale di righe:
var ds = new Ext.data.Store({
});
A questo punto bisogna aggiungere una barra di paging (primo, indietro, avanti, ultimo) alla parte
inferiore del riquadro della griglia e il gioco è quasi fatto:
var gridFoot = grid.getView().getFooterPanel(true);
L'ultimo passo è quello di passare il primo limite di inizio e i parametri per caricare i dati:
ds.load({params:{start:0, limit:25}});
Griglie editabili
Le griglie editabili sono sicuramente l'approccio più interessante in termini di interazione e usabilità
dei dati. Abbiamo creato un esempio pratico che potete scaricare da qui e che ho già utilizzato in un
video per ICTV.
Un esempio pratico
Abbiamo collocato all'inizio della nostra pagina il contenuto dei dati della nostra tabella e messo in
un Array. Questa contiene i dati seguendo un particolare ordine. Nel nostro caso ogni riga dell'array
dei dati contiene rispettivamente il nome di un'attività, la sua data di inizio, la relativa scadenza, e
uno stato che ci aiuta a capire se l'attività e in corso, pianificata o altro:
var myData = [
['Realizzare 2 video per code it','03/06/2008','23/06/2008','IN CORSO'],
['Realizzare 3 articolo per
HTML.it','23/06/2008','24/06/2008','PIANIFICATA'],
['Procedere con il disegno delle applicazioni
IPHONE','13/04/2008','23/05/2008','IN RITARDO'],
['Comprare un IPHONE','03/07/2008','23/07/2008','PIANIFICATA'],
['Comprare MAC','04/07/2008','23/08/2008','PIANIFICATA'],
['Scrivere lettera a Mario
Rossi','09/06/2008','02/10/2008','PIANIFICATA'],
['Cercare ristorante per Barbara
Bianchi','01/10/2008','23/12/2008','PIANIFICATA']
];
Conclusione
Il nostro viaggio sugli aspetti client di Ext sta volgendo al termine. Le componenti più importanti
sono emerse e la loro composizione ci può portare a costruire già applicazioni relativamente
complesse. Nei prossimi articoli affronteremo altre due componenti fondamentali di Ext: alberi e
menu.