Újrahasznosítható Ajax-osztálykönyvtár fejlesztése

Az eddigi Ajax-példakódok az egyszerűség kedvéért nem voltak böngészőfüggetlenek. Ebben a fejezetben egy saját Ajax-osztálykönyvtárat fogunk megírni, amelynek célja az Ajax-kommunikáció leegyszerűsítése. Olyan osztálykönyvtárat fogunk tervezni és megírni, amely egy gyártófüggvényt fog megvalósítani a kérelemobjektum böngészőfüggetlen létrehozására. Továbbá azonosítjuk és egyszerűsítjük az Ajax-programozással járó ismétlődő és unalmas eljárásokat. Az Ajax-osztálykönyvtárunk remélhetőleg megkíméli majd a fejlesztőket attól, hogy a bonyolult részletekkel kelljen foglalkozniuk.

A kérelemobjektum létrehozása

Kezdjük a kérelemobjektum létrehozásával. Az Internet Explorer 7-es verziója és az összes többi böngésző (Mozilla, Opera, Safari,...) natív módon támogatja az XMLHttpRequest-objektumot. Ezekben a böngészőkben elég példányosítanunk az objektumot.


var oXHR = null;
if (typeof XMLHttpRequest != "undefined"){
    oXHR = new XMLHttpRequest();
}

A fenti kód a typeof operátor segítségével ellenőrzi, hogy az XMLHttpRequest osztály definiálva van-e. Ha az osztály nem létezik, az if utasítás else ágában Internet Explorer 6 és 5-ös verziói számára írt kód következik. A kód feltételes megjegyzéssel (About Conditional Comments) van elrejtve más böngészők elől.


var oXHR = null;
if (typeof XMLHttpRequest != "undefined"){
    // Mozilla, Netscape, Opera, Safari, Internet Explorer 7 böngészők
}else{

/*@cc_on
  @if (@_jscript_version >= 5)
      // Internet Explorer 5.0 és későbbi verziók
@end @*/

}

A kérelemobjektum az Internet Explorer 6 és korábbi verzióiban ActiveX objektum formájában létezik. ActiveX-objektumok JScript-ben történő létrehozására az ActiveXObject osztály szolgál. Konstruktora egy karakterláncot fogad el, amely a létrehozandó ActiveX objektum azonosítóját (nevét és verzióját) tartalmazza.


oXHR = new ActiveXObject("Microsoft.XMLHTTP");

A kód létrehozza a kérelemobjektum első verzióját, amely az Internet Explorer 5.0-ban jelent meg. Ez az XHR-objektum az Internet Explorer 5.0 és későbbi verzióiban elérhető, mégse használjuk, ha a kliens böngészője támogatja az objektum újabb verzióit is. Az új verziók stabilabbak és gyorsabbak a korábbiaknál.

Amennyiben lehetséges, használjuk a 6.0-s verziót (Microsoft XML Team's WebLog - Using the right version of MSXML in Internet Explorer). Korábbi verziók használata nem ajánlott különböző biztonsági és a nem szabványos implementációkból felléphető problémák miatt. Az 5.0-ás verziót (MSXML 5.0 for Microsoft Office Applications) ne használjuk, mert azt a Microsoft kizárólag az Office programcsomaghoz szánta. Sajnos egyetlen módon határozhatjuk meg, hogy a kérelemobjektum melyik verzióját támogatja a kliens böngészője: sorra létre kell hoznunk az objektumokat try{}catch{} blokkban a legfrissebbtől kezdve. Ha az objektum nem létezik, hibaüzenet keletkezik, amit elkapunk a catch blokkban, majd folytatjuk a következővel.


var oXHR = null;
var MSXML_PROGIDS = ['Msxml2.XMLHTTP.6.0',
                     'Msxml2.XMLHTTP.4.0',
                     'Msxml2.XMLHTTP.3.0',
                     'Msxml2.XMLHTTP'
                     'Microsoft.XMLHTTP'];

for (var i=0; i < MSXML_PROGIDS.length; i++){
    try {
        oXHR = new ActiveXObject(MSXML_PROGIDS[i]);
        break;
    } catch (e) {
    }
}

Az MSXML ActiveX osztálykönyvtár eredetileg az IE 4.0-hoz készült Active-csatornák elemzésére. Az MSXML-t egészen 2001-ig csak IE-val együtt lehetett használni, de ekkor a Microsoft kiadta az MSXML 3.0-t, amely egy különálló csomag volt. Még ugyanebben az évben egy 4.0-s verziót is kiadtak, és ekkor átnevezték az MSXML-t Microsoft XML Core Services Componentre. A Microsoft XML Core Services (MSXML) lehetővé teszi a JScript, Visual Basic Scripting Edition (VBScript) és Microsoft Visual Studio 6.0 alkalmazásokat használó felhasználóknak, hogy XML-alapú alkalmazásokat hozhassanak létre, amelyek képesek együttműködni az XML 1.0 szabványt használó egyéb alkalmazásokkal. A Microsoft XML Core Services külön is letölthető Microsoft Core XML Services (MSXML) 6.0 Service Pack 1. Az MSDN weboldalán MSDN - MSXML bővebben olvashat az MSXML-ről.

Az MSXML_PROGIDS tömb az érvényes verziósztringeket tartalmazza. A tömb első eleme a legújabb verzió azonosítóját tartalmazza. A ciklus feldolgozza a tömb elemeit, és mindegyik verziósztringgel megpróbál példányosítani egy kérelemobjektumot. Az első sikeres próbálkozásnál a ciklus befejeződik.

A már meglévő kódból készítsünk egy függvényt a kérelemobjektum böngészőfüggetlen létrehozására. Mivel olyan osztálykönyvtárat írunk, amit mások is használni fognak, használjunk névteret. Így nem szennyezzük a globális névteret függvényeinkkel, változóinkkal. Az Ajax-osztálykönyvtárunk összes változóját és függvényét egyetlen objektum fogja tartalmazni, amelynek az Ajax nevet választottam.

Részlet a ajax/ajax-osztaly/ajax.js fájlból.

var Ajax = { // névtér objektum
    _MSXML_PROGIDS:['Msxml2.XMLHTTP.6.0',
                    'Msxml2.XMLHTTP.4.0',
                    'Msxml2.XMLHTTP.3.0',
                    'Msxml2.XMLHTTP',
                    'Microsoft.XMLHTTP'],

    CreateXhrObject:function(){
        var oXHR = null;

        if (typeof XMLHttpRequest != "undefined"){
            oXHR = new XMLHttpRequest();
        }else{

/*@cc_on
  @if (@_jscript_version >= 5)


            for (var i=0; i < this._MSXML_PROGIDS.length; i++){
                try {
                    oXHR = new ActiveXObject(this._MSXML_PROGIDS[i]); 
                    this._MSXML_PROGIDS = [this._MSXML_PROGIDS[i]];
                    break;
                } catch (e) {
                    oXHR = null;
                }
            }
@end @*/

            if (!oXHR && window.createRequest) { // IceBrowser 
                try {
                    oXHR = window.createRequest();
                } catch (e) {
                    oXHR = null;
                }
            }

        }

        return oXHR;
    }
}

A CreateXhrObject() metódus visszatérési értéke egy XHR-objektum lesz, vagy null érték, ha a böngésző nem támogatja az XHR-objektumot. A függvényben néhány javítást eszközöltem. Közvetlenül a megfelelő kérelemobjektum példányosítása után, átírom a verziósztringeket tároló tömböt, hogy az csak egyetlen elemet tartalmazzon, így következő alkalommal már nem kell feleslegesen példányosítani a nem létező XHR-osztályokat.


oXHR = new ActiveXObject(this._MSXML_PROGIDS[i]);
this._MSXML_PROGIDS = [this._MSXML_PROGIDS[i]];
break;

Ezzel a kód futási teljesítménye biztosan javul valamelyest.
A függvényt továbbá bővítettem egy rövid kóddal, ami akkor van végrehajtva, ha az XHR-objektumot nem sikerült létrehozni. Ez a kódrész megpróbálja létrehozni a kérelemobjektumot, ha a böngésző IceBrowser ICEbrowser. Az IceBrowser egy ritka böngésző, ami az XHR-objektum létrehozását a window.createRequest() utasításal támogatja Dojo Toolkit.

A részletek elrejtése saját XHR-objektummal

Szerencsére az XHR-objektumot az egyes böngészők gyártói eltérések nélkül valósították, meg, így miután példányosítottuk a kérelemobjektumot az előbbiekben megírt gyártófüggvénnyel (Ajax.CreateXhrObject()), nem kell többé böngésző típusokat detektálnunk és külön kódrészeket írni az egyes böngészők részére.

Az Ajax-osztálykönyvtárunk azonban még nem készült el. Az osztálykönyvtárunknak le kell egyszerűsítenie az Ajax-kommunikációt. A fejlesztőknek az XHR-objektum használatához sok felesleges részletet kell ismerniük, valamint sok az ismétlődő eljárás, ami egyszerűsíthető. Az XHR-objektumot nem tudjuk megváltoztatni, de tervezhetünk egy saját XHR-objektumot a saját szánk íze szerint. Saját XHR-objektumunk olyan interfésszel fog rendelkezni, ami a kliens számára elrejti a felesleges információkat. Objektumunk felhasználóinak csak azt kell majd tudniuk, hogy az mit tud, a megvalósítás hogyanja az objektum „műhelytitka” marad. Saját XHR-objektumunk tartalmazni fog egy „igazi” XHR-objektumot, aminek majd delegálni fogja a feladatokat. Az osztálykönyvtár teljes forráskódja olvasható a 13. számú mellékletben, vagy a CD mellékleten.

Az Ajax-osztálykönyvtárunk osztálydiagramja

Lássunk néhány példát az elkészült kérelemobjektumunk használatára (további példák).


var callback = {
    success:function(o){alert('Válasz: '+ o.responseText)},
    failure:function(o){alert('Hiba:'+ o.statusText)}
}; 

var oXHR = new Ajax.Request();
oXHR.Call('server.php',callback);

A fenti példakód egy GET-kérést intéz a kiszolgálóhoz aszinkron módon. Az osztály példányosítása után a Call() metódussal küldjük el a kérést. A Call() metódus az átadott paraméterek és az alapértelmezett beállítások (alapértelmezésben a kérés típusa GET, a kérés módja pedig aszinkron) alapján inicializálja az XHR-objektumot. Ha az XHR-objektum nem támogatott, a metódus null értékkel tér vissza. Ezt mi nem ellenőriztük a fenti példában, mert azzal számolunk, hogy ezt az ellenőrzést már megtettük korábban (például az Ajax.isSupported() metódussal). A Call() metódus első paramétere a kérés URL-jét tartalmazza. A második paraméter egy paraméter objektum, amely tartalmazza a kérésre adott válasz lekezeléséhez szükséges metódusokat. A Call() metódus utolsó opcionális paramétere a szerverre elküldendő adatokat tartalmazza. Mivel GET-kérésről van szó, a harmadik paramétert nem adtuk meg. Ha a kérés végrehajtása sikeres volt, a paraméter objektum success tulajdonságában definiált eseménykezelő lesz meghívva. Ellenkező esetben a failure tulajdonságban definiált eseménykezelő lesz meghívva. Mindkét eseménykezelő az XHR-objektumot kapja bemenetként.

POST-kérést hasonló módon küldhetünk, csak át kell állítani a method tulajdonság értékét "POST"-ra.


var callback = {
    success:function(o){alert('Válasz: '+ o.responseText)},
    failure:function(o){alert('Hiba:'+ o.statusText)}
}; 

var sPostData = "message=Hello%20World&user=John%20Doe";
var oXHR = new Ajax.Request();
oXHR.method = 'POST';
oXHR.Call('server.php',callback,sPostData );

Ügyeljünk arra, hogy az elküldendő adatokat URL-kódolva kell átadni a Call() metódusnak.

A Call() metódusnak átadandó paraméter objektum rendelkezhet még egy scope (hatókör) nevű tulajdonsággal is. Egy helyesen megtervezett objektumorientált kódban gyakran előfordul, hogy az XHR-kérés eseménykezelői egy objektum metódusai. Amennyiben ezek a metódusok a this kulcsszón keresztül hivatkoznak az objektum tulajdonságaira, hiba lépne fel, a hatókör elvesztése miatt. A paraméter objektum scope tulajdonsága ezt a problémát orvosolja. A scope tulajdonság azt mondja „futtasd le a success és a failure eseménykezelő függvényeket úgy, mint ennek az objektumnak a metódusait”.

var objTest = {
    msgSuccess : 'Success :-)',
    failure    : 'Failed !',

    "processRequest" : function (o){
        alert(this.msgSuccess);
    },

    "handleError" : function(o){
        alert(this.msgFailure +"\n Reason:"+ o.statusText);
    }
};

var callback = {
    'success' : objTest.processRequest,
    'failure' : objTest.handleError,
    'scope'   : objTest
};

var oXHR = new Ajax.Request();
oXHR.Call('server.php',callback);

A példakódokon jól látható, hogy az osztálykönyvtár használata mennyire leegyszerűsítette az Ajax-programozást, javult a kód olvashatósága.

Végezetül következzen egy rövid összefoglaló arról, hogy az Ajax-osztálykönyvtárnak mennyire sikerült leegyszerűsítenie az Ajax-kommunikációt.