ugrás a tartalomhoz

jQuery form plugin kibővítve

LaySoft · 2010. Júl. 13. (K), 09.34
jQuery form plugin kibővítve

Webes munkáim során sok adminfelületet csinálok, ahol rengeteg űrlap is készül, és ellenőrizni kell a kitöltött adatokat. Ezt az ellenőrzést szerveroldalon mindenféleképpen meg kell tennünk. Kliens oldalon ugyan nem kötelező, de manapság alapvető elvárás. Ha meg akarjuk spórolni a kliensoldali ellenőrzést, a szerver oldali kontroll után úgysem ússzuk meg a visszajelzést, hogy valami adat nincs rendben. Ha pedig ellenőrzünk kliens oldalon is, akkor ugyanazt a logikát kell két nyelven kiviteleznünk; ha egy újabb mező kerül az űrlapba, akkor két helyen kell a kódot módosítani.

A kezdeti ajax nélküli időkben lefutott a submit, ellenőriztük az adatokat, és ha valami hibádzott, akkor újra kitettük az űrlapot, a kitöltött értékeket pedig visszaírtuk például a value attribútumokba, és mindemellett valahogy jeleztük is, hogy hol nem jó az adat. Ez nem ördöngösség, de ha valaki csinált már ilyet, akkor tudja milyen nehézkes. Arról nem is beszélve, hogy mondjuk jelszavakat nem túl szép dolog value értékként visszaírni formba. Ha továbblépünk, és egy kicsit próbálunk felhasználóbarátak lenni, akkor a submit előtt JavaScripttel ellenőrizzük az adatokat. Ilyenkor – hiszen nem tűnik el, amit kitöltöttünk – megspóroljuk a visszaírásukat. Persze ekkor sem kerülhetjük meg a szerver oldali ellenőrzést, mert kliens oldalon akármit művelhet a felhasználó, vagyis megint két helyen, két nyelven kell ugyanazt az ellenőrző logikát alkalmaznunk.

Ha egy űrlap helyes elküldése után nem akarjuk az egész oldalt újratölteni, hanem csak egy részébe szeretnénk valamit kirakni, akkor ajaxot alkalmazhatunk. Alapesetben ilyenkor nekünk kell „kézzel” összeszedni a form elemeit, összepakolni egy XMLHttpRequestbe, elküldeni a szerver felé, majd várni a választ, és ettől függően folytatni az eseményeket. Nagyban megkönnyíti munkánkat a jQuery form plugin, ami leveszi vállunkról az űrlap elemek összegyűjtögetésének terhét, és szerver oldalon úgy jelennek meg a változóink, mintha egy „hagyományos” submit történt volna. De ettől még mindig kell ellenőrizni kliens- és szerver oldalon egyaránt.

Illusztráció
Fotó: Török Gábor

A jQuery form pluginra épülő, pár függvényből álló kis rendszerem – amit most be szeretnék mutatni – ezt a kettősséget igyekszik kiküszöbölni, egyszerűvé téve az űrlapok ellenőrzését, ezáltal nagy mértékben felgyorsítva a munkát. A lényege az, hogy az ellenőrzést csak a kötelező szerver oldalon végezzük el, de mind hiba esetén a visszajelzés, illetve hibátlan kitöltés esetén a további esemény szabadon választhatóan lehet egy HTML tartalom betöltődése vagy JavaScript kód végrehajtása.

Az egyszerűség szemléltetése érdekében nézzünk egy alap példát, a megvalósításról majd később, elsőre direkt nem mutatom meg magukat a függvényeket, csak a használatukat:

<?php

include 'functions.php';

if (isset($_POST['reg'])) {
    $p = '!^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,6})$!';
    
    if (empty($_POST['email'])) { // Ki van töltve?
        JS_Response("alert('Adja meg az e-mail címét!'); $('#email').focus()");
    } elseif (!preg_match($p, $_POST['email'])) { // Helyes?
        JS_Response("alert('Helytelen e-mail cím!'); $('#email').focus()");
    } else { // Mehet
        // Adatbázisba írás stb...
        HTML_Response('<p>Sikeres regisztráció: ' . $_POST['email'] . '</p>');
    }
}

header('Content-Type: text/html; charset=utf-8');

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>jqForm</title>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
		<script type="text/javascript" src="http://github.com/malsup/form/raw/master/jquery.form.js"></script>
		<script type="text/javascript" src="js.php"></script>
	</head>
	<body>
		<form method="post" action="<?php= $_SERVER['PHP_SELF'] ?>">
			<p><input type="text" name="email" id="email"/></p>
			<p><?php ElKuld('Regisztráció', 'reg', "$('#valasz')") ?></p>
		</form>
		<div id="valasz"></div>
		<div><? highlight_file(__FILE__) ?></div>
	</body>
</html>

Az űrlap actionje önmagára mutat, a submit gomb az ElKuld() PHP függvénnyel lesz kirakva, ebben a példában csak három paraméterét használjuk. Amint látható, csak egy helyen, az elmaradhatatlan szerver oldalon ellenőrzünk, mégis JavaScript alert() a visszajelzés, ha valami adat nem helyes. Ha pedig minden rendben, akkor egy HTML tartalmat töltünk be az ElKuld() harmadik paraméterében egy jQuery selector által meghatározott konténerbe. Ezzel a módszerrel megspóroljuk a kliens oldali JavaScript ellenőrzést, de nem nagy kár érte, mert egy rossz indulatú felhasználó kénye-kedvére módosíthatja a JavaScript kódot, ezért úgyis csak a szerver oldali ellenőrzés a mérvadó, annak eredményét vehetjük biztosra. Ugyanakkor nem vesznek el az űrlapba írt adatok, nem kell őket value értékekben újra kiírnunk. Ha egy új űrlap elemet hozunk létre, akkor csak a szerver oldalon kell megcsinálni a rá vonatkozó ellenőrzést.

Szerver oldal

Ezek után nézzük a megvalósítást. A PHP függvények a http://bogex.hu/jqform/functions.php?s címen találhatók.

Az ElKuld() függvény hozza létre a gombokat, a fenti példánál ez az eredmény:


  <input type="button" value="Regisztráció" onclick="ElKuld(this,'reg',$('#ide'),false)" class="button"/>

A függvény paraméterei: void ElKüld ( string $szoveg, mixed $akcio [, string $target = FALSE [, string $validate = FALSE [, bool $del = FALSE [, bool $submit = FALSE ]]]] )

Az első paraméter lesz a gomb szövege.

A második lehet szöveg vagy tömb típus is. Szöveg típus esetén ez felel meg egy hagyományos űrlap submit elem nevének, vagyis a gombra kattintva szerver oldalon megjelenik. Akkor használunk tömböt, ha nem csak a gomb nevére van szükségünk, hanem értéket is szeretnénk átadni vele, vagy ezen felül több paramétert is el kell küldenünk.

Következő a target, ez egy jQuery selector, ahova a HTML válasz tartalmát akarjuk tenni. Noha ez meg van adva, még nem kötelező HTML választ generálnunk, ez a fenti példában is jól látható. Azonban ha HTML válasz is várható, akkor ezt a paramétert mindenféleképpen meg kell adnunk.

A validate paraméterrel az egész űrlap küldés folyamatát vétózhatjuk meg, ezt a jQuery form plugin beforeSubmit() callback függvényével oldom meg. Erre általában törlő gomboknál van szükség, ahol rákérdezek, hogy a látogató biztos-e a dolgában. Ha nemet válaszol, akkor nem kerül elküldésre az űrlap. Emellett még felhasználható a form elemek kliens oldali bonyolultabb ellenőrzésére is.

A del paraméter csak egy egyszerű stílus módosító. Ha megadjuk, akkor a gomb CSS osztálya nem button, hanem delete lesz. Csak a megjelenést változtatja, a működést nem befolyásolja.

Az utolsó submit paraméter egyrészt stílus módosító, megadásával submit lesz a stílusosztály. De van egy sokkal fontosabb, a működést alapvetően befolyásoló szerepe is. Ha azt szeretnénk, hogy a form ne csak a gombra kattintva legyen elküldve, hanem Enter megnyomására is, akkor ezt kell használnunk. További szerepe akkor lesz, ha több ElKuld gombunk is van egy űrlapon, és meg szeretnénk adni, hogy Enter megnyomására a sok közül melyik hajtódjon végre. Például ha adott egy Új tételt felvesz gomb, és mellette sok törlés gomb, akkor nyilvánvaló, hogy az új tétel felvételét szeretnénk az Enterre végrehajtatni, a törléseket pedig csak kattintásra.

Ez a függvény tehát button ill. submit input elemeket hoz létre. A button elemnél az onClick eseményre hajtódik végre az ElKuld() JavasSript függvény, a submit elemnél kicsit bonyolultabb a helyzet. Itt az űrlap onSubmit eseményére lesz beállítva az ElKuld(), ezzel elérjük, hogy Enterre és kattintásra egyaránt lefusson.

Kliens oldal

Eddig csak előállítottuk a HTML kódunkat, nézzük akkor ez egész lelkét, amitől működik: a JavasSript ElKuld() függvényt.

Az első paramétere a this objektum, ami maga a gomb, ahol történt a submit. Ebből a jQuery .closest() metódusával megállapítjuk, hogy melyik űrlapról van szó. Ezáltal egy oldalon használhatunk több formot is, akár egyező input nevekkel, így is fogja tudni a függvény, hogy éppen melyik űrlappal kell foglalkoznia.

A második paramétere az akcio, ami ha a PHP ElKuld() függvényében szintén akcio paraméter string, akkor ez is az lesz, ha tömb akkor pedig json_encode()-dal átalakított objektum lesz. Ebből a paraméterből készítjük el a jQuery form plugin ajaxSubmit() metódusának data paraméterét, amellyel plusz adatokat adhatunk meg. Ezek hozzá lesznek csapva a form elemeihez.

A harmadik paraméter a target, amely szintén a PHP ElKuld() függvény azonos nevű paraméterének felel meg. Ez határozza meg a konténert, ahova a HTML válasz tartalma kerül.

A negyedik paraméter a validate, ezzel vétózhatjuk meg a form elküldését. Ha meg van adva, a form elküldése előtt végrehajtja ezt a JavaScript kódot. Ha ez a kód hamis értékkel tér vissza, akkor nem lesz elküldve a form.

Ezután jön a form elküldése és várjuk rá a választ. Ha z űrlapon nincs fájl feltöltő mező, akkor ajaxos XMLHttpRequesttel történik a küldés, és a jQuery 1.4-es verziójától kezdve a form plugin ajaxSubmit() metódusának success callback függvénye visszaadja az XHR objektumot is. Ezáltal lehet kulturáltan megállapítani, hogy milyen típusú (Content-Type) válasz érkezett a szerver felől. (Azért írtam, hogy „kulturáltan”, mert a rendszert használtam már jQuery 1.3.2 alatt is, de ott ronda jelző byte-tal tudtam csak megoldani ezt.) Ha HTML válasz jött, akkor betesszük a target paraméter által meghatározott konténerbe. Ha JavaScript érkezett, akkor semmit sem kell tennünk, mert a jQuery végre is hajtotta a kódot, nem kell eval.

Ha az elküldött űrlapban fájl feltöltés is volt, akkor kezd bonyolódni a helyzet. Mivel az XMLHttpRequest nem tud file uploadot, ezért a jQuery – hasonlóan más keretrendszerekhez – ilyenkor „hagyományos” submitot hajt végre egy iframe-be irányítva. Ilyenkor értelemszerűen nem áll rendelkezésre a callback függvényben az XHR objektum, ezért valahogy másképp kell megállapítani a válasz típusát. Erre a célra a böngészők azon tulajdonságát használjuk fel, hogy ha egy iframe-be text/javascript Content-Type fejléccel érkezik tartalom, akkor automatikusan beágyazzák egy <pre></pre> elembe, és a speciális karakterek HTML entitással jelennek meg. A Firefox natúr <pre> elemet használ, a Chrome, Safari és OWB paraméterezi, az Opera pedig <PRE>-t használ, de nem zárja le (ez utóbbit nem értem milyen meggondolásból teszi). A legnagyobb baj persze megint az IE-el van, az ugyanis nem is veszi a választ, valami (vélt) biztonsági kockázatra hivatkozva. Akárhogy próbálgattam a biztonsági beállításokat, nem sikerült elérnem hogy eljusson a callback függvényig. Ha admin felületen használjuk a rendszert, és rá tudjuk venni a felhasználókat, hogy ne használjanak IE-t, akkor nincs gond, ellenkező esetben kerüljük a fájl feltöltés utáni JS választ. A tartalom kinyerését a <pre> elemből és a HTML entitások dekódolását egyetlen .text() metódussal érjük el, persze Opera esetében először le kell zárnunk a választ egy </pre> elemmel, mert ő ezt elfelejti.

Ha file upload után HTML válasz jön, akkor sem túl könnyű a dolgunk, mert ha a tartalom nem egy magában is érvényes HTML kód (pl. <p>Paragraph</p>), hanem csak egy töredék (pl. <tr><td>1111</td><td>2222</td></tr>), akkor a böngésző kigyalulja belőle a HTML elemeket és csak 11112222 marad belőle. Ezért ilyenkor a választ <textarea></textarea> elembe kell ágyazni, ezt végzi el a HTML_Response PHP függvény, ha a második paraméterét igazra állítjuk. Ezenkívül a htmlspecialchars() függvényt is végrehajtjuk az adaton, Chrome, Safari, OWB (KHTML) böngésző esetében kétszer kell ezt megtennünk, hogy a végén ki tudjuk nyerni a <textarea> értékéből az eredeti tartalmat. Ez a <textarea> elembe ágyazás nem az én ötletem, a form plugin is így működik file upload esetén, ahol szintén hasonló célra használja, de mi sajnos ezt nem tudjuk hasznosítani, mert ott a submit előtt kell megadni, hogy milyen típusú lesz a válasz, nálunk meg a submit elküldése után szerver oldalon derül ki.

Működés közben

Egy bonyolultabb példán megpróbálom szemléltetni a fontosabb lehetőségeket.

A példa egy táblázathoz ad hozzá ill. belőle vesz el sorokat, ehhez a sorok.php file-t hívogatja. Láthatjuk, hogy itt a törlésnél felhasználjuk az ElKuld() PHP függvény negyedik validate paraméterét. Ide megadhatjuk egy függvény nevét is, amivel ellenőrzést végezhetünk az űrlapon, vagy – mint ebben az esetben is – csak egy jóváhagyó kérdést teszünk fel. Ha hamis értékkel térünk vissza, akkor nem lesz elküldve az űrlap. A file upload mezőt nem kötelező kitölteni, mert az utóbbi néhány verzió óta a form plugin akkor is iframe submitot csinál, ha üres a file upload input. Amíg ez nem így volt, addig még azzal is foglalkozni kellett, hogy volt-e feltöltve valóban file, ennek függvényében ágyazni <textarea> elembe a választ, vagy sem.

A Felvesz gomb submit paramétere igazra van állítva, ezért Enter megnyomására is el lesz küldve a form, nem csak a gombra klikkeléskor. A válaszban láthatjuk, hogy a HTML_Response() függvénynek most nem stringet adunk át, hanem egy file nevet, amit be fog tölteni, valamint a második paramétere igazra állításával jelezzük, hogy ez egy file upload utáni válasz, elérve ezzel hogy ágyazza <textarea> elembe.

A JS gomb csak azt mutatja be, hogy a JavaScript válaszban lehet hibás ill. le nem zárt HTML elemeket is küldeni. (Ez nem megy IE-ben.)

Az Érték 1 és Érték 2 gomb azt demonstrálja, hogy lehet több egyező nevű gomb is, különböző értékekkel. Ez egy „hagyományos” oldalon annak felel meg, amikor több submit gombunk van ugyanazzal a névvel, és mindig csak annak az értéke megy át elküldéskor, amelyiket megnyomtuk. Itt azonban még azt is megválaszthatjuk, hogy az egyik gomb megnyomására HTML választ adunk, míg a másikra JavaScriptet.

Ezen kívül még van sok egyéb variációja is a paramétereknek, azt hiszem a legfőbb irányokat bemutattam, a kódok letölthetők, szabadon felhasználhatóak, akit érdekel, kísérletezhet. Az egyetlen probléma, amire nem találtam megoldást, az az IE-ben a fájl feltöltés utáni JavaScript válasz. Remélem ennek ellenére sokaknak tanulságos volt az írásom és szeretettel várom a kritikákat, észrevételeket, továbbfejlesztési ötleteket, esetleg kód „szépítő” javaslatokat.

 
LaySoft arcképe
LaySoft
A múlt évezredtől foglalkozik webfejlesztéssel, a jelenlegi óta PHP-val. Ős C64/Amiga rajongó, a Newcomer című játék programozója. A kezdetektől Amigán szerkeszti a PHP szkripteket, ettől jól érzi magát. PHP és JavaScript érdekli, tetszenek neki a trükkök, újdonságok. Nem bonyolítja túl a dolgokat, szereti az egyszerű, letisztult megvalósításokat. Ez áll a zenei ízlésére is.
1

Newcomer

_subi_ · 2010. Júl. 13. (K), 21.35
off:

Engedd meg, hogy gratuláljak a Newcomer-hez! Édesapám egyik kedvenc játéka volt C64-en, és én magam is játszottam vele egy keveset. Kíváncsi lennék, hogy lenne-e érdeklődés ma hasonlóra, mondjuk webes környezetben.

Bevallom, kicsit irigyellek, programozási szempontból szerintem az volt a hőskorszak (illetve pár évvel előtte). Szerintem érdekesebb lehetet összehozni, mint egy száraz webalkalmazást.
2

Newcomer live!

LaySoft · 2010. Júl. 14. (Sze), 08.58
Köszönöm szépen!

A Newcomer egy régi történet, 1991-re nyúlik vissza. Gondolom örömödre szolgál a hír, hogy a project még mindig él, bár én már nem vagyok benne. Hamarosan megújul a weboldal is, de ezzel sem én foglalkozom. A webarchive megőrizte a régi oldalakat, ott lehet csemegézni.

Társaloghatunk még róla, csak ne itt offoljuk szét a témát.