En aquesta pràctica tractarem de la validació de les dades introduïdes per l'usuari visitant de la nostra pàgina.

Hi ha ocasions que l'usuari ha d'introduir informació sol·licitada per la pàgina, per exemple dades d'un formulari que posteriorment s'ha d'enviar, valors numèrics per processar en un programa de càlculs, valors textuals acotats entre poques opcions de resposta a algunes preguntes. etc.

No podem refiar-nos que la informació introduïda per l'usuari estigui d'acord amb el format de les dades esperades. Introduir un text quan s'espera un número, una adreça de correu sense el símbol @, etc. solen ser errors habituals.

Per tant, el programa JavaScript ha de verificar que els valors introduïts siguin coherents, és a dir, ha de validar l'entrada d'informació, i si no és correcta, advertir l'usuari del seu error.

La manera de verificar si la informació és correcta es fa analitzant les dades introduïdes mitjançant comparacions amb els possibles valors correctes, o bé, comparant amb expressions regulars.
  Elements del llenguatge en aquest capítol
Propietat length : Conté la longitud de la cadena en nombre de caràcters.
Mètode charAt(n) : Retorna el caràcter situat en la posició n dins la cadena, començant
  per 0.
Mètode substring(inici,final) : Retorna la subcadena ubicada entre les posicions inici-final
  d'una cadena.
Mètodes indexOf(car) i lastIndexOf(car) : Retornen la posició dins una cadena d'un
  caràcter  car, començant a buscar des del principi o des del final, respectivament.
  Retornen -1 si no l'han trobat.
Mètodes toLowerCase() i toUpperCase() : Converteixen a minúscules i majúscules,
  respectivament, els caràcters d'una cadena.
Mètode isNaN(valor) : Retorna true si valor és valor no numèric (is not a number). En cas
  contrari, si valor és numèric, retorna false.
Mètode match(patró) : Retorna la cadena coincident amb el patró si l'ha trobat, sinó
  retorna null.
Metode replace(patró, subst) : Substitueix la cadena subst per la subcadena que coincideix
  amb el patró.
Expressions Regulars.

 
1.- Validació per comparació.
Dreamweaver incorpora una opció a "Ventana/Comportamientos/+/Validar formularios" que ofereix opcions de validació de dades de formularis. Les opcions són limitades i ho fa per comparació amb la incorporació de la funció genèrica MM_validateForm().

En aquest apartat farem una funció de nom valida(), que servirà per fer unes validacions per comparació, similars a les que introdueix el Dreamweaver, però, fetes per nosaltres. Així les entendrem millor i ens serà més fàcil adaptar-les a qualsevol altra situació.

Per fer l'anàlisi del text a validar és necessari treballar amb cadenes. Per això, ens seran molt útils els mètodes i propietats de l'objecte String. S'ha de tenir en compte que els caràcters que formen una cadena es numeren progressivament començant amb el índex 0 per al primer caràcter. Al principi d'aquesta pràctica hi ha una descripció dels mètodes que utilitzarem.

La següent funció eliminaBlancsExtrems() és una funció auxiliar que elimina els espais en blanc al principi i al final de la cadena que se li passa com a paràmetre i retorna la mateixa cadena sense els espais en blanc.

De vegades, sol passar que l'usuari introdueix de forma involuntària espais en blanc al principi i/o al final del text. Aquests espais en blanc solen ser difícils de localitzar ja que no es veuen, sobretot si estan al final. Podem ajudar a evitar aquests tipus d'errors implementant la funció que elimina aquests espais en blanc.

La línia 3 comprova si el primer caràcter (charAt(0)) és igual a un espai en blanc (' '). Si ho és s'extreu la subcadena situada entre la posició 1 i el final de la cadena (cadena.length) i l'assigna un altre cop a la variable cadena. Aquest procés es repeteix tantes vegades com calgui (while) mentre el primer caràcter de la cadena contingui un espai en blanc.

La línia 4 fa el mateix que la 3, però, referent als espais en blanc que hi pugui haver al final de la cadena. Per això comprova si hi ha un espai en blanc al final de la cadena tot comprovant la posició charAt(cadena.length-1). Recordem que length retorna la longitud de la cadena en nombre de caràcters, i que la cadena comença a comptar els caràcters amb el 0, per tant l'última posició tindrà l'índex ...length -1. Això ho repeteix mentre hi hagi espais en blanc al final (while).

Un cop s'han eliminat els espais en blanc al principi i al final, es retorna el valor resultant (línia 5). Aquesta funció s'invocarà des de dins la funció valida().

1 function eliminaBlancsExtrems(cadena)
2 {
3   while (cadena.charAt(0)==' ') cadena = cadena.substring(1,cadena.length);
4   while (cadena.charAt(cadena.length-1)==' ')
                                             cadena = cadena.substring(0,cadena.length-1);
5   return cadena;
6 }

 
A continuació, iniciarem la creació comentada de la funció de nom valida(), que serà l'encarregada de validar els diferents camps del formulari indicat al final d'aquest apartat. Repetirem el procés de validació per a cadascun dels quatre camps que conté el formulari. Si és correcte la variable resCampX valdrà true, sinó valdrà false. Mireu abans l'últim bloc de codi d'aquest apartat on hi ha el codi HTML amb els camps dins el formulari que volem validar.

El camp no. 1 és un camp que ha de contenir dades numèriques obligatòriament. A la línia 9 assignem a valCamp1 el contingut del quadre de text camp1, eliminant-ne prèviament els possibles espais en blanc que s'hagin introduït al principi i al final amb la funció eliminaBlancsExtrems().

A la línia 9 assignem per defecte el valor true a la variable resCamp1 i a la línia 11 verifiquem si el camp 1 és buit o conté un valor no numèric amb isNaN(). Si és així, assigna a resCamp1 el valor false.

7   function valida()
8   {
9       var valCamp1=eliminaBlancsExtrems(document.form1.camp1.value);
10      var resCamp1=true;
11      if (valCamp1 =='' || isNaN(valCamp1)) resCamp1=false;
 
Les línies de codi següents corresponen a la validació del camp no. 2. Aquest Camp no. 2, també és obligatori i ha de contenir un valor numèric comprés entre -4 i 8. A les línies 12 i 13, assignem a valCamp2 el valor de camp2 sense espais en blanc extrems i a resCamp2 el valor true, respectivament.

A la línia 14 és la que fa la validació pròpiament dita, i consisteix en efectuar comparacions, si valCamp2 és una cadena buida, o és un valor no numèric o és inferior a -4 o és superior a 8, s'assigna a resCamp2 el valor false.

12   var valCamp2=eliminaBlancsExtrems(document.form1.camp2.value);
13   var resCamp2=true;
14   if (valCamp2 =='' || isNaN(valCamp2) || valCamp2<-4 || valCamp2>8)
                                                                                          resCamp2=false;
 
A continuació s'efectua la validació de l'adreça electrònica sol·licitada com a obligatòria en el Camp no. 3. La majoria de validacions d'una adreça de correu solament verifiquen l'existència del símbol @ dins el text. Així és com ho fa el Dreamweaver. El codi següent efectua una validació més detallada.

Primer hem de tenir clar quina és l'estructura mínima d'una adreça e-mail, l'estructura general és: x@y.dom on x potser un o més caràcters alfabètics o numèrics, majúscules o minúscules, o un punt . o una ratlla baixa _ . y potser un o més caràcters alfabètics minúscules o numèrics i pot incloure el "." . dom és un dels dominis de primer nivell existent, per exemple: .es, .com, .fr, .org, .net, etc.

Lògicament, aquest codi solament verifica que l'estructura de l'e-mail sigui correcta, el que no pot fer és comprovar si aquest e-mail existeix realment.

La línia 15 llegeix el valor del camp de text del formulari i l'assigna a valCamp3, havent eliminat prèviament els espais en blanc inicials i finals si hi fossin. Les línies 16 i 17 assignen el valor inicial a resCamp3 i verifiquen que l'adreça no sigui buida, respectivament.

A continuació de la línia 17, es fan múltiples comprovacions. Si alguna d'elles no és correcta, s'assignarà el valor false a resCamp3.

La línia 18, s'assegura que dins la cadena no hi hagi cap espai en blanc. La línia 19 cerca la posició d'un punt començant des del final i assigna aquesta posició a la variable posUltimPunt. A la línia 20, es verifica que aquest últim punt existeix.

Sabem que des de la posició de l'últim punt fins el final de la cadena s'hi ha de trobar el nom del domini de primer nivell (.es, .org, .com, ... ). Per tant, a la línia 21 assignem a la variable domini el contingut que es troba des de la posició de l'últim punt fins al final de la cadena, fent una extracció amb substring entre la posició de l'últim punt (posUltimPunt) i el final de la cadena determinat amb la propietat length.

La línia 22 verifica que el contingut de la variable domini correspongui amb algun dels dominis existents. En aquest exemple solament hem posat 5 dominis. Si desitgem ampliar-ho a tots els altres coneguts, s'hauran d'afegir dins la condició del if amb el següent format: ...&&domini!='.dom'.

La línia 23 assigna a longDomini el valor de la longitud de domini, a la 24 determinem la posició del símbol @ i a la 25 comprovem que no existeixi més d'un símbol @ fent una nova cerca d'aquest símbol des del final de la cadena. Si solament existeix una @ les dues cerques de posició ens donaran el mateix resultat.

A la línia 26 verifiquem que la posició de @ no estigui situada com a primer caràcter ni tampoc com a últim caràcter tenint en compte la longitud del domini.

Si la validació de l'adreça e-mail passa correctament totes aquestes proves, resCamp3 valdrà true, sinó valdrà false.

15   var valCamp3=eliminaBlancsExtrems(document.form1.camp3.value);
16   var resCamp3=true;
17   if (valCamp3 == '') resCamp3=false;
18   if (valCamp3.indexOf(' ')!=-1) resCamp3=false;
19   var posUltimPunt = valCamp3.lastIndexOf('.');
20   if (posUltimPunt==-1) resCamp3=false;
21   var domini=valCamp3.substring(posUltimPunt,valCamp3.length);
22   if (domini!='.es' && domini!='.com' && domini!='.net' && domini!='.org' &&
                                                                          domini!='.fr') resCamp3=false;
23   var longDomini=domini.length;
24   var posArroba=valCamp3.indexOf('@');
25   if (posArroba!=valCamp3.lastIndexOf('@')) resCamp3=false;
26   if (posArroba<=0 || posArroba>=valCamp3.length-longDomini-1) resCamp3=false;
 
Les línies següents corresponent a la validació del camp 4. Aquest camp solament pot tenir tres possibles valors, en majúscules o minúscules, o deixar-lo en blanc.

Com que és igual que estigui en majúscules o en minúscules, abans de fer les comparacions, hem de convertir tots els caràcters de la cadena a minúscules amb la funció toLowerCase() de la línia 29. També podríem passar-ho tot a majúscules amb toUpperCase(), en aquest cas, les comparacions de la línia 30 s'haurien de fer amb el text en majúscules.

La línia 30 és on es fa la validació. És compara amb els tres valors possibles o bé amb una cadena buida, si és que és en blanc. Si no es compleix la condició resCamp4 valdrà false, de cas contrari, valdrà true.

27   var valCamp4=eliminaBlancsExtrems(document.form1.camp4.value);
28   var resCamp4=true;
29   valCamp4=valCamp4.toLowerCase();
30   if (valCamp4!='verd' && valCamp4!='blau' && valCamp4!='blanc' && valCamp4!='')
                                                                                                      resCamp4=false;

 
En aquest punt de la funció valida() ja tenim quatre variables resCampX amb el valor true o false, segons hagi estat el resultat de la validació. Ara queda advertir a l'usuari dels possibles errors, si hi són.

A les línies 31 i 32, si totes les variables resCampX valen true, assignem a la variable noError el valor true de cas contrari, assignem false. És a dir, noError val false si hi ha alguna validació incorrecta.

La línia 33 conté un alert que mostra el resultat a l'usuari. Per a cada camp, mostrem "OK!" o" Error" mitjançant l'operador ternari ? : , i un text indicador del resultat final avaluant la variable noError.

La línia 34 retorna el resultat global (indicat per noError) de la validació a l'event onSubmit que ha invocat a la funció valida(). Si es retorna true, s'efectuarà l'acció d'enviament de les dades, en canvi, si retorna false, quedarà anul·lada l'acció d'enviament de les dades i ens quedarà el formulari pendent per modificar els errors que hi hagi.

31   if (resCamp1 && resCamp2 && resCamp3 && resCamp4) var noError=true;
32   else var noError=false;
33   alert('Camp 1 : ' + ((resCamp1)?'OK!':'Error!') + '\n' +
              'Camp 2 : ' + ((resCamp2)?'OK!':'Error!') + '\n' +
              'Camp 3 : ' + ((resCamp3)?'OK!':'Error!') + '\n' +
              'Camp 4 : ' + ((resCamp4)?'OK!':'Error!') + '\n\n' +
              ((noError)?'Tot correcte, les dades s\'enviaran ara.'
              :'Tens errors, verifica\'ls abans d\'enviar.'));

34   return noError;
35   }
 

Finalment, a continuació, tenim el codi HTML on està inclòs el formulari d'exemple i les dues funcions descrites prèviament.

Com a element destacable podem observar com s'invoca l'event Submit mitjançant el controlador d'events onSubmit, dins la línia d'inici del formulari. Aquest event s'invocarà quan fem clic a sobre del botó "Enviar".

L'event Submit invoca a la funció valida() de la següent forma: return valida(); . Això fa que el resultat de la validació (true o false) es passi a l'acció d'enviament del formulari, de tal manera que si el valor retornat és true, s'efectuarà l'acció d'enviament, en canvi, si és false, no s'efectuarà cap enviament.

<html>
<head>
<title>Exemple</title>

<script language='JavaScript'>
     
... Introduïm les línies 1 - 35 descrites prèviament ...
</script>

</head>

<body bgcolor='#BAE8E7'>
 
<form name='form1' method='post' action='mailto:usuari@servidor.dom'
                                                                    onSubmit='return valida();'>
      Introdueix un valor numèric:<br>
      # 1: <input type='text' name='camp1'> (Obligatori.)<p>
      Introdueix un valor numèric entre -4 i 8:<br>
      # 2: <input type='text' name='camp2'> (Obligatori.)<p>
      Introdueix un e-mail:<br>
      # 3: <input type='text' name='camp3'> (Obligatori.)<p>
      Introdueix 'verd', 'blau' o 'blanc' (majúscules o minúscules):<br>
      # 4: <input type='text' name='camp4'> (Es pot deixar en blanc.)<p>
      <input type='submit' value='Enviar'> <input type='reset' value='Esborrar'>
  </form>
</body>
</html>




 
2.- Validació mitjançant comparacions amb "Expressions Regulars".
A l'apartat anterior, el codi es complica excessivament quan les comparacions a efectuar són molt variades. En aquest apartat introduirem el concepte de Expressions Regulars que ens permetran fer patrons de validació més complexos de forma més senzilla. Aplicarem aquests conceptes per substituir part del codi JavaScript descrit a l'apartat anterior.

Per entendre ràpidament el que són les Expressions Regulars recordarem els 'comodins' * i ? utilitzats a l'MS-DOS i Windows per seleccionar múltiples fitxers. Podríem dir que les Expressions Regulars són una evolució molt més avançada d'aquells rudimentaris 'comodins'.

El tema de les Expressions Regulars és molt extens, disposa de moltes opcions i la seva aplicació és comuna a altres llenguatges de programació diferents del JavaScript, com ara, el Perl, etc. En aquest apartat no tractarem aquest tema amb extensió. Solament descriurem aquells aspectes introductoris necessaris per aplicar-ho al codi de l'apartat anterior.

Podeu buscar més informació sobre el tema a Internet, llibres especialitzats o, per començar, veure l'ajuda de Dreamweaver (Ayuda/Referencia/JavaScript.... cercant la paraula RegExp).

Una Expressió Regular és un patró per a cadenes de caràcters. Tenim dues formes de crear els patrons corresponents a les Expressions Regulars. Mitjançant l'ús de l'objecte RegExp o bé declarant el patró com una expressió literal situada entre dues barres / (/patró/). Aquí utilitzarem la segona forma.

Un cop tenim creat el patró, el podem utilitzar en els mètodes exec() i test() de l'objecte RegExp o els mètodes match(), replace(), search() i split() de l'objecte String. Els principals caràcters especials per generar patrons són:

      \ Indica que el següent caràcter és especial i no s'ha de considerar un literal.
      ^ Permet trobar coincidències que comencen al principi de la cadena.
      $ Permet trobar coincidències que comencen al final de la cadena.
      * Permet trobar coincidències dels caràcters que el precedeixen 0 o més vegades.
      + Permet trobar una o més coincidències dels caràcters que el precedeixen.
      ? Permet trobar 0 o una coincidència dels caràcters que el precedeixen.
      . Localitza qualsevol caràcter sempre que no estigui al principi de la cadena.
      a|b Localitza qualsevol dels patrons (a o b).
      [...] Localitza qualsevol dels caràcters situats dins els [ ].
      \d Localitza dígits dins la cadena.

Disposem també dels modificadors següents:
      g Aplica el patró de forma global a tota la cadena, no solament a la primera coincidència.
      i Ignora si els caràcters són majúscula o minúscula.

Els modificadors s'introdueixen al final de la Expressió Regular, després de la / final.

Els següents exemples de patrons d'expressions regulars ens seran útils per aplicar-los en el codi de l'apartat anterior:

    /[-+]?\d*\.?\d*/
Localitza números decimals.
[-+]? - Indica 0 o 1 caràcter + o - .
\d* - Indica 0 o més dígits numèrics.
\.? - Indica 0 o 1 punt.
\d* - Indica 0 o més dígits numèrics.

    /[A-Za-z0-9._]+@[a-z0-9.]+(.es|.com|.net|.org|.fr)/g
Localitza cadenes amb el format adequat per ser considerades una adreça e-mail.
[A-Za-z0-9._]+ - Indica 1 o més caràcters entre A i Z, a i z, 0 i 9 o un punt o un guió baix.
@ - Indica el símbol literal @.
[a-z0-9.]+ - Indica 1 o més caràcters entre a i z, 0 i 9 o un punt.
(.es|.com|.net|.org|.fr) - Indica una de les cadenes de text separades amb | , si es desitja augmentar el nombre de dominis vàlids s'ha de fer afegint-los aquí.

    /verd|blau|blanc|/gi
Localitza cadenes amb els continguts verd o blau o blanc o buit, en majúscules o minúscules. Fixeu-vos com després de la paraula blanc hem afegit un símbol | seguit de la finalització del patró /, aquesta part és la que permet seleccionar els camps buits. També s'ha inclòs el modificador g per indicar tota la cadena i per indicar majúscules i minúscules.

    /^ +| +$/g
Localitza espais en blanc al principi o al final d'una cadena.
^ + - Cerca espais en blanc a l'inici de la cadena.
  +$ - Cerca espais en blanc al final de la cadena.

Utilitzarem també els següents mètodes:
  - Mètode match(patró) : Retorna la cadena coincident amb el patró si l'ha trobat, sinó retorna
    null.
  - Mètode replace(patró, subst) : Substitueix la cadena subst per la subcadena que coincideix
    amb el patró.


En el codi següent apliquem les Expressions Regulars al programa de validació vist a l'apartat anterior.

La línia que substitueix a les línies 3, 4 i 5 s'encarrega de substituir els espais en blanc del principi i final de la cadena. Per això cerca el patró d'un o més espais en blanc des del principi (^ +) o un o més espais en blanc des del final ( +$) i els substitueix per una cadena buida ' '. El modificador g indica que es faci per a tota la cadena.

La part de codi valCamp1 != valCamp1.match(/[+-]?\d*\.?\d*/) que substitueix a la línia 11efectua una comparació entre el contingut de la variable valCamp1 i el valor retornat per match() a l'aplicar el patró /[+-]?\d*\.?\d*/, si no coincideixen, vol dir que el contingut del camp1 no és un número.

A la línia 14 es fa una acció similar a la indicada en el paràgraf anterior.

El codi valCamp3.match(/[A-Za-z0-9._]+@[a-z0-9.]+(.es|.com|.net|.org|.fr)/g) de la línia que substitueix a les línies 17-26 comprova el contingut de la variable valCamp3. Per això, s'extreu una cadena de la variable valCamp3 segons el patró indicat com a paràmetre de match(). Si aquesta cadena no coincideix amb el valor que conté la variable valCamp3, vol dir que l'adreça e-mail no s'ajusta al patró.

El codi valCamp4!=valCamp4.match(/verd|blau|blanc|/gi) de la línia que substitueix a les línies 29 i 30 comprova si el valor retornat per match() segons el patró indicat, és diferent del contingut de valCamp4. Si és diferent vol dir que el contingut de valCamp4 no s'ajusta als requeriments sol·licitats pel camp4.

Observem que la utilització de les expressions regulars redueix el codi necessari per fer validacions per comparació, sobretot si aquest codi de comparació és extens com en el cas de la validació de l'adreça e-mail.

En el cas concret de la validació de l'adreça e-mail, la utilització de les expressions regulars ens permet ajustar-ho amb més precisió. Si en l'apartat anterior, s'acceptava qualsevol caràcter per formar l'e-mail en els texts previs i posteriors a l'@, en aquests cas, aquests caràcters són determinats dins els [ ] i es pot evitar la introducció de caràcters estranys.




Substituïm les següents línies pel codi indicat a continuació:

la 3, 4 i 5 per:
  return cadena=cadena.replace(/^ +| +$/g,'');

la 11 per:
  if (valCamp1 =='' || valCamp1 != valCamp1.match(/[+-]?\d*\.?\d*/)) resCamp1=false;

la 14 per:
  if (valCamp2 =='' || valCamp2!=valCamp2.match(/[+-]?\d*\.?\d*/) || valCamp2<-4 ||
                                                                                    valCamp2>8) resCamp2=false;

de la 17 fins la 26 per:
  if(valCamp3 != valCamp3.match(/[A-Za-z0-9._]+@[a-z0-9.]+(.es|.com|.net|.org|.fr)/g))                                                                                                                 resCamp3=false;

la 29 i 30 per:
  if (valCamp4!=valCamp4.match(/verd|blau|blanc|/gi)) resCamp4=false;