Gebruikerslogin

Nu online

Er zijn momenteel 1 gebruiker en 13 gasten online.

Enquête

Wat is leuker?
White hat SEO
70%
Black hat SEO
30%
Totaal aantal stemmen: 98

AJAX, een object georiënteerde xml parser

Een paar dagen geleden schreef Erik-Jan al een artikel over Ajax voor beginners. Hierin werden de principes van Ajax (Asynchronous JavaScript And XML) uitgelegd en werd een eenvoudig voorbeeld gegeven om er lekker mee aan de slag te kunnen. In het kort komt het er op neer dat je JavaScript kunt gebruiken om een server side script (bijv. php of asp) aan te roepen, waardoor je zonder de pagina te herladen data uit bijvoorbeeld een database kunt ophalen.

Dit artikel is hier een vervolg op. We gebruiken hier een uitgebreidere XML parser en leggen uit waarom dit zo is. In het kort komt de uitbreiding hier op neer:

  1. Deze XML parser is object georiënteerd.
  2. Deze XML parser geeft ook daadwerkelijk XML terug, in plaats van de data in het server side script zelf weer te geven. De 'x' van Ajax wordt nu dus ook gebruikt.

Object georiënteerd

Zoals ik al zei in punt 1 was het verschil tussen deze XML parser en die uit het eerste artikel dat deze object georiënteerd gebouwd is. Klinkt mooi, maar waarom dan?

Wat je ziet bij de vorige XML parser is dat het xmlHttpRequest in een globale variabele gestopt werd. Dat werkt op zich prima, totdat je meerdere requests tegelijk wilt uitvoeren. Dat is immers (ook) de kracht van Ajax ook, de 'a' van asynchronous. Wanneer gaat het dan fout?

Stel je hebt een request uitgevoerd en de behandeling van dit request duurt 2 seconden. Nu wil je binnen die 2 seconden nog een volgend request uitvoeren. Dan zal je hiervoor weer de globale variabele moeten gebruiken. Maar het gaat fout zodra het tweede request met de verwerking bezig is en het eerste 'terugkomt' (readystate == 4). Dan is namelijk het eerste request overschreven door het tweede en ben je dus je data kwijt. Onhandig dus. Nu kun je wel twintig verschillende globale variabelen aan gaan maken, maar dat lijkt me ook niet echt de bedoeling.

De oplossing? Maak de parser object georiënteerd. Elk request krijgt zijn eigen object, waardoor er geen data meer door elkaar gaat lopen en verloren gaat. Ik ga hier niet vertellen hoe het hele object georiënteerd programmeren an sich werkt, maar ga er maar vanuit dat het werkt ;-). Als je echt graag wilt weten hoe het werkt, dan spit je of de code door, of je zoekt een mooi artikel over object georiënteerd programmeren in JavaScript. Zo, daar kom ik even makkelijk van af. :-)

Data als XML

Er was nog een tweede reden waarom ik over deze nieuwe XML parser begon. Juist, de XML zelf. Het server side script dat je aanroept zal in plaats van data (opgemaakt door HTML) naar het scherm spugen een mooi XML document genereren. Deze XML halen we vervolgens met JavaScript op en we kunnen ermee doen wat we willen.

De code

Genoeg gebazeld, nu de code maar eens even. Omdat het iets te lang wordt om in dit artikel te plaatsen moet je hem maar even downloaden. De code zal ik niet regel voor regel uitspitten, maar ik zal even de belangrijkste functies aan bod laten komen.

Initialisatie

In regel 9 t/m 16 worden een aantal variabelen gedeclareerd die we later nodig hebben. Vervolgens wordt de init() functie aangeroepen. Deze functie wordt meteen uitgevoerd zodra je het object aanmaakt. Hierin zou je bijvoorbeeld een 'loading' plaatje kunnen laten tonen. Aan het eind van het verhaal, als het request klaar is wordt de _closeLoading functie aangeroepen waarmee je dit 'loading' plaatje weer kunt laten verdwijnen.

Settings

Vervolgens hebben we een aantal functies die de instellingen van het huidige object bepalen. Ik zal ze even kort langslopen:

  • setResponseAsText(): je kan ervoor kiezen om in plaats van XML ook gewoon text als data terug te krijgen. In dat geval moet je deze functie aanroepen.
  • setDisabledASync(): normaal gesproken wil je graag asynchrone requests uitvoeren. Dat wil zeggen dat het ene request niet op het andere hoeft te wachten en je dus lekker snel kunt werken. Mocht je dat niet willen, dan kun je dit dus uitzetten.
  • setFunction(): aan de setFunction() functie kun je (komma gescheiden) een set van functies meegeven die moeten worden uitgevoerd, nadat het request klaar is. Hierin kun je dus functies zetten die de XML verwerken en de data in HTML naar het scherm spugen.
  • setURL(url): hiermee geef je de URL mee van het server side script wat moet worden aangeroepen.
  • addParam(param, value): met deze functie stel je de parameters in voor de URL die je aanroept. Dus in plaats van achter de URL (na het vraagteken) alle parameters te plakken (dat mag wel, maar hoeft dus niet), kun je deze functie zo vaak aanroepen als je wilt om al je parameters mee te geven. Wel zo overzichtelijk.

Een voorbeeld van het aanmaken van zo.n object kan er als volgt uitzien:

function getZoekresultaten(zoekwoorden, boek) {
var xmlhttp = new HTTPDataConnection();
xmlhttp.setURL("resultaten.php");
xmlhttp.addParam("zoekwoorden", zoekwoorden.value);
xmlhttp.addParam("boek", boek.value);
xmlhttp.setFunction(verwerkInput);
xmlhttp.requestData();
}

Het request

Dan de belangrijkste functie: requestData(). In deze functie wordt het daadwerkelijke request uitgevoerd. Bovenin de functie worden nog even de ingestelde parameters achter de URL geplakt en vervolgens wordt het xmlHTTPRequest geopend en wordt de boel in werking gezet. Aan het eind wordt de handleData() functie aangeroepen waar de data wordt verwerkt.

Data verwerking

De handleData() functie haalt de data op nadat het request klaar is. Dit weet je doordat de readystate variabele de waarde '4' heeft. De (XML) data die we terugkrijgen van het server side script wordt in de this.receive variabele gestopt.

Een voorbeeld van een XML document vindt je hieronder:

<data>
<object>
<tekst><![CDATA[In den beginne]]></tekst>
<boek><![CDATA[Genesis]]></boek>
<hoofdstuk><![CDATA[1]]></hoofdstuk>
<vers><![CDATA[1]]></vers>
</object>
<object>
<tekst><![CDATA[Zo had genoemd de namen van al het vee]]></tekst>
<boek><![CDATA[Genesis]]></boek>
<hoofdstuk><![CDATA[2]]></hoofdstuk>
<vers><![CDATA[20]]></vers>
</object>
</data>

Vervolgens worden in de handleData() functie de door jou opgegeven functies (via de setFunction() functie) aangeroepen. Tot slot krijgt de _closeLoading() functie een schop waarmee je eventuele loading plaatjes weer weg kunt stoppen.

Weergave van de data

Wil je data die je in je XML terug hebt gekregen op je scherm tonen, dan kan dat in een van de functies die je aanroept na het request. In onderstaand voorbeeld heet deze functie verwerkInput(). Het commentaar zegt genoeg denk/hoop ik.

function verwerkInput(xmlhttp) {
// haal data op als array, met XML element 'object' als parent
var dataArray = xmlhttp.getDataAsArray("object");

// haal aantal resultaten op uit XML element 'count'.
var count = xmlhttp.getDataValue("count");

if(count != 0) {
if(count > 25) {
output ="<p><em>Er zijn meer dan 25 resultaten gevonden. Verfijn uw zoekopdracht.</em></p>\n";
}
else {
output ="<p><em>Er zijn " + count + " resultaten gevonden.</em></p>\n";
}

output += "<table>\n";
for(var object in dataArray) {
if(typeof(dataArray[object]) == "object") {
tekst = dataArray[object]["tekst"];
boek = dataArray[object]["boek"];
hoofdstuk = dataArray[object]["hoofdstuk"];
vers = dataArray[object]["vers"];
output += "<tr>";
output += "<td style=\"width:150px;\" valign=\"top\"><strong>" + boek + " " + hoofdstuk + ":" + vers + "</strong></td>";
output += "<td>" + tekst + "</td>";
output += "</tr>";
}
}
output += "</table>\n";

document.getElementById('output').innerHTML = output;
}
}

Het einde

Tot zover deze object georiënteerde XML parser. Ik hoop dat je het een beetje hebt kunnen volgen. Zo niet, schroom dan vooral niet om in het forum een vraag te stellen.

Zijn we dan nu klaar met Ajax? Nee, nog niet helemaal. Binnenkort komt er nog een toepassing van dit artikel. In deze toepassing gaan we een zoekmachine maken die m.b.v. Ajax de data ophaalt en weergeeft.

Noot: Dit artikel werd u mede mogelijk gemaakt door Ronald Everts.

remy
Aantal berichten: 4

hallo

Klopt het dat er wat fouten instaan? Ik ben zelf de hele avond bezig geweest om het om te bouwen naar een POST versie.

function getZoekresultaten(zoekwoorden, boek) {
var xmlhttp = new HTTPDataConnection();
xmlhttp.setURL("resultaten.php");
xmlhttp.addParam("zoekwoorden", zoekwoorden.value);
xmlhttp.addParam("boek", boek.value);
xmlhttp.setFunction(verwerkInput);
xmlhttp.requestData();
}

Volgens mij moet in dit stukje code de 'verwerkInput' schreven worden als 'verwerkInput(xmlhttp)'
Nu werkt het namenlijk wel, maar heb niet zoveel verstand van js om te kunnen beoordelen of dat goed is.

Ook heb ik dit veranderd:

function verwerkInput(xmlhttp) {
// haal data op als array, met XML element 'object' als parent
var dataArray = xmlhttp.getDataAsArray("object");
enz..

IN:

function verwerkInput(xmlhttp) {
// haal data op als array, met XML element 'object' als parent
var dataArray = function() {xmlhttp.getDataAsArray("object");}
enz..

Anders werkte het niet.

Wel heb ik nu het probleem dat ik steeds Undifined terugkrijg ipv de xml waarden.
Ik werk namenlijk met JSON en heb zelf een functie toegevoegd welke alle data die verstuurd wordt rechtstreeks naar de html stuurt, maar die geen geen data, maar enkel 'undifined'.
Dat is dit stukje, stuurt gewoon this.received terug:
return this.receive;

Terwijl er wel echt data verstuurt wordt(wordt ook opgeslagen in de database), maar kan het retouneren in mijn javascript, waarschijnlijk omdat verwerkInput(), met daarmee mijn gemaakte functie xmlHttp.getDataAsRaw(), al uitgevoerdt wordt terwijl de readystate nog niet op 4 staat en dus de verwerking van het php gedeelte nog niet afgehandeld is.

function verwerkInput(xmlHttp)
{

result = function() {xmlHttp.getDataAsRaw();}
document.write(result);

Maargoed, nog ff een paar dagen prutsen en het gaat vast werken. ;)

Arjan
Avatar van Arjan
Aantal berichten: 532

Heej. welkom hier.. ik zal proberen op je vragen antwoord te geven :-)

Volgens mij moet in dit stukje code de 'verwerkInput' schreven worden als 'verwerkInput(xmlhttp)'

Nee dat hoeft niet per se. Je stopt namelijk de functie zelf in een variabele en niet de functie aanroep. Wat jij voorstelt zou wel kunnen, maar hiervoor zou de parser anders geschreven moeten worden. In iig wat in het voorbeeld staat is niet fout, maar gewoon een andere werkwijze.

Het tweede punt wat je verandert hebt: wat jij nu doet is een hele functie in een variabele stoppen, in plaats van de array die 'ik' opvraag. Dat is nogal een verschil, aangezien jij er een functie in stopt die in feite niets teruggeeft. Vanaar die undefined. Er moet dan nog een return waarde bij. Maar dat is onzinnig, aangezien het effect dan hetzelfde is als 'mijn' versie.

Het laatste gedeelte van je vraag over JSON en verder volg ik niet helemaal :-) Wat gaat daar precies mis? Of heeft dat te maken met je 2e vraag?

remy
Aantal berichten: 4

Nouja, kijk, mijn php pagina geeft een json string terug. Die krijg ik ook netjes te zien in Firebug, en ik wil dus eigenlijk gewoon die string weer terug hebben in mijn html pagina, dus niet via een functie met array's e.d.

Vandaar dat ik deze functie erbij had gemaakt ipv de getDataAs Array functie:

getDataAsRaw: function()
{

return this.receive;

}

Maar dit werkt dus niet, maar ik begrijp niet helemaal waarom niet. Ik heb het vermoeden dat wanneer deze functie aangeroepen wordt de data via ajax nog onderweg is.

Arjan
Avatar van Arjan
Aantal berichten: 532

Kun je misschien wat meer code plaatsen of desnoods de hele JS, want zo kan ik moeilijk checken wat er precies mis gaat.

Ik weet nl ook niet waar je die getDataAsRaw functie aanroept..

en als je geen data krijgt, moet je gewoon gaan debuggen met alerts ofzo.. gewoon kijken wanneer het nog goed gaat en wanneer het fout gaat.