0Day Forums
How to create dynamic buttons to call a function with different arguments - Printable Version

+- 0Day Forums (https://zeroday.vip)
+-- Forum: Coding (https://zeroday.vip/Forum-Coding)
+--- Forum: JScript (https://zeroday.vip/Forum-JScript)
+--- Thread: How to create dynamic buttons to call a function with different arguments (/Thread-How-to-create-dynamic-buttons-to-call-a-function-with-different-arguments)



How to create dynamic buttons to call a function with different arguments - tympanitis900 - 07-24-2023

I'm trying to build up a jscript interface on a WSH component for foobar2000 music player.
At some point, i need to create a variable number of buttons that will each call the same function, but using a different argument. I can create the buttons, using a *for loop* and an *array*. However, all buttons will pass the argument of the **last** button only.

Here is the code i use to add buttons :

for (var i=0; i<_groupsArray.length; i++) {
_btn = _groupsArray[i];
ƒview.addButton(_btn, _img, function () {setView(_btn)}, function () {doPlay(_btn)});
}
In this case, _btn is used both as the button label and the argument i want to pass to the function when i click on the button (it will be an artist name, or album title or something similar). *ƒview* is a custom object that holds the buttons. I created it so i don't have to specify position of buttons, *ƒview* will calculate them whenever needed.
This is the part of the addButton method that will actually create the buttons (the rest does calculate the layout). Each action argument is passed as a parameter to the button being created, without any processing by the addButton method.

this.addButton = function(label, img, action1, action2, action3, action4, action5) {
...
new ButtonObject(label, __x, __y, __w, __h, action1, action2, action3, action4, action5);
Finally, i have this in my buttonObject constructor :

function ButtonObject(label, x, y, w, h, action1, action2, action3, action4, action5) {
...
this.action1 = action1;
this.action2 = action2;
this.action3 = action3;
this.action4 = action4;
this.action5 = action5;
...
this.act1 = function () {
var __doIt = function () {this.action1();};
return __doIt;
}
The function passed during the *for loop* will finally be stored in the object as action1, action2 ... property.
I tried many different ways of calling *action1* with act1 method, including directly calling *this.action1()*; using *this.action1.apply()*; trying to form a clausure as you see above. Either it will call the correct action but using the argument of the **last** button created, or it will crash or have no effect.

I'm now out of ideas to try. I had some partial success passing the function and the argument separately but that's an ugly trick. I've read a number of tutorials on clausures and how they can keep the local variables alive, but i still didn't managed to make them work for me in this case. My last attempt (as you can see it in the code) will crash when i click the button with a "this object does not handle this property or method" pointing to the line

var __doIt = function () {this.action1();};
So how do i keep the arguments "alive" within each button?


RE: How to create dynamic buttons to call a function with different arguments - reearned549184 - 07-24-2023

Try something like:

for (var i=0; i<_groupsArray.length; i++) {
_btn = _groupsArray[i];
ƒview.addButton(_btn, _img,
function(b) {return function () {setView(b)};}(_btn),
function(b) {return function () {doPlay(b)};}(_btn)
);
}

You need to create a closure to hold the value of _btn, not just a binding to it. The extra wrapper function does precisely that.