groovecoder (15) [Avatar] Offline
#1
I'm using prototype.lite.js, and trying to create an Explanation class...

Explanation = Class.create();
Explanation.prototype = {
initialize: function (threadId) {
this.threadId = threadId;
this.explainThread(this.threadId);
},

explainThread: function(threadId){
var explainURL = "explain.php?Id="+threadId;
new ajax(explainURL, {method:'GET', onComplete: this.handleExplainResponse});
},

handleExplainResponse: function(request){
eval(request.responseText);
this.sql = localExplanation.sql;
this.tables = localExplanation.tables;
this.alertExplanation();
},

alertExplanation: function(){
var dispOutput = "Explanation for thread: " + this.threadId;
dispOutput += " SQL: " + this.sql;
dispOutput += " tables: ";
for(i=0; i<this.tables.length; i++){ >
table = this.tables[i];
dispOutput += table.table + ", ";
}
alert(dispOutput);
}
};

In my page, I have:

function explain(threadId){
new Explanation(threadId);
}

and on a div:

<div class="Columns dataRow" onmouseover="highlight(this);" onmouseout="deHighlight(this);" onclick="explain(1);">

When I click on the div, everything seems to work fine down to the point where the handleExplainResponse function tries to call the alertExplanation function. It gives an error: "this.alertExplanation is not a function"

?!?!
groovecoder (15) [Avatar] Offline
#2
Re: the function that isn't ?
I'm also using moo.ajax (http://mad4milk.net/entry/moo.ajax) which I think might be causing a problem...? I don't know enough about javascript to tell whether or not the moo.ajax call is discarding the object context when it comes back...but it seems that my this.threadId variable gets lost after the moo.ajax call....
Pascarello (208) [Avatar] Offline
#3
Re: the function that isn't ?
I glanced at it real quick. Not really thinking on my part, little busy coding an Ajax project at the moment. By you might want to try

changing:
new ajax(explainURL, {method:'GET', onComplete: this.handleExplainResponse});

to

var _ref = this; new ajax(explainURL, {method:'GET', onComplete: _ref.handleExplainResponse});

not sure if that will do it, but worth a shot. If it does not work, I will try to work with it a little later on when I have some time.

Eric
groovecoder (15) [Avatar] Offline
#4
Re: the function that isn't ?
That didn't work either - still got a "this.displayExplanation is not a function" error.

But I'm now fairly positive that the new ajax(...) line is causing the problem(s). I commented it out and had the explainThread() function make a simple call to handleExplainResponse, then I hard-coded some values for request and localExplanation. When I ran it, it entered this.displayExplanation() and all of the this.* variables retained their values.

Like I said, I don't know enough to know if it's a problem with the moo.ajax class, but it sure looks like it is. I'm going to look into using a different ajax class/lib.
groovecoder (15) [Avatar] Offline
#5
Re: the function that isn't ?
Sure enough...when I went to look at Yahoo's ConnectionManager class (http://developer.yahoo.com/yui/connection/), I read this: "When an object's method is used as the success or failure handler in a callback, any usage of this in the method loses scope."

But, the Yahoo ConnectionManager seems to provide a solution to that by specifying scope in the callback object used to initiate/configure the ConnectionManager. I'm going to convert my code to use Yahoo's ConnectionManager and I'll post the results here.
groovecoder (15) [Avatar] Offline
#6
Re: the function that isn't ?
The Yahoo.util.Connection class worked like a champ. The following correctly alerted the values of this.threadId, this.sql, and this.tables. I think I have a new favorite Ajax library...er, tied with Rico, of course! smilie

Explanation = Class.create();

Explanation.prototype = {
initialize: function (threadId) {
this.callback = {
success: this.handleExplainResponse,
failure: this.handleExplainResponse,
scope: this
};
this.threadId = threadId;
this.sql = "";
this.tables = "";
this.explainThread(this.threadId);
},

explainThread: function(threadId){
var explainURL = "explain.php?Id="+threadId;
YAHOO.util.Connect.asyncRequest("GET", explainURL, this.callback);
},

handleExplainResponse: function(request){
eval(request.responseText);
this.sql = localExplanation.sql;
this.tables = localExplanation.tables;
this.displayExplanation();
},

displayExplanation: function(){
var dispOutput = "Explanation for thread: " + this.threadId;
dispOutput += " SQL: " + this.sql;
dispOutput += " tables: ";
for(i=0; i<this.tables.length; i++){ >
table = this.tables[i];
dispOutput += table.table + ", ";
}
alert(dispOutput);
}
};
groovecoder (15) [Avatar] Offline
#7
same problem with setInterval?
Okay, so I got around the problem of the moo.ajax breaking the usage of "this"...only to run into the same problem again when using the setTimeout or setInterval function. What I'm trying to do is create a poller function inside my object which will run every X seconds, like so:

ProcessList = Class.create();

ProcessList.prototype = {
initialize: function() {
this.pCallback = {
success: this.refreshThreads,
failure: this.refreshThreads,
scope: this
};
this.refreshInterval = 2;
},

refreshThreads: function(request){
$('threadDisplay').innerHTML = (request) ? request.responseText : "";
if ($('secondsRefresh').value){
var secValue = parseFloat($('secondsRefresh').value);
if (isNaN(secValue)){
alert("Invalid entry for refresh interval, resetting to default 3");
this.refreshInterval = 2;
} else {
this.refreshInterval = secValue;
}
}
if(this.refreshInterval > 0){
setInterval(this.updateThreadInfo, this.refreshInterval*1000);
}
},

updateThreadInfo: function(){
alert("this.refreshInterval: " + this.refreshInterval);
var processURL = "processlist.php?"; // server-side script

for (var i=0;i<filters.length;i++){ >
var filter = filters[i];
var filterInputEl = document.getElementById(filter);
if(filterInputEl.value){
processURL += filter+"="+filterInputEl.value+"&";
}else{
processURL += filter+"=ALL&";
}
}
YAHOO.util.Connect.asyncRequest('GET', processURL, this.pCallback);
}

};

Then from inside my page, I have:

var processList = new ProcessList();
processList.refreshThreads();

Which causes the page to wait 2 seconds, and then run updateThreadInfo, which gives an error: "connection.js (line 32): callback has no properties; ProcessList.js (line 42)"

If I change the page to call processList.updateThreadInfo(), it does the asyncRequest correctly ONCE, then into the refreshThreads (which executes the setInterval), and then it starts breaking again.

So it looks like the setInterval is messing up the "this" scope again...any ideas for a better way to implement a polling service?

Thanks.
groovecoder (15) [Avatar] Offline
#8
Re: same problem with setInterval?
UPDATE:

I've confirmed that setInterval is indeed breaking the "this" context, so I tried some ideas from the ydn-javascript group...

First I tried:
var self = this;
setInterval(function(){self.updateThreadInfo();}, this.refreshInterval*1000);

Which worked, but created a closure so I obviously can't use it (?)

I also tried:
setInterval(this.updateThreadInfo, this.refreshInterval*1000, this.pCallback);

Which also worked, but caused another closure.

I also tried:
setInterval(ProcessList.updateThreadInfo, ProcessList.refreshInterval*1000);
error:
"useless setInterval call (missing quotes around argument?"

so I tried:
setInterval(ProcessList.updateThreadInfo(),ProcessList.refreshInterval*1000);
and
setInterval("ProcessList.updateThreadInfo()",ProcessList.refreshInterval*1000);
error:
"ProcessList.updateThreadInfo is not a function"

And finally, I tried using a helper function...

mySetTimeout = function(func, delay){
var context = window, argsStart = 2;
if(typeof func == ‘Function’ || typeof func == ‘function’){
context = func;
func = delay;
delay = arguments[2];
argsStart++;
}

if(typeof func == ‘String’ || typeof func == ‘string’){
func = context[func];
}

var args = [];
for (var i = argsStart; i < arguments.length; i++) {
args.push(arguments[i]);
}
return setTimeout(function () { func.apply(context, args); }, delay);
}

and then:

mySetTimeout(this, this.updateThreadInfo, this.refreshInterval*1000);
which gave:
"func.call is not a function (mySetTimeout.js line 25)"
which occurs on the last return line of the mySetTimeout function.

function.call() and function.apply() are only available as of JS 1.3 ... do today's browsers support JS 1.3 ? Why isn't the call function recognized?