V dnešním díle se naučíme validovat formuláře pomocí Ajaxu.
Validace formuláře pomocí Ajaxu
V knize Ajax - vytváříme vysoce interaktivní webové aplikace nám autor předkládá odpověď na otázku, kdy začít s webovými aplikacemi v Ajaxu. Doporučuje začít u kontroly vstupu uživatele ve formuláři a to si dnes také předvedeme.
Použitím Ajaxu u věcí typu ověření vstupu uživatele odstraníme nutnost opětovného načtení stránky při neúspěšné validaci na straně serveru a nebudeme také nuceni použít primitivní JavaScriptové funkce, které nám nikdy nezaručí 100% výsledek. Stále hrozí, že uživatel bude mít JavaScript vypnutý. Nyní se určitě ptáte, v čem je tedy ověření dat pomocí technologie Ajax tak světoborné. Pokud má klient vypnutý JavaScript, neměl by mu fungovat ani Ajax. Ano, je to sice pravdivé tvrzení, ale Ajax nám umožňuje provést kontrolu dat na straně serveru pomocí stejného scriptu, který můžeme použít k validaci dat i po odeslání formuláře. V případě, že má uživatel zapnutý JavaScript, se kontrola bude provádět asynchronně na straně serveru. Pokud uživatel má JavaScript vypnutý, kontrola se provede pouze po odeslání na straně serveru, v případě neúspěchu se uživatel nevyhne znovu načtení stránky. Pro demonstraci celého procesu slouží následující ilustrace.
V následující části se zaměříme na samotnou kontrolu dat Ajaxem. Pomocí objektu Just in time na straně serveru.
Tvorba kódu
Formulář, který chceme vytvořit, bude obsahovat tyto položky: přezdívku, jméno, příjmení a e-mail. Naše registrace bude používat tři JavaScriptové funkce:
- vytvorXHR() - pro nás již známá funkce pro vytvoření objektu XMLHttpRequest
- vytvorPozadavek() - funkce pro vytvoření požadavku objektu XMLHttpRequest s několika malými změnami
- validace() - funkce, která pomocí XML zajistí komunikaci se scriptem na straně serveru
Kromě těchto tří funkcí využijeme také pole, které pojmenujeme cache. Toto pole nám bude sloužit jako fronta. V případě, že bude objekt XHR zaneprázdněný, uložíme požadavek do fronty a počkáme, až bude mít XHR na tyto požadavky čas.
var cache = new Array();
Jako první si vytvoříme HTML soubor s formulářem pro registraci. Stránka by mohla vypadat nějak takto:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="cs">
<head>
<meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
<meta http-equiv="content-language" content="cs" />
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache, must-revalidate" />
<meta http-equiv="expires" content="0" />
<meta name="generator" content="PSPad editor, www.pspad.com" />
<meta name="author" content="all: Petr 'plasmo' Láslo; mailto: plasmo@plasmo.cz; web: http://plasmo.cz" />
<script src="js.js" type="text/javascript"></script>
<title>Registrace uživatele</title>
</head>
<body onload="document.getElementById('nick').focus()">
<fieldset>
<legend>Registrace uživatele</legend>
<form action="#" method="post">
<table border="0">
<tr>
<td><label for="nick">Nick:</label></td>
<td><input type="text" name="nick" id="nick" value="" onblur="vytvorPozadavek(this.id,this.value)" /></td>
<td><div id="nickZprava"></div></td>
</tr>
<tr>
<td><label for="jmeno">Jméno:</label></td>
<td><input type="text" name="jmeno" id="jmeno" onblur="vytvorPozadavek(this.id, this.value)" /></td>
<td><div id="jmenoZprava"></div></td>
</tr>
<tr>
<td><label for="prijmeni">Přijmení:</label></td>
<td><input type="text" name="prijmeni" id="prijmeni" onblur="vytvorPozadavek(this.id, this.value)" /></td>
<td><div id="prijmeniZprava"></div></td>
</tr>
<tr>
<td><label for="email">E-mail:</label></td>
<td><input type="text" name="email" id="email" onblur="vytvorPozadavek(this.id, this.value)" /></td>
<td><div id="emailZprava"></div></td>
</tr>
</table>
</form>
</fieldset>
</body>
</html>
Tvorba HTML souboru musí být všem jasná. Upozorním jen na pár drobností.
- Prohlížeči musíme zakázat ukládání dat do cache:
- Ukazatel nastavíme na první položku formuláře:
- K odpálení požadavku použijeme událost onblur:
...
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache, must-revalidate" />
<meta http-equiv="expires" content="0" />
...
...
<body onload="document.getElementById('nick').focus()">
...
...
... onblur="vytvorPozadavek(this.id, this.value)" />
...
Další soubor, který vytvoříme, bude obsahovat funkce JavaScriptu. Opět standartně pojmenovaný - js.js
- Jako první vytvoříme proměnné XHR a cache. Jak jsem se učili již na začátku kurzu, v proměnné xhr bude uložen odkaz na objekt XHR a proměnná cache bude obsahovat frontu požadavků.
- Druhá funkce, kterou již nebudu ani popisovat (naučili jsme se ji tvořit v 1. lekci kurzu), je vytvorXHR():
- Další funkce, kterou budeme tvořit, je vytvorPozadavek(). Funkci oproti předchozím dílům trochu upravíme. Bude přebírat dva parametry - id prvku a hodnotu prvku. První změnou je rozšíření o podmínku, která kontroluje existenci prvku id. Pokud se kontrola vyhodnotí jako pravda, přidá požadavek do fronty. Další drobnou změnou je přidání podmínky do části try, díky které vytvoříme požadavek jen v případě, že XHR nemá zrovna nic na práci a zároveň jen tehdy, když fronta není prázdná. Další změnou je použití metody xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); objektu XHR pro nastavení MIME typů. Poslední malou změnou je předání cache jako parametru metody xhr.send(cacheVstup);. Funkce bude vypadat následovně:
- Třetí a poslední funkcí, kterou vytvoříme, je funkce validace(). Tato funkce je velmi podobná té z minulého dílu. Opět přečte dokument XML a zobrazí jej na webové stránce. Nyní vám ukáži, jak celá funkce vypadá, a následně Vás upozorním na odlišnost od funkce ctiOdpoved() z minulého dílu. Zde máte slíbenou funkci:
- Odlišnost, které si můžete všimnout, je podmínka, která vypisuje HTML kód na obrazovku. Pokud údaj neprojde validací, bude XML uzel stav obsahovat hodnotu 0. V tomto případě zobrazíme chybovou zprávu. V opačných případech nevypíšeme nic.
- Poslední soubor, který vytvoříme, je validace.php. Jeho obsah bude totožný s podobnými soubory z minulých dílů. Jediné, co nám přibude, jsou hlavičky, které zamezí ukládání dat do cache, a logika pro ověření zadaných údajů. Kontrola probíhá následujícím způsobem: nejdřív dle id prvku rozhodneme, který prvek budeme vůbec kontrolovat (zda nick, jméno, přijmení atd.). Pomocí podmínky if/else přiřadíme hodnoty proměnným $hodnota2 a $zprava2.
var xhr = vytvorXHR();
var cache = new Array();
function vytvorXHR(){
var xhr;
try{
xhr = new XMLHttpRequest();
}catch(e){//pro případ starší verze prohlížeče
var MSXmlVerze = new Array('MSXML2.XML.Http.6.0','MSXML2.XML.Http.5.0','MSXML2.XML.Http.4.0','MSXML2.XML.Http.3.0','MSXML2.XML.Http.2.0','Microsoft.XML.Http');
for(var i = 0; i < MSXmlVerze.lenght; i ++){
try{
xhr = new ActiveXObject(MSXmlVerze[i]);
}catch(e){
//vzniklou chybu ignoruji a pokračuji nastavením další verze
}
}
}
if(!xhr)
alert("Došlo k chybě při vytváření objektu XMLHttpRequest!");
else
return xhr;
}
function vytvorPozadavek(id,hodnota){
if(xhr){
if(id){
//zakoduji hodnoty
id = encodeURIComponent(id);
hodnota = encodeURIComponent(hodnota);
//vložím hodnoty do fronty
cache.push("id="+id+"&hodnota="+hodnota);
}
try{
//pokračovat budu jen v případě že cache není prázdná a objekt
//XHR nemá co na práci
if((xhr.readyState == 4 || xhr.readyState == 0)&& cache.length > 0){
//z cache načtu další hodnotu
var cacheVstup = cache.shift();
xhr.open("POST","http://programujte.com/page/200808251506_validace.php",true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.onreadystatechange = validace;
xhr.send(cacheVstup);
}
}catch(e){
alert("Nelze se připojik k serveru:\n" + e.toString());
}
}else{
alert("Funkce \"precitSoubor()\": chybí objekt XMLHttpRequest");
}
}
function validace(){
if(xhr.readyState == 4){
if(xhr.status == 200){
var XMLRes = xhr.responseXML;
//zachycení chyb IE a Opery
if(!XMLRes || !XMLRes.documentElement){
throw("Chybná struktura XML:\n"+xhr.responseText);
}
//zachycení chyb ohnivé lišky :-)
var rootNodeName = XMLRes.documentElement.nodeName;
if(rootNodeName == "parsereerror"){
throw("Chybná struktura XML:\n"+xhr.responseText);
}
//čtu dokument, jelikož je vše ok :-)
xmlRoot = XMLRes.documentElement;
odpoved = xmlRoot.getElementsByTagName("odpoved")[0].firstChild.data;
stav = xmlRoot.getElementsByTagName("stav")[0].firstChild.data;
id = xmlRoot.getElementsByTagName("id")[0].firstChild.data;
zprava = document.getElementById(id+"Zprava");
//alert(odpoved+" "+id+" "+zprava);
if(odpoved == 0){
zprava.innerHTML = stav;
}else{
zprava.innerHTML = "";
}
setTimeout("vytvorPozadavek();", 500);
}else{
alert("Požadavek HTTP není v pořádku.")
}
}
}
if(odpoved == 0){
zprava.innerHTML = stav;
}else{
zprava.innerHTML = "";
}
<?php
header("Expires: Wed, 23 Dec 1980 00:30:00 GMT");
header("Last-Modified:".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
switch ($_REQUEST['id']) {
case "nick":
//pokud nick obsahuje hodnotu vracím 1 == true jinak 0 == false
if($_REQUEST['hodnota']){
$hodnota2 = 1;
$zprava2 = " ";
}else{
$hodnota2 = 0;
$zprava2 = "Musíte zadat nick!";
}
break;
case "jmeno":
if(empty($_REQUEST['hodnota'])){
$hodnota2 = 0;
$zprava2 = "Musíte zadat jméno!";
}else{
$hodnota2 = 1;
$zprava2 = " ";
}
break;
case "prijmeni":
if(empty($_REQUEST['hodnota'])){
$hodnota2 = 0;
$zprava2 = "Musíte zadat přijmení!";
}else{
$hodnota2 = 1;
$zprava2 = " ";
}
break;
case "email":
if(!eregi("^[a-z0-9_.-]+@([a-z0-9_-]+\.)+[a-z]{2,4}$",$_REQUEST['hodnota'])){
$hodnota2 = 0;
$zprava2 = "E-mailová adresa musí být v platném tvaru!<br /> Příklad: pepa.nova@seznam.cz,pepa@seznam.cz,pepa-novak@seznam.cz<br />";
}else{
$hodnota2 = 1;
$zprava2 = " ";
}
break;
}
//výstup je XML dokument, proto odešlu správný mime-type
header("Content-Type: text/xml");
//tvořím nový XML
$xml = new DOMDocument("1.0","utf-8");
//vytvočím kořenový element zamesntanci
$validace = $xml->createElement("validace");
$xml->appendChild($validace);
$vysledek = $xml->createElement("vysledek");
//vytvorim element odpoved a vložím do něj data
$odpoved = $xml->createElement("odpoved");
$odpovedData = $xml->createTextNode($hodnota2);
$odpoved->appendChild($odpovedData);
//vytvorim element zprava a vložím do něj data
$stav = $xml->createElement("stav");
$stavData = $xml->createTextNode($zprava2);
$stav->appendChild($stavData);
//vytvorim element id a vložím do něj data
$id = $xml->createElement("id");
$idData = $xml->createTextNode($_REQUEST['id']);
$id->appendChild($idData);
$vysledek->appendChild($odpoved);
$vysledek->appendChild($stav);
$vysledek->appendChild($id);
$validace->appendChild($vysledek);
//uložím XML výstup
$vystup= $xml->saveXML();
//zobrazím
echo $vystup;
?>
Závěr
Dnes jsme vytvořili druhou opravdu Ajaxovou webovou aplikaci. V dalším díle se můžete těšit na tvorbu okna s nápovědou.
Úkol
Pokuste se vylepšit naši Ajaxovou validaci tak, aby se v případě, kdy má uživatel vypnutý JavaScript, provedla na straně serveru.