PDA

View Full Version : passing object into addListener



martin
21 Feb 2007, 7:33 PM
DomQuery is great. I figured I'd clean up some of my code with it until I ran into this problem:

I have an object that attaches one of it's own functions to a bunch of dom elements. When that function executes it calls another of its functions.

batchInit is really selecting the appropriate item and then assign the function to call and a reference to the object so when the function get's executed it has a reference to the object.

I can replace the entire batchInit function with:
Ext.select("#wrap span.ui-info a:first-child").on("click", this.prepInfo);

Which is amazing. However, I don't understand how to pass the object to the function itself as in the old addListener:
YAHOO.util.Event.addListener(anchors[0],'click', func, this, false);

so that the prepInfo knows how to call the showDialog function.

Perhaps I'm doing this all wrong...martin


The old way:

var InfoDialog = function(){
// define some private variables
var dialog;

// return a public interface
return {
init : function(){
this.batchInit("ui-info", this.prepInfo);
//more init functions here
},

batchInit : function(type, func){
var links = YAHOO.util.Dom.getElementsByClassName(type, "", "wrap");

// attach to click event of anchor and pass the link into the dialog box when called
for (var i = 0; i<links.length;i++) {
if (links[i].tagName == "SPAN") {
var anchors = links[i].getElementsByTagName('a');
YAHOO.util.Event.addListener(anchors[0],'click', func, this, false);
}
}
},

prepInfo : function(e, el){
dialog.setTitle('Info');
Ext.apply(dialog, {modal:false});
el.showDialog(e, this);
},

showDialog : function(e, link){
//do some work
}
};
}

kjordan
21 Feb 2007, 8:11 PM
It works the same way.

myElement.on('click',this.handleClick,this,true);

Now in handleClick, this will be the execution scope.

martin
21 Feb 2007, 8:24 PM
I see. There is a slight diference though>

With Ext.on the objects in the called functions reversed:

YAHOO's addlistener assignes the clicked link to 'this' and the passed object to 'el'. Ext.on however sets 'this' to the object and el to the link clicked...martin

Animal
22 Feb 2007, 12:40 AM
Ext 1.0 is different and offers more flexibility.

You could use



myElement.on({"click": func, scope: this, foo:"bar"});


The function will be called with params (Ext.EventObject, targetEl, {"click": func, scope: this, foo:"bar"}) in the scope specified by the "scope" property. The options object passed to the on() call is passed as the 3rd parameter, so you can add anything you want in there, and pick it up in your handler.

If no scope is specified, the scope is set to the DOM element.

martin
22 Feb 2007, 7:00 PM
That's cool that you can pass in other parameters and such, but I don't understand why the object changes that 'this' refers to. It's incosistent:

Ext.select("span.ui-info a:first-child").on("click",this.prepInfo);
When the prepInfo function fires: 'this' refers to the link itself and oddly enough is also passed in as the third parameter (the second being the click event). arguments.length = 3.


Ext.select("span.ui-info a:first-child").on("click",this.prepInfo, this);
When the prepInfo function fires: 'this' suddenly no longer refers to the link itself but the additional object passed in. The third parameter is now the link. In essence its flipped.

Ext.select("span.ui-info a:first-child").on({"click": this.prepInfo, scope: this});
When the prepInfo function fires: 'this' again no longer refers to the link itself but the additional object passed in. In addition a new object is attached with the click event and the object in it.

Same result with
Ext.select("span.ui-info a:first-child").on("click",this.prepInfo, {scope: this});

I'd call it a bug, but I'm really not an expert on this.
At least in the first three situations 'this' inside the attached function should refer to the object the function is attached to and not the object the function belongs to and the additional parameter should be the third one (I hope this makes sense)...martin


[/code]

Belgabor
22 Feb 2007, 7:24 PM
I'm also no expert on this (;)), but at least the first two examples make perfect sense.
this.prepInfo only identifies the function, it does not define or imply the scope in any way, that's the job of the third parameter of on. If that is omitted, the scope defaults to the element the handler is attached to.
As I'm mostly a C++/Java guy this concept is also pretty alien to me, but after watching the yahoo yui tutorial vids linked elsewhere in this forum, I at least learned to recognize when JavaScript's weirdness strikes :p

martin
22 Feb 2007, 7:51 PM
This however sounds like an API issue and not a javascript weirdness and hence a question for Jack?

I would think the API should be consistent. It would also be consistent with the addlistener arguments order in the YUI, I think or at least how I've used it)...martin

kjordan
22 Feb 2007, 8:05 PM
This however sounds like an API issue and not a javascript weirdness and hence a question for Jack?

I would think the API should be consistent. It would also be consistent with the addlistener arguments order in the YUI, I think or at least how I've used it)...martin
I don't think it's any different really. Ext also has a addListener thing if you feel more comfortable with that. With Ext and on, you're already saying which element to use and thus it doesn't need to be passed. However, if you want to change the scope, then you can pass an object in and pass true at the end. The same works for YUI. on is sort of like myobj.addClickListener(); in that it's just shorthand for addListener.

There's also that fact that Jack is trying to free Ext from dependencies on YUI, so there's no reason it has to follow the YUI API.

martin
22 Feb 2007, 8:55 PM
'on' is just shorthand for 'addListener' according to the docs.

My struggle is not that the element is passed automatically, but that the behavior changes in the different ways of calling 'on' or 'addlistener'.

That makes it hard to deal with the various implementations if i later need to pass a scope or other variable, I have to change the called function as well.

I guess I'll have to think again about using it. I'd rather know that its consistent and I can make changes later without having to rewrite the functions again.

Thanks for your insights...martin

jack.slocum
23 Feb 2007, 2:58 AM
> That's cool that you can pass in other parameters and such, but I don't understand
> why the object changes that 'this' refers to.

The parameter is called "scope" because that is what it changes.

> It's incosistent:

The event handlers are very consistent. If you pass in a scope - that becomes the scope - otherwise the scope is the event element. Let's look at your examples.

Ext.select("span.ui-info a:first-child").on("click",this.prepInfo);

No scope passed, so scope is the link.

Ext.select("span.ui-info a:first-child").on("click",this.prepInfo, this);
Ext.select("span.ui-info a:first-child").on({"click": this.prepInfo, scope: this});

Scope passed is "this", so that will be the scope on handler calls (not the link).

> I'd call it a bug, but I'm really not an expert on this.

I would take the time to get to learn them. I promise, I don't do things for no reason. ;) The new event API is mostly backwards compatible, but offers much more flexibility and power.


> At least in the first three situations 'this' inside the attached function should
> refer to the object the function is attached to and not the object the function
> belongs to and the additional parameter should be the third one

"this" inside the function is the scope. So if you pass in a requested scope, it is set to that. How can this be wrong? It gives you the ability to point "this" at anything you want, and if you say I want it to be X it should set it to X and not leave it the link.

Your handlers are always called with 3 parameters:

e (Ext.EventObject), target (The event target, e.g. the link), options (the options object passed in to the event attachment).

If you have any other questions, I'd be happy to help.

Animal
23 Feb 2007, 3:02 AM
That's cool that you can pass in other parameters and such, but I don't understand why the object changes that 'this' refers to. It's incosistent:

Ext.select("span.ui-info a:first-child").on("click",this.prepInfo);
When the prepInfo function fires: 'this' refers to the link itself and oddly enough is also passed in as the third parameter (the second being the click event). arguments.length = 3.
[/code]

It's not inconsistent. There is no "this" when you just pass a function like that. It's just a reference to a function, and that's all.

http://www.yui-ext.com/manual/core:events

http://www.yui-ext.com/manual/utilities:function

martin
23 Feb 2007, 9:39 AM
I think I'm catching on. I understand why changing the scope changes the 'this' reference within the function called when the event happens.

To sum it up, Ext.on is more flexible in that I can attach anything, which is great.
Attaching anything, however, means it's changing the scope at the same time. All parameters passed as the third parameter in the .on call will be bundled together in the scope object passed to the function.

(I also tried attaching the link itself as the scope, but then the function called doesn't get the scope of the parent object instead the link reference twice.)

Thanks for clarifying. Main reason I wanted to preserve the old way is that I've got a boat load of work to do if I want to migrate from YUI's way.

It can be done just not by keeping the scope to the link itself. I guess I either have to bite the bullet and rewrite all the functions or live with the old way. :?

Thanks for helping me learn...martin

jack.slocum
23 Feb 2007, 2:12 PM
You shouldn't have to rewrite anything - they are compatible (for the msot part). What exactly is breaking?

martin
23 Feb 2007, 2:29 PM
I can't pass an additional object into the attached function without switching the scope:
Here's what I did so far in YUI. This happens inside of an object that has a method named prepInfo (I'm looping over all the found anchors):


YAHOO.util.Event.addListener(anchor,'click', this.prepInfo, this, false);

prepInfo looks like this:


prepInfo : function(e, el){
dialog.setTitle(this.title);
Ext.apply(dialog, {modal:false});
el.showDialog(e, this);
},


The scope of the function is the anchor (referenced by 'this') and el is the object the function belongs to.
e = event
el = parentObject
this = anchor

If I do this with Ext the parameters of the functions change as the scope is reassigned differently:


Ext.select("span.ui-info a:first-child").on("click",this.prepInfo,this);


In this case, the scope is no longer the anchor but instead the parent object and el is the anchor. They are just flip flopped. I love the simplicity of it, it just means that I have to go into all my methods and functions and reverse them.
e = event
el = anchor
this = parentObject

Perhaps I should have done it differently with YUI as well but it would make more sense (to me at least) if I could add additional parameters without changing the scope.

Does this make sense?

Thanks...martin

jack.slocum
23 Feb 2007, 2:36 PM
Ah, I see what you are doing. You can do the same thing:

Ext.select("span.ui-info a:first-child").on("click", this.prepInfo, false, this);

or:

Ext.select("span.ui-info a:first-child").on({"click":this.prepInfo, foo: this});

Then you handler will be called with:


function(e, target, o){
alert(o.foo);
}

and the scope will remain the link.

martin
23 Feb 2007, 6:42 PM
That's close enough. In this case I only have to change the signature of the functions and just ignore the duplicate 'target' (another reference to the anchor).

prepInfo : function(e, target, el)

For your info: the second form throws an error. Firebug reports:

fn has no properties
EventManager(a#yui-gen0.wikilink1 doku.php, "foo", Object, undefined, undefined)js.php (line 1627)
EventManager(a#yui-gen0.wikilink1 doku.php, Object foo=Object, undefined, a#yui-gen0.wikilink1 doku.php, undefined)js.php (line 1627)
CompositeElement(Object foo=Object, undefined, undefined, undefined)js.php (line 1631)
init()js.php (line 3255)
Observable()js.php (line 1625)
EventManager()js.php (line 1627)
CustomEvent(DOMContentLoaded )js.php (line 1599)
[Break on this error] Ext.EventManager=function(){var _1;var _2;var _3=false;var _4;var _5;var E=YAHOO...