L’argomento della validazione dei dati di una form tramite JavaScript è noto alla maggior parte degli sviluppatori di applicazioni Web. L’approccio classico consiste nell’intercettare un determinato evento (Blur, Click, Submit) ed eseguire un’apposita funzione JavaScript che verifichi la validità dei dati inseriti dall’utente.
Questa soluzione, descritta dall’articolo Validare e inviare un form con JavaScript , ha indubbi vantaggi, tra cui l’immediatezza della risposta nel caso di inserimento di valori errati, il non utilizzo della banda di comunicazione e il non coinvolgimento della parte server dell’applicazione. Tuttavia, l’approccio classico alla validazione dei dati presenta qualche svantaggio.
Gli svantaggi dell’approccio classico
Non mi riferisco alla possibilità che il client non supporti JavaScript o il supporto del linguaggio sia disabilitato. Questa eventualità è sempre presente e deve essere gestita con un ulteriore controllo server-side. Mi riferisco, invece, al fatto che le funzioni di validazione hanno quasi sempre la stessa struttura e lo sviluppatore deve ricrearla tutte le volte per ciascun campo da convalidare.
Consideriamo il semplice caso di una form con due campi, nome e cognome, la cui funzione di convalida verifichi semplicemente che sia stato effettivamente inserito un valore in ciascuno di essi. La form potrebbe avere la seguente struttura:
<form method="post" name="dati" action="elabora.asp"
onSubmit="return Convalida()">
Nome: <input type="text" name="nome"/>
<br/>
Cognome: <input type="text" name="cognome"/>
<br/>
<input type="submit" value="Invia" />
</form>
La funzione di convalida potrebbe avere la seguente forma:
function Convalida() {
var nome = document.dati.nome.value;
var cognome = document.dati.cognome.value;
if ((nome.value == “") || (nome.value == “undefined"))
{
alert(“Campo obbligatorio!");
document.dati.nome.focus();
return false;
}
if ((cognome.value == “") || (cognome.value == “undefined"))
{
alert(“Campo obbligatorio!");
document.dati.cognome.focus();
return false;
}
return true;
}
Cosa succede se aggiungiamo un terzo campo alla form, per esempio un campo per l’indirizzo, e vogliamo che anche l’inserimento di un valore in questo campo sia obbligatorio? Dobbiamo aggiungere un altro if analogo a quelli per la convalida dei campi nome e cognome. E se oltre a verificare che venga inserito un valore nei campi dobbiamo verificare che il numero di caratteri sia compreso tra un valore minimo ed un massimo? Dobbiamo intervenire su tutti gli if della funzione di convalida. Niente di complicato, ma ad una più attenta analisi possiamo individuare almeno i seguenti svantaggi:
- la scrittura delle funzioni di convalida è un’attività ripetitiva e come tale può essere fonte di errori di digitazione, specialmente se si utilizza il copia ed incolla e può sfuggire qualche dettaglio dell’adattamento del codice per un nuovo campo;
- la manutenzione del codice può diventare impegnativa quando si scopre un bug o si deve effettuare una modifica;
- il codice per la validazione risulta difficilmente riusabile in altre applicazioni.
Un approccio dichiarativo
Non sarebbe più comodo poter creare dei campi sui quali imporre dei vincoli senza scrivere codice in maniera ripetitiva? Cioè semplicemente dichiarare che un determinato campo richiede obbligatoriamente un valore, che tale valore sia di un certo tipo, che il numero di caratteri sia compreso tra un minimo ed un massimo, ecc.
La libreria DDV (Declarative Data Validation) consente di fare proprio questo. DDV è una libreria JavaScript che consente di specificare pseudo-attributi HTML per dichiarare dei vincoli di validità per i controlli di una form.
Come usare la libreria
La libreria, allegata per il download al presente articolo, è composta da un unico file dataValidation.js contenente il codice JavaScript che effettua la validazione in base ad una serie di pseudo-attributi HTML. Per abilitare un documento HTML all’utilizzo della libreria è necessario inserire un riferimento ad essa nella sezione <head> del documento:
<script type="text/javascript" src="dataValidation.js"></script>
Avendo a disposizione la libreria, la convalida della form analizzata prima può essere trasformata come nel seguente esempio:
<form method="post" name="dati" action="elabora.asp"
onSubmit="return formValidate(this)">
Nome: <input type="text" name="nome" ddv-required="true"
/>
<br/>
Cognome: <input type="text" name="cognome" ddv-required="true"/>
<br/>
<input type="submit" value="Invia" />
</form>
Come potete vedere non è necessario scrivere nessuna funzione JavaScript, ma è sufficiente specificare lo pseudo-attributo HTML ddv-required = “true" e chiamare la funzione formValidate() passando come parametro l’oggetto che rappresenta la form corrente (this).
Gli pseudo-attributi
La versione attuale della libreria (1.0) prevede i seguenti pseudo-attributi:
ddv-maxlength
Indica il numero massimo di caratteri consentito in un campo. Ad esempio, il seguente campo prevede un numero massimo di 10 caratteri:
<input type="text" ddv-maxlength="10" />
ddv-minlength
Indica il numero minimo di caratteri richiesto da un campo. Il seguente esempio indica che il campo prevede almeno cinque caratteri:
<input type="text" ddv-minlength="5" />
Utilizzato in combinazione con ddv-maxlength è possibile indicare un intervallo di lunghezze in caratteri previste per il campo.
ddv-required
Indica se l’inserimento di un valore nel campo è obbligatorio (true) o meno (false).
ddv-type
Consente di specificare il tipo di dato previsto per il campo. Attualmente è possibile specificare i tipi di dato numeric e date. Il seguente esempio mostra come utilizzare questo pseudo-attributo HTML per convalidare un campo di tipo numerico ed uno di tipo data:
<input type="text" ddv-type="numeric" />
<input type="text" ddv-type="date" />
Nel caso di data, il formato predefinito è quello inglese (mm/dd/yyyy), quindiverranno considerate valide date come 12/31/2004 mentre non saranno considerate valide date del tipo 31/12/2004. Affinché vengano considerate valide le date nel formato italiano è necessario specificare un ulteriore pseudo-attributo HTML ddv-dateformat, come nel seguente esempio:
<input type="text" ddv-type="date" ddv-dateformat="it" />
ddv-regexp
Questo pseudo-attributo consente di specificare una espressione regolare in base alla quale il valore del campo viene considerato valido. Ad esempio, il seguente campo prevede una sequenza di caratteri che segua lo schema di un codice fiscale:
<input type="text" ddv-regexp="[a-zA-Z]{6}dd
[a-zA-Z]dd[a-zA-Z]ddd[a-zA-Z]"/>
Convalida di form e convalida di campo
Una volta definiti i vincoli su ciascun campo della form, la convalida viene effettuata invocando una funzione della libreria DDV. La libreria prevede due funzioni di convalida: fieldValidate() e formValidate(). La prima consente di effettuare la convalida per un singolo campo mentre la seconda convalida tutti i campi di una form che contengono gli pseudo-attributi visti prima.
La funzione fieldValidate() prevede in input un oggetto che rappresenta il campo da convalidare e restituisce un valore booleano che indica se il campo è valido (true) o meno (false).
La funzione formValidate() prevede in input un oggetto che rappresenta la form da convalidare e restituisce un valore booleano che indica se il campo è valido (true) o meno (false).
Il momento in cui effettuare la convalida dipende dalle scelte dello sviluppatore.
Nell’esempio visto prima la convalida veniva effettuata al momento dell’evento submit, ma nulla vieta di effettuare la convalida campo per campo ogni volta che il focus passa da uno all’altro, come nel seguente esempio:
<form method="post" name="dati" action="elabora.asp">
Nome: <input type="text" name="nome"
ddv-required="true" onBlur="fieldValidate(this)" />
<br/>
Cognome: <input type="text" name="cognome"
ddv-required="true" onBlur="fieldValidate(this)" />
<br/>
<input type="submit" value="Invia" />
</form>
Personalizzare le convalide
La libreria DDV prevede anche la possibilità di effettuare convalide personalizzate. La personalizzazione può essere fatta specificando una propria funzione JavaScript che verrà eseguita dopo la convalida standard. Cerchiamo di chiarire il concetto con qualche esempio.
Supponiamo che un campo debba prevedere l’inserimento obbligatorio di un valore numerico e che questo valore debba essere compreso tra 1 e 10, estremi inclusi. Possiamo esprimere questi vincoli nel seguente modo:
<input type="text" ddv-required="true" ddv-type="numeric"
onBlur="fieldValidate(this, ‘miaConvalida()’)" />
Gli pseudo-attributi controlleranno che sia stato specificato un campo e che tale campo sia di tipo numerico, mentre l’ulteriore convalida viene delegata alla funzione JavaScript miaConvalida() opportunamente scritta dal programmatore.
La funzione di convalida supplementare deve essere passata come secondo argomento della funzione fieldValidate(). Essa deve essere specificata come stringa con gli eventuali parametri. La funzione viene valutata dopo la convalida automatica derivata dagli pseudo-attributi e deve restituire un valore booleano a seconda che la validazione abbia avuto successo o meno.
Anche la funzione formValidate() prevede un secondo parametro opzionale per specificare l’eventuale funzione di convalida supplementare. Bisogna tener presente, comunque, che anche in questo caso la funzione viene eseguita dopo le validazioni standard.
Abbiamo presentato l’utilizzo della libreria JavaScript DDV che consente di utilizzare un approccio dichiarativo nella convalida dei dati lato client. L’approccio dichiarativo consente di evitare la scrittura ripetitiva di codice JavaScript per la convalida dei dati inseriti dall’utente ed offre un modello più comodo e lineare.
Analizzeremo il funzionamento interno della libreria e vedremo come possa essere facilmente estesa per includere la gestione di eventuali altri pseudo-attributi.
Abbiamo visto Declarative Data Validation, una libreria JavaScript che consente di adottare un approccio dichiarativo per la convalida dei dati in una form.
L'approccio dichiarativo ci permette di utilizzare semplicemente degli pseudo-attributi HTML per dichiarare i vincoli da imporre ad un campo di una form invece di scrivere codice JavaScript.
In questo articolo vediamo come è implementata la libreria e come funziona la convalida dei dati.
Ricordiamo che DDV può essere liberamente modificata e distribuita sotto i vincoli della licenza GPL.
Struttura della libreria
La struttura della libreria è molto semplice. Essa è costituita da un piccolo numero di funzioni JavaScript, ma le funzioni fondamentali per la validazione sono:
- formValidate()
È la funzione che si occupa di convalidare i campi di una form che contengono gli opportuni pseudo-attributi - fieldValidate()
Questa funzione convalida un singolo campo che contiene pseudo-attributi; può essere considerata la funzione fondamentale della libreria
Le altre funzioni (verifyDateFormat(), isBlank(), setConstraint()) sono di supporto o hanno scopi particolari che analizzeremo in un successivo articolo.
Nel seguito analizzeremo in dettaglio le due funzioni di base e cercheremo di capire come avviene la convalida dei dati.
Gestione degli pseudo-attributi
Il punto centrale del funzionamento della libreria consiste nella possibilità di individuare tramite codice JavaScript gli pseudo-attributi definiti nel codice HTML. Questa possibilità ci viene offerta dalla proprietà attributes del campo da convalidare. La proprietà rappresenta un array di tutti gli attributi dell'oggetto HTML da analizzare, compresi gli attributi non standard.
Alla luce di questa possibilità possiamo avere un'idea di come effettuare il controllo, ad esempio, del numero minimo di caratteri per un campo, corrispondente allo pseudo-attributo ddv-minlength:
function fieldValidate(field) {
...
//ddv-minlength
if (field.attributes["ddv-minlength "] != null) {
if ((field.value.length < field.attributes["ddv-minlength "].value)
&& (field.value.length != 0)) {
alert("Le dimensioni del campo devono essere di almeno " + field.attributes["ddv-minlength "].value + " caratteri!")
field.focus()
return false
}
}
...
}
Il primo controllo effettuato consiste nell'accertarsi che sia stato assegnato lo pseudo-attributo ddv-minlength al campo da convalidare. In caso affermativo viene effettuato il controllo sulla lunghezza del campo. Se il valore della lunghezza del campo è inferiore al valore specificato per lo pseudo-attributo ddv-minlength viene visualizzato un messaggio d'errore, il focus viene posizionato sul campo coinvolto e viene restituito il valore false.
Da notare, in questo caso specifico, che il campo viene considerato valido se la sua lunghezza è uguale a zero. In caso contrario lo pseudo-attributo ddv-minlength implicherebbe l'obbligo di inserire un valore, cosa che invece è delegata all'attributo ddv-required.
Questo semplice esempio mostra come è realizzata la funzione fieldValidate(): una sequenza di if ciascuno dei quali verifica la presenza di uno degli pseudo-attributi ed effettua il corrispondente controllo.
Il codice completo è accessibile direttamente dalla libreria.
Convalida di una form
La convalida di tutti i campi di una form è delegata alla funzione formValidate(). Questa si limita semplicemente ad effettuare un ciclo sugli elementi contenuti nella form e, per ciascun elemento, invoca la funzione fieldValidate():
function formValidate(form) {
var i
var fields = form.elements
var l = fields.length
var valid = false
for (i = 0; i < l; i++) {
valid = fieldValidate(fields[i])
if (!valid) {
return false
}
}
return true
}
Come si evince dal codice, alla prima occorrenza di un campo non valido la funzione termina restituendo il valore false.
Gestione delle funzioni personalizzate
Le funzioni di validazione fieldValidate() e formValidate() prevedono la possibilità di specificare una funzione supplementare da eseguire dopo la convalida standard. La funzione di convalida supplementare viene specificata come stringa nel secondo parametro opzionale delle due funzioni e la sua gestione è abbastanza semplice: se la funzione personalizzata è stata specificata viene eseguita tramite la funzione eval() altrimenti la funzione di convalida termina.
Il seguente codice riporta la funzione formValidate() integrata con la gestione delle funzioni personalizzate:
function formValidate(form, func) {
var i
var fields = form.elements
var l = fields.length
var valid = false
for (i = 0; i < l; i++) {
valid = fieldValidate(fields[i])
if (!valid) {
return false
}
}
if (func != null) {
return eval(func)
} else {
return true
}
}
La funzione fieldValidate() segue lo stesso schema. Da notare che nel caso in cui sia stata specificata una funzione supplementare da eseguire dopo la convalida standard, il valore restituito da formValidate() e fieldValidate() corrisponde al valore restituito dalla funzione supplementare. È bene tener presente questo particolare nel caso in cui le funzioni di convalida vengano richiamate in corrispondenza di eventi il cui valore di output determina specifici comportamenti, come ad esempio l'evento onSubmit.
Abbiamo analizzato l'implementazione della libreria DDV. Abbiamo visto come il principio fondamentale su cui si basa la libreria è l'accessibilità agli attributi di un elemento della form tramite JavaScript.
La struttura della libreria è abbastanza semplice ed è facile espanderla con altri pseudo-attributi per il controllo di altri vincoli. Tuttavia, l'utilizzo di attributi personalizzati comporta alcune problematiche che andiamo ad analizzare.
L'utilizzo della libreria DDV per la convalida dei dati di una form secondo un approccio dichiarativo, pone alcuni problemi e considerazioni.
Compatibilità con i browser
Il problema principale per la compatibilità con i vari browser risiede nell'utilizzo della proprietà attributes per accedere agli pseudo-attributi HTML sui quali si basa la validazione dichiarativa.
Questo attributo risulta correttamente supportato dai seguenti browser: Microsoft Internet Explorer 6.0, Mozilla 1.6, Netscape 6.0, Opera 7.5. Tuttavia, nelle precedenti versioni di questi browser il supporto di questa proprietà risulta inesistente o incompleto.
Occorre tenere presente questo aspetto per decidere se utilizzare o meno la libreria DDV per la validazione client-side. In ogni caso è possibile modificare il codice della libreria aggiungendo le opportune istruzioni per individuare il browser ed eseguire eventualmente la validazione. Indipendentemente dal browser che accede alla nostra form, inoltre, è fortemente consigliata la convalida dei dati anche sul lato server. Questo per evitare brutte sorprese nel caso in cui l'utente abbia disabilitato il supporto per l'esecuzione degli script client-side.
Aderenza agli standard W3C
La presenza degli pseudo-attributi implica la non aderenza agli standard W3C. Questo aspetto comporta una mancata convalida del documento Web che utilizza la libreria da parte degli strumenti di valutazione del codice HTML/XHTML.
In teoria, essendo XHTML è una ridefinizione di HTML in termini di XML (cfr. XHTML: il futuro di HTML), dovrebbe risultare estensibile per definizione. L'estensibilità potrebbe essere ottenuta mediante l'utilizzo dei namespace, ma gli strumenti di valutazione della conformità del codice XHTML non consentono questa possibilità.
Nelle stesse specifiche di XHTML si legge che il W3C sta studiando un modo per definire la conformità per documenti che coinvolgono più namespace e propone come esempio l'integrazione XHTML+MathML (http://www.w3.org/TR/2002/REC-xhtml1-20020801/#well-formed).
In ogni caso, il problema della conformità del codice HTML/XHTML che utilizza gli pseudo-attributi può essere aggirato generando dinamicamente gli attributi stessi tramite codice JavaScript.
A tale scopo la libreria DDV mette a disposizione la funzione setConstraint() che consente di creare dinamicamente ciascun vincolo di validazione dei campi della form, lasciando il codice HTML/XHTML originario conforme alle specifiche W3C.
Generazione dinamica degli pseudo-attributi
Ma vediamo in pratica come utilizzare la funzione setConstraints().
Supponiamo di avere il seguente codice XHTML che definisce una form con due campi:
<form method="post" action="elabora.asp" onsubmit="return Convalida()">
Nome: <input type="text" id="nome"/>
<br/>
Cognome: <input type="text" id="cognome"/>
<br/>
<input type="submit" value="Invia" />
</form>
Il codice è conforme alle specifiche standard XHTML 1.0. Per sfruttare l'approccio dichiarativo della validazione offerto dalla libreria DDV dobbiamo definire una funzione di inizializzazione che assegneremo all'evento onload del body:
<body onload="initValidation()">
La funzione initValidation() si occuperà di creare i vincoli da impostare sui campi della form sfruttando la funzione setConstraint(). Questa funzione prevede tre parametri di tipo stringa: l'ID dell'elemento a cui applicare il vincolo di validazione, il nome dell'attributo da impostare ed il valore da assegnare all'attributo stesso. Facendo riferimento alla form dell'esempio precedente e supponendo di volere imporre un valore obbligatorio per entrambi i campi, la funzione avrà la seguente forma:
function initValidation() {
setConstraint("nome", "ddv-required", "true");
setConstraint("cognome", "ddv-required", "true");
}
In questo modo gli pseudo-attributi ddv-required per i campi nome e cognome vengono aggiunti dinamicamente al DOM (Document Object Model) della pagina corrente, lasciando inalterato il codice XHTML.
Un'osservazione particolare riguarda l'uso della funzione setConstraint() per la creazione di un attributo ddv-regexp. In questo caso, occorre prestare attenzione all'espressione regolare da assegnare all'attributo, in particolare alla presenza del carattere che viene interpretato da JavaScript come carattere di escape. Ad esempio, se si vuole assegnare l'espressione regolare [a-zA-Z]{6}dd[a-zA-Z]dd[a-zA-Z]ddd[a-zA-Z] che identifica un codice fiscale è necessario raddoppiare i caratteri in \, come nel seguente esempio:
setConstraint("codiceFiscale", "ddv-regexp",
"[a-zA-Z]{6}\d\d[a-zA-Z]\d\d[a-zA-Z]\d\d\d[a-zA-Z]");
Altri schemi di validazione
Esistono altre tecnologie che offrono un approccio dichiarativo alla validazione dei dati inseriti da un utente in una form HTML. In particolare, ASP.NET prevede una serie di controlli specifici per la validazione, mentre ColdFusion prevede alcuni attributi che consentono un approccio analogo a quello della libreria DDV.
Tuttavia, entrambi gli approcci si basano su una interpretazione server-side dei controlli e degli attributi. Anche se ASP.NET genera codice JavaScript per la validazione client-side, la soluzione rimane vincolata ad una specifica tecnologia di elaborazione server-side.
Rispetto a queste soluzioni, la libreria DDV offre un approccio completamente indipendente dall'elaborazione server-side e, pertanto, può essere utilizzata con qualsiasi tipo di elaborazione sul server (CGI, ASP, ASP.NET, ColdFusion, PHP, ecc.).
Conclusioni
Concludiamo la panoramica sulla libreria DDV analizzando le problematiche relative alla compatibilità tra i browser, l'aderenza agli standard W3C e confrontandola con gli schemi di validazione server-side.
Naturalmente la libreria può essere ampliata e migliorata per supportare altri tipi di convalida. Vi rimandiamo al sito ufficiale http://ddv.manthys.it su cui verranno pubblicate le successive evoluzioni.