I am trying to build a lib and I need to call functions dynamically depending on the variables I have in parameter like this
strategies = min
function dispatchRuleToStrategy(strategies)
{
$.each(strategies, function(index, value) {
strategy = "strategy_" + value;
});
}
function strategy_min()
{
// do something
}
How can I call the function strategy_min() from dispatchRuleToStrategy()?
I've been trying a couple of things none of which are working.
Thanks for your help
Use an Object to create a dictionary of your functions e.g. lib
var lib = {
'strategy_min': strategy_min
};
then you can invoke via the key in this dictionary Object
lib['strategy_min']();
If you've named all your functions and you don't want to re-type the names over and over, you could
var lib = {};
function addToLib(fn) {
lib[fn.name] = fn;
}
// then
addToLib(strategy_min);
// or
[strategy_min].forEach(addToLib);
Put them in an object and use the property name:
var strategy_table = {
min: function() {
// do something
},
max: function() {
// do something else
},
...
};
Then you can access them as strategy_table[value]:
$.each(strategies, function(index, value) {
strategy_table[value]();
});
Others have already suggested to create a wrapper object for the functions, however, if your strategy_min() function is in the global scope, you can access it directly:
window['strategy_' + value]();
window in browsers refers to the global object. The bracket notation is used to access properties whose keys are dynamically generated. This way you are accessing the function, which is a property of the global object, i.e. window, and calling it using the parentheses.
Finally I found the real problem. I was in a jquery document ready which is a closure. I did not knew what closures were before today.
Thanks all for your help
You can use eval() function in the following manner
$.each(strategies, function(index, value) {
strategy = "strategy_" + value;
eval(strategy+"()");
});
Related
I have the 'phone_dlg_manager' constructor function and its private methods show and init_country_code_combobox. The dialog reference is held in the phone_dlg variable. The show method triggers init_country_code_combobox and I have two options:
1) Explicitly pass the variable country_combobox that the init_country_code_combobox methods needs:
function phone_dlg_manager(ctx, open_dlg_button, edit_ctrl, item)
{
var phone_dlg;
show();
function show()
{
phone_dlg = ctx.application.ui.create_dialog(0, "PhoneEditorDlg");
init_country_code_combobox(phone_dlg.country);
read_to_dialog_controls(this._form_item);
phone_dlg.visible = true;
}
function init_country_code_combobox(country_combobox)
{
country_combobox.items.clear();
country_combobox.items.start_adding();
country_combobox.items.finish_adding();
}
}
2) Since phone_dlg is accessible withing init_country_code_combobox through closure, I can access the property that I need without explicitly passing the variable:
function phone_dlg_manager(ctx, open_dlg_button, edit_ctrl, item)
{
var phone_dlg;
show();
function show()
{
phone_dlg = ctx.application.ui.create_dialog(0, "PhoneEditorDlg");
init_country_code_combobox(phone_dlg.country);
read_to_dialog_controls(this._form_item);
phone_dlg.visible = true;
}
function init_country_code_combobox()
{
var country_combobox = phone_dlg.country;
country_combobox.items.clear();
country_combobox.items.start_adding();
country_combobox.items.finish_adding();
}
}
The second option seems easier to understand when reading code, however it makes the init_country_code_combobox function know more than it needs. Which option should I choose?
Thanks
This is mostly a matter of style. Option 1 is a little cleaner, and more extensible, since you can use init_country_code_combobox() to initialize more than just the one dialog. But if this is unlikely to be necessary, option 2 is not unreasonable.
This might seem like a noob question but I'm not sure what to do. I have function with 2 variables.
function someInfo(myVar1,myVar2)
{
this.lmyVar1=myVar1;
this.myVar2=myVar2;
}
myInstance=new someInfo("string1","string2");
function drawVariables(){
document.write(myInstance.myVar1);
document.write(myInstance.myVar2);
}
I want to use the same drawVariable() for multiple instances. I just can't figure out how the exact syntax for that. How can I make drawVariable() use a different instance of someInfo without repeating anything? Is there a simple example or tutorial I can follow?
Add an argument to the definition of function drawVariables. In the code below, this argument is called info. Now you can use info as your object inside the drawVariables function, and while calling drawVariables function, you can pass whatever instance you want to pass it. drawVariables function would now work with whatever instance you pass it while calling.
function someInfo(myVar1,myVar2)
{
this.myVar1=myVar1;
this.myVar2=myVar2;
}
// Create two separate instances
myInstance=new someInfo("string1", "string1");
myInstance2 = new someInfo("string2", "string2");
// info is the argument that represents the instance passed to this function
function drawVariables(info){
alert(info.myVar1 + ", " + info.myVar2);
}
// Call the function twice with different instances
drawVariables(myInstance);
drawVariables(myInstance2);
See http://jsfiddle.net/WLHuL/ for a demo.
function drawVariables(instance){
document.write(instance.myVar1);
document.write(instance.myVar2);
}
Would it make sense for you to do it this way?
function someInfo(myVar1, myVar2)
{
this.lmyVar1 = myVar1;
this.myVar2 = myVar2;
this.drawVariables = function ()
{
document.write(this.lmyVar1);
document.write(this.myVar2);
}
}
function Test()
{
var obj1 = new someInfo("aaa", "bbb");
var obj2 = new someInfo("xxx", "zzz");
obj1.drawVariables();
obj2.drawVariables();
}
I am trying to extend a jquery element with custom properties and functions so I can track my elements and get their custom properties anytime.
at the moment I have done this:
jQuery.fn.designerElement = function (tpl, cls) {
this.__template = tpl
this.des__class = cls;
this.compile = function () {
this.html(this.__template(this));
return this;
}
return this;
};
var template = Handlebars.compile(
'<div id="' + +new Date() + '" class="{{des__class}}"></div>'
);
var el = $('<div></div>').designerElement(template, "form-container");
el.attr('id', "test");
el.compile();
$('body').append(el);
Now if I call $('#test').compile() it say says the method is undefined.
Here is a fiddle: http://jsfiddle.net/jmorvan/HLVj4/
To explain my context, I need the methods and properties available directly on the object for some dataBindings to work, thats why i can't use .data(). It seemed to me jquery plugin would be the best approach but I am definitly missing something here.
So just to be clear I would need to be able to access properties like this: $('#test').__template as well as functions.
thanks for your time!
I think you already know what is going on but here's a short explanation: With $('#test') you are creating a new jQuery object. It contains the same element but you are defining properties for the jQuery object, not the element. So what you are asking is if there is a way to add the functionality to the element but with the jQuery object attached. In jQuery data() can after all be used to do this:
jQuery.fn.designerElement = function (tpl, cls) {
this.__template = tpl
this.des__class = cls;
this.compile = function () {
this.html(this.__template(this));
return this;
}
this.data('this', this);
return this;
};
And retrieve that object with:
var test = $('#test').data('this');
Here is the fiddle
Another solution is to store all these objects in a globally available array or JSON object.
I'm not into JavaScript OOP, so I've made an object with some fields which contains some functions to invoke.
var test = {
questions: [],
addQuestion: function(questionTitle, possibleAnwsers)
{
// not really important
},
appendQuestionToHTML: function(question)
{
// not really important
},
makeQuestionFieldsEditable: function($questionNode)
{
$questionNode.find(".questionTitle").first(function(){this.changeTextOnClick($(this));});
$questionNode.find(".questionChoice").each(function(){this.changeTextOnClick($(this));});
},
changeTextOnClick: function($spanElement)
{
// not really important
}
};
Following object in makeQuestionFieldsEditable() function looks for ".questionTitle"-class node and all of ".questionChoice"-class nodes invoke another function for them.
The problem is that using this in anonymous function references to itself, not function saved on field changeTextOnClick.
Javascript/JQuery wants to invoke this function on HTMLDivElement, which doesn't exists.
Is there any solution?
You can do the trick using a reference to your this variable :
makeQuestionFieldsEditable: function($questionNode)
{
var that = this;
$questionNode.find(".questionTitle").first(function(){that.changeTextOnClick($(this));});
$questionNode.find(".questionChoice").each(function(){that.changeTextOnClick($(this));});
},
I think all you need to do is change 'this' to 'test' (the variable you have assigned this object to).
Can anyone tell me why my 'showDiv_boo' is undefined inside the class´s method?
I also can´t access my class´s methods.
Here´s my class 'Blink' class with its properties and methods:
function Blink(div) {
this.div = div
}
Blink.prototype.counter = 0
Blink.prototype.showDiv_boo = true
Blink.prototype.showDiv = function() {
this.div.style.visibility = 'visible'
}
Blink.prototype.hideDiv = function() {
this.div.style.visibility = 'hidden'
}
Blink.prototype.startEngine = function() {
if (this.showDiv_boo) {
this.showDiv()
} else if (!this.showDiv_boo) {
this.hideDiv()
}
this.showDiv_boo = !this.showDiv_boo
this.counter++
}
Blink.prototype.startEffect = function() {
this.idEffect = setInterval(this.startEngine, 1000 / 45)
}
So, if I create:
_blink = new Blink(myDiv);
_blink.startEffect();
You can test... the variable 'showDiv_boo', is undefined inside the method.
Even, if I set the showDiv_boo inside the method to true, it won´t call my class´s methods showDiv or hideDiv.
Anyone?
Thanks :)
The reason why is that startEngine is called from setInterval. The way in which this callback is invoked causes startEngine to have a different value for this than startEffect. You need to save this in order to maintain it in the callback. For example.
Blink.prototype.startEffect = function () {
var self = this;
self.idEffect = setInterval(function () { self.startEngine(); }, 1000 / 45);
};
You need to:
use var self and call the method via self.startEngine()
use an anonymous function to wrap the call in [1] i.e. function(){ self.startEngine(); }
This is because when you just pass this.startEngine or self.startEngine you are just passing the function startEngine without specifying what this is, which in both cases is supplied by the global conext of DOMWindow.
To give an example...
function startEngine() {
...code omitted...
};
Blink.prototype.startEngine = startEngine;
Blink.prototype.start = function() {
setTimeout(startEngine, 0); // obviously wrong, what is this?
setTimeout(Blink.startEngine, 0); // actually the same as line above, although not as obvious
setTimeout(startEngine.bind(this), 0); // works correctly
}
works to add code to the prototype and if used in the anonymous function will work as expected, but if you just use Blink.startEngine as the callback it is exactly the same as using startEngine only the second is more obviously wrong because there's no object it is being called on so you'd expect this to be whatever is supplied by the context.
The other way you could do this without using the anonymous function would be
Blink.startEngine.bind(self)
Which returns a function that will call startEngine with the correct this same as explicitly creating the anonymous function and wrapping the call to self.startEngine()
Heres a link to a fiddle to play around with the differences: http://jsfiddle.net/bonza_labs/MdeTF/
If you do the following, you will find it is defined
var x = new Blink('hello');
x.showDiv_boo
Javascript uses prototypical inheritance. While showDiv_boo may not be explicitly defined within the instance of Blink that you now have, it does exist within the prototype that Blink inherits from. When you try referencing showDiv_boo from within the object, the Javascript engine realizes the object does not own a member by that name and then will check its prototype.
Along with setting a temporal variable to store this, you must call the startEngine() function with that variable:
Blink.prototype.startEffect = function(){
var self = this;
self.idEffect = setInterval(function(){ self.startEngine.call(self); }, 1000/45);
}
Note the .call(self), which basically calls the function with the variable self, so the variable this in startEngine will be the correct one.