PDA

View Full Version : Ext.util.Observable additional addListener option "addOnce"



dyndan
28 Aug 2008, 4:43 AM
Hi people,

in some cases loopy code or extensive use of event firing can lead to unwanted multiple attachments of a listener.

To avoid this, I added the option "addOnce" to the addListener function options (like single, delay, buffer, see http://extjs.com/deploy/dev/docs/?class=Ext.util.Observable).

This allows you to do the following:



this.myMultipleTimesCalledFunction = function(outerArgs) {
//Note that listener function should not deal with outerArgs in case of "addOnce=true".
this.on("test", function(args) {
alert("test listener handled args="+args);
}, this, {
single : true,
addOnce : true
});
}
"addOnce = true" manages addListener in a way that prevents the same listener function for the same scope is added more than once.




Code:



Ext.util.Event.prototype.orgAddListener = Ext.util.Event.prototype.addListener;
Ext.util.Event.prototype.addListener = function(fn, scope, options) {
function hashCode(str) {
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = (31 * hash + str.charCodeAt(i)) % 99999999;
}
return hash;
}
var add = true;
if (options.addOnce) {
var fnHash = hashCode(fn.toString());
for (var i = 0, len = this.listeners.length; i < len; i++) {
if (this.listeners[i].scope == scope) {
if (!this.listeners[i].fnHash)
this.listeners[i].fnHash = hashCode(this.listeners[i].fn
.toString());
if (this.listeners[i].fnHash == fnHash) {
add = false;
break;
}
}
}
}
if (add)
this.orgAddListener(fn, scope, options);
}


By the way:

I checked event handling in Ext.util.Observable and I came to the conclusion that event firing through fireEvent does not support custom arguments as explained here (method addListener):



el.on('click', this.onClick, this, {
single: true,
delay: 100,
forumId: 4 //This is not supported
});
(Please correct me if I am wrong.)

I do not miss this feature cause the following does it just fine:



this.fooFunction = function(fooArgs) {
this.on("test", function() {
firstTestFunction(fooArgs);
}, this);

this.on("test", function(invokingArgs) {
secondTestFunction(invokingArgs);
}, this, {
addOnce : true
});
}
this.myInvokingFunction = function(invokingArgs) {
this.fireEvent("test",invokingArgs);
}

MyObject.fooFunction("Args of first call");
MyObject.fooFunction("Args of second call");
MyObject.myInvokingFunction("Something else");
In this example "firstTestFunction" gets called twice. The first call gets passed in "Args of first call" and the second call gets passed in "Args of second call", "secondTestFunction" gets called once with passed in "Something else".


dyndan

Animal
28 Aug 2008, 6:08 AM
I'd see that as bad coding.

If your code paths are non-deterministic in that you can't tell when and where to add a listener, then there must eb something wrong.

dyndan
28 Aug 2008, 9:46 PM
Hi animal,

thanks a lot for your thoughts. I reviewed my use case again and all of a sudden I asked myself "Wait, why don't I just add the listener at construction time?"

Well, I keep my code for managing "addOnce" anyway. Sometimes it can be hard to stick to such high standards.


Did you also have a look at the second issue in my first post?

Thanks again,

dyndan

Animal
29 Aug 2008, 11:46 AM
Ext.Element is not Observable.

Element's event listeners are called passing the Event, target and options object. So



el.on('click', this.onClick, this, {
single: true,
delay: 100,
forumId: 4 //This is not supported
});


The onClick function receives that options object as its third parameter.