Hi JB,
Am 23.03.2009 um 17:36 schrieb Jean-Baptiste BRIAUD -- Novlog:
> I come into a tricky javascript issue with a listener.
> The idea is to have a description of several buttons like this :
>
> [{lbl:"OK", cde:okFunction, ctx:this}, {lbl:"Cancel",
> cde:cancelFunction, ctx:this}]
>
> Then, a static function is called to build the panel and listeners
> like that :
>
> f : function(currentDesc) {
> var buttonPane = new qx.ui.container.Composite(new
> qx.ui.layout.HBox(5, "right"));
> for (var i = 0, buttonDescLength = buttonDesc.length; i <
> buttonDescLength; i++) {
> var button = new qx.ui.form.Button(buttonDesc[i]
> ['lbl']);
> var currentDesc = buttonDesc[i];
> var listenerCode = function() {
> button.debug("Button " + currentDesc['lbl'] +
> " CLICK ! code " + currentDesc['cde']);
> if (currentDesc['cde'] != null &&
> currentDesc['ctx'] != null) {
>
> currentDesc['cde'].call(currentDesc['ctx']);
> }
> };
> button.addListener("execute", listenerCode,
> button); // I try here with and without context as I think it doesn't
> matter in this case because of the use of call.
> buttonPane.add(button);
> }
> }
>
> Whatever I try, it is always the cancel button code that is launched
> in the listener.
> I guess it is linked to some closure and variable that are not stored
> in the closure's context : currentDesc['cde'] doesn't "stick" to the
> okFunction during the loop ??
>
> Could it be the static ? Only the factory is static, so the definition
> line is just pass as a parameter to the static function.
> Like that : blabla.f([{lbl:"OK", cde:okFunction, ctx:this},
> {lbl:"Cancel", cde:cancelFunction, ctx:this}]); // So here, ctx:this
> make sense.
>
> Any idea ?
The problem is the way you're defining the closure:
for (var i = ...) {
var x = someArray[i];
var listenerCode = function(...) {
// do something with x
}
// add the listener to the button
}
Because of JavaScript scoping rules (all local variables are visible
in the whole function), the pseudo-code above is equivalent to this:
var x;
for (var i = ...) {
x = someArray[i];
var listenerCode = function(...) {
// do something with x
}
// add the listener to the button
}
When you instantiate the function, it is bound to the current variable
context (i.e. it receives an invisible reference to the current set of
local variables). The problem is that the variables are bound by
reference, not by value. So every function instance references the
same variable x with the same value (the last one that the variable
receives inside the loop).
The solution (or at least one solution) is to use a generator function:
var generateListener = function(x) {
var listenerCode = function() {
// do something with x here
};
return listenerCode;
}
for (var i = ...) {
var listenerCode = generateListener(someArray[i]);
// add the listener to the button
}
This way, the array value is passed _by value_ to the generator
function. The generated code then references the local variables
inside the generator _by reference_ (including the function
parameters). However, this doesn't matter as a new local variable set
is created for each invocation of the generator.
I hope this explanation is understandable :-) Of course, it also
works for more than one parameter - just make the generator receive as
many parameters as you need.
Regards,
Andreas
------------------------------------------------------------------------------
Apps built with the Adobe(R) Flex(R) framework and Flex Builder(TM) are
powering Web 2.0 with engaging, cross-platform capabilities. Quickly and
easily build your RIAs with Flex Builder, the Eclipse(TM)based development
software that enables intelligent coding and step-through debugging.
Download the free 60 day trial. http://p.sf.net/sfu/www-adobe-com
_______________________________________________
qooxdoo-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel