Jump to content

[SOLVED] possible OOP scope issue? - AJAX wrapper


PC Nerd

Recommended Posts

Hi,

 

IVe written my own little AJAX wrapper to help streamline my AJAX pages etc.  Ive got an issue with it where my getData function only returns the data if i have previously gone: alert(this.information);  the information variable is an array that contains the data returned from the query.  It does contain data - the xml proccessing works fine.

 

here is my OOP code:

 

AJAX.prototype.getData = function() {

	//alert(this.information);
	this.statusobject = this.information;
	if(this.information) {
		return(this.information);
	}
	else {
		return "EMPTY";
	}

}

 

when I use the alert(this.information); - the getData does return it.... if i dont alert the data.. then it returns  "EMPTY".

 

the code that calls my object:

 

	query = new AJAX();
	query.setStatusAlerts(0, statusObject); // Allow script to update user with query status.
	query.runQuery('getLocations.php', 'Area_ID='+id, 'location')
	var Locations = query.getData();
	alert(Locations);

 

 

so:

 

when getData alert's this.information...... my end code variable "Locations" holds the array of data - correctly formatted etc.  If i dont alert(this.information); then it returns empty.... and Locations is "EMPTY".

 

Any suggestions.  as you can see... at the moment ive set my status element to the information returned from the query.  my status element usually contains text based on xmlObj.readyState ( ie "Data returned".)  so accessing the information once to place it into the element doesnt work.

 

Any ideas as to why this data seems only accessable after ive alert()'ed it?

 

Thanks

Link to comment
Share on other sites

Wihtout seeing all the code I can't be sure, but I think the problem is because you are not waiting for a status change.

 

With AJAX there will be a delay between the time that the call is made and when the response is available. That delay can be very quick (a fraction of a second) to very long (many seconds). However you are calling getData right after runQuery and not giving it any time to do the query and return the data. So, when you say "my getData function only returns the data if i have previously gone" it is most likely returning data from the previous call.

 

I just built an AJAX class for my own purposes (it's on my home PC so don't have access to it at the moment), but you will need to inject a delay to test for onreadystatechange. One option would be to modify the runQuery function to return the output once it is received and not use the getData.

 

Link to comment
Share on other sites

My full ajax wrapper:

 


function AJAX() {
var self = this;
var xmlProccessor;
var xmlObj;
var xmlProccessor;
var proccessTag;
var information;
try {
	self.xmlObj = new XMLHttpRequest();
}
catch(e) {
	try {
		self.xmlObj = new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch(e) {
		try {
			self.xmlObj = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch(e) {
			alert("AJAX is not enabled in your browser.  To complete this form you need to have full JavaScript capabilities enabled.  Please email webmaster@battleages.com for more information.");
			return false;
		}
	}
}
this.xmlObj.onreadystatechange = function()
	{
		if(self.statusalert == true) {
			if(self.xmlObj.readyState == 0) {
				self.statusobject.innerHTML = "AJAX";
			}
			else if(self.xmlObj.readyState == 2) {
				self.statusobject.innerHTML = "Request sent.";
			}
			else if(self.xmlObj.readyState == 3) {
				self.statusobject.innerHTML = "Receiving data.";
			}
			else if(self.xmlObj.readyState == 4) {
				self.statusobject.innerHTML = "Proccessing data.";
			}
		}
		if(self.xmlObj.readyState == 4) {
			self.xmlProccessor = new XML(self.xmlObj.responseXML);
			v = self.saveData(self.xmlProccessor.proccessOn(self.proccessTag));
			//alert("V: "+v);
			if(v != true) {
				alert("Could not save data");
			}
			else {
				//alert("Data Saved:\n\n "+self.information +"\n\n" + "Type: "+typeof self.information);
			}
		}
	}
}
AJAX.prototype.saveData = function(h) {
	this.information = h;
	if(this.information) {
		return true;
	}
	else {return false;}
}

AJAX.prototype.setStatusAlerts = function(a, b) {
	if(a == 1) {
		this.statusalert = true;
		this.statusobject = b;
	}
	else {this.statusalert = false;}
}

AJAX.prototype.runQuery = function(url, sendData, xmlTagName) {

	this.proccessTag = xmlTagName;
	this.xmlObj.open("POST", url+"?"+sendData);
	this.xmlObj.send(null);
	//xmlObj.send(sendData);
	//alert("Data: "+this.data);
}

AJAX.prototype.getData = function() {

	alert(this.information);
	if(this.information) {
		return(this.information);
	}
	else {
		return "EMPTY";
	}

}

 

now if i understand what your saying... is that either a) im not using onreadystatechange ( which i am).... and i can alert the direct output of the proccessOn() function ( which parses the data..... it displays correctly.... and save data displays correctly when i ask it to alert self.information.

 

 

is that what you meant?

Link to comment
Share on other sites

Sorry, but I am no following you last question. After looking at the full code, I think the problem is still as I stated (perhaps poorly) before.

 

When you call runQuery() that function makes the call to the server-side page. Then the very next line calls getData() to get the output of that request. The problem, I beleive, is that the time for the server call and response take more time than it takes before you run the getData() call. Let's say it take 1/10 of a second for the server call and response to complete. It probably take 1/100 of a second for the code to move from the runQuery() call to the getData() call. Thus, you are always getting the results of the last query - which is why the first one returns no data.

 

I made an attempt at a quick modification that might work (have not tested it). Here is the basic concept: 1) Whenever you call runQuery it will set the object property this.information to false. 2) Then when the saveData function is run it will set this.information to the AJAX output. 3) When you call getData it will loop until this.information is not false. This will give the AJAX process time to complete before getData tries to return the value. I added two lines (both with // ***** NEW LINE *****)I don't consider this an efficinet solution due to the loop. Using onreadystatechange to trigger the response is better, but it would take longer for me to rewrite for that.

 

function AJAX() {
var self = this;
var xmlProccessor;
var xmlObj;
var xmlProccessor;
var proccessTag;
var information;
try {
	self.xmlObj = new XMLHttpRequest();
}
catch(e) {
	try {
		self.xmlObj = new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch(e) {
		try {
			self.xmlObj = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch(e) {
			alert("AJAX is not enabled in your browser.  To complete this form you need to have full JavaScript capabilities enabled.  Please email webmaster@battleages.com for more information.");
			return false;
		}
	}
}
this.xmlObj.onreadystatechange = function()
	{
		if(self.statusalert == true) {
			if(self.xmlObj.readyState == 0) {
				self.statusobject.innerHTML = "AJAX";
			}
			else if(self.xmlObj.readyState == 2) {
				self.statusobject.innerHTML = "Request sent.";
			}
			else if(self.xmlObj.readyState == 3) {
				self.statusobject.innerHTML = "Receiving data.";
			}
			else if(self.xmlObj.readyState == 4) {
				self.statusobject.innerHTML = "Proccessing data.";
			}
		}
		if(self.xmlObj.readyState == 4) {
			self.xmlProccessor = new XML(self.xmlObj.responseXML);
			v = self.saveData(self.xmlProccessor.proccessOn(self.proccessTag));
			//alert("V: "+v);
			if(v != true) {
				alert("Could not save data");
			}
			else {
				//alert("Data Saved:\n\n "+self.information +"\n\n" + "Type: "+typeof self.information);
			}
		}
	}
}
AJAX.prototype.saveData = function(h) {
	this.information = h;
	if(this.information) {
		return true;
	}
	else {return false;}
}

AJAX.prototype.setStatusAlerts = function(a, b) {
	if(a == 1) {
		this.statusalert = true;
		this.statusobject = b;
	}
	else {this.statusalert = false;}
}

AJAX.prototype.runQuery = function(url, sendData, xmlTagName) {
	this.information = false // ***** NEW LINE *****
	this.proccessTag = xmlTagName;
	this.xmlObj.open("POST", url+"?"+sendData);
	this.xmlObj.send(null);
	//xmlObj.send(sendData);
	//alert("Data: "+this.data);
}

AJAX.prototype.getData = function() {
	while (!this.information) {} // ***** NEW LINE *****
	alert(this.information);
	if(this.information) {
		return(this.information);
	}
	else {
		return "EMPTY";
	}

}

Link to comment
Share on other sites

ahhh ok - i think i know what ill do ...

 

Ill add a member variable called "queryComplete", and then getData can only get data if that is true..... and saveData sets it after it successfully writes the information.

 

Thanks sooo much..... i attempted a timout but it clearly didnt work ( which is why i suspeected oop scope). - turns out my timeout never worked int the firt place.

 

Link to comment
Share on other sites

hmmm,

 

that while loop and  anything similar is anoying - it freezes up the browser.

 

is there another way to pause the function until a step is complete? ( as in like window.setTimeout();.... that actually pauses the window?).

 

see if i was to code this custom for every page - then sure.. i could use readystate to change when the page loads, and ive done that before.  however the point of this wrapper is so that i dont have to do that.... to make it as easy as create object, run query, get data, use data.  Is there any other way i can pause the window or use readystate withougth havign to custom write this general purpose ajax wrapper each time - and withought freezing the page?

 

Thanks

 

edit:  you were 100% right on the time issue above... thanks

Link to comment
Share on other sites

I'm not gonna lie; I'll just throw this out there: I didn't read the entire thread, but I think I have an idea of what your problem is.

 

Well first off, this is wrong:

 

AJAX.prototype.runQuery = function(url, sendData, xmlTagName) {

 

this.proccessTag = xmlTagName;

this.xmlObj.open("POST", url+"?"+sendData);

this.xmlObj.send(null);

//xmlObj.send(sendData);

//alert("Data: "+this.data);

}

 

Why are you sending it as POST and not posting anything?  Do you mean to make it send through POST?  Then again, maybe you're sending the data through GET on purpose since you have the correct POST stuff commented below.

 

 

Anyway, your problem:

 

Like mjdamato has already said, you're checking a variable that hasn't been sent yet.  Why hasn't it been sent?  The AJAX request is asynchronous, meaning it starts running and then execution is continued.  (If you've ever coded something in a lower-level-ish language, think of it like spawning a thread and making it do the AJAX request.  It's not technically like that, but it helps illustrate the point.)

 

Anyway, one way around it would be to make the request not asynchronous.  You know the send() method?  There's a lot of params to it, and the third one is whether or not the request should be asynchronous.  If it's set to false, script execution halts until the AJAX request finishes.  This is a bad solution though as it can freeze execution for a long time if something goes wrong.

Link to comment
Share on other sites

ok - so what i want is a way to keep the AAJX asynchronous... howeve the function that calls it shoudl be halted until the ajax finishes.  I still want the rest of the page to function normally....?  Ill have a play around with the false argument though - thanks

 

edit:

with the post data - it wasnt beign sent correctly for some reason - and as this was the first time id used post data on AJAX - i jus reverted back to sendign the basic GET data - however i can sde your point.  Im assumign though that changing POIST to GET wont solve my problem thoughj?

 

Link to comment
Share on other sites

The only way to keep the call asynch and continue execution is a call back.  (I lied.  That's not the only way, but a listener in this situation is basically the same as a callback [in function, not how they work], and a callback is much easier/more efficient.)

 

I would personally do it like this:

 


function ExampleCallback(responseText, status, ajax) { //the parameters are just to make sure you would have everything you would need
     if(status != 200) { alert('Uh oh'); return false; }
     alert(responseText);
}

var a = new Ajax();
a.runQuery("GET", "somepage.php", "", ExampleCallback);

 

Then your AJAX object would look like this:

 

function AJAX() {
var self = this;
var xmlProccessor;
var xmlObj;
var xmlProccessor;
var proccessTag;
var information;
var callback;
try {
	self.xmlObj = new XMLHttpRequest();
}
catch(e) {
	try {
		self.xmlObj = new ActiveXObject("Msxml2.XMLHTTP");
	}
	catch(e) {
		try {
			self.xmlObj = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch(e) {
			alert("AJAX is not enabled in your browser.  To complete this form you need to have full JavaScript capabilities enabled.  Please email webmaster@battleages.com for more information.");
			return false;
		}
	}
}
this.xmlObj.onreadystatechange = function()
	{
		if(self.statusalert == true) {
			if(self.xmlObj.readyState == 0) {
				self.statusobject.innerHTML = "AJAX";
			}
			else if(self.xmlObj.readyState == 2) {
				self.statusobject.innerHTML = "Request sent.";
			}
			else if(self.xmlObj.readyState == 3) {
				self.statusobject.innerHTML = "Receiving data.";
			}
			else if(self.xmlObj.readyState == 4) {
				self.statusobject.innerHTML = "Proccessing data.";
			}
		}
		if(self.xmlObj.readyState == 4) {
			//self.xmlProccessor = new XML(self.xmlObj.responseXML);
			//v = self.saveData(self.xmlProccessor.proccessOn(self.proccessTag));
			//alert("V: "+v);
			if(this.callback) {
				if(this.processTag) {
					var txml = new XML(this.xmlObj.responseXML);
					var tval = txml.processOn(this.processTag);
					//assuming your xml stuff works... never done that before ;p
					this.callback(tval, this.xmlObj.status, this);
				}
				else {
					this.callback(this.xmlObj.responseText, this.xmlObj.status, this);
				}
			}
		}
	}
}

AJAX.prototype.setStatusAlerts = function(a, b) {
if(a == 1) {
	this.statusalert = true;
	this.statusobject = b;
}
else {this.statusalert = false;}
}

AJAX.prototype.runQuery = function(method, url, sendData, cback, xmlTag) {
this.proccessTag = (xmlTag) ? xmlTag : false;
this.callback = (cback) ? cback : false;
//if I were you, I would make the post data a little easier to send....  In this format, you would have to do var=val&var2=val2... so on, but why make you gen that everytime?
//You could do something like this:
/*
if(typeof sendData == 'object') {
	var tobj = sendData;
	sendData = "";
	for(k in tobj) {
		sendData = sendData + k + "=" + escapeURI(tobj[k]) + "&";
		//escapeURI doesn't work in some situations, but you could google for a better solution if you actually do this
	}
}

Then, you could pass in something like runQuery("login.php" {username: "Corbin", password: "secret"}) instead of runQuery("login", "username=Corbin&password=Secret")

*/
this.xmlObj.open(method, ((method == "GET") ? url + "?" + sendData : url), true);
this.xmlObj.send((method == 'POST') ? sendData : null);
}

 

 

Oh, by the way, I personally would handle the XML stuff in the callback, not in the AJAX object, since it shouldn't be the object's responsibility to handle the return, but that's just my opinion.

Link to comment
Share on other sites

thats got it working - thanks.

 

Im goign to work on it a little more soon - to make the arguments simply an array of whatever and that sort of thing... so its really cross-usage etc.  but for now its working 100% perfect :)

 

Thanks

Link to comment
Share on other sites

It would probably be better to use an object, as it can do name associations, and arrays can't.  (For example {name: "corbin"} instead of ["corbin"])  Or, I guess you could do a multi-dimensional array or two arrays....

 

 

Also, I figure I should warn you incase you try to reuse an instance of the AJAX class....

 

The XMLHttpRequest object has issues with reusability in IE.

 

http://en.wikipedia.org/wiki/XMLHttpRequest#Reusing_XMLHttpRequest_Object_in_IE

Link to comment
Share on other sites

ahhh ok - ill have a read.

 

This is for a private usage between me and a small team of developers - wll who use FF, and all who ... if dont use FF wont be able to use this system ;)  But thanks for the heads up on that - ill keep it in mund when developing larger use projects wiht my wrapper.

 

Thanks

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.