How to call a function on the scope from a string value - javascript

I have an object containing an array of strings
$scope.actions=[
"add_inscription",
"add_tools",
"add_instruction",
"remove_inscription",
"remove_tools",
"remove_instruction"
];
and I would like to be able to do dynamic action calls through a delegating function..
$scope.delegate = function () {
var arg = arguments[0];
for ( key in $scope.actions ) {
if ($scope.actions[key] == arg ) {
// call function that has a matching name
}
}
}
So in my template I have something like this
<button ng-click="delegate('add_inscription')">Add Inscription</button>
I don't know if I am thinking in the right direction with this either,, but the point is that my actions object is actually pretty large and I don't want to write massive switch case statement that I will have to update all the time.
Is there a way to do this in angular?
I have no problem doing this in straight up javascript
var fnstring = "add_inscription";
// find object
var fn = window[fnstring];
// if object is a function
if (typeof fn === "function") fn();
but in angular I can't get this done..

assuming that your "actions" functions are defined inside the scope, like:
$scope.add_inscription = function(){ ... }
you should do:
var _action = 'add_inscription';
$scope[_action]();

Related

Compare functions in Javascript

I have an API that takes a function as an input, and then inside the API, the intent is to add the function to an Array if the function is not already added to the Array.
The call to the API is of the form:
myApiHandle.addIfUnique(function(){
myResource.get(myObj);
});
The API is:
myApiHandle.addIfUnique(myFunc) {
if (myArray.indexOf(myFunc) === -1) {
return;
}
// add to array
}
Now this obviously does not work as expected, since each time a new function is being passed in.
My Question is: Is there a way to pass in a function into the myApiHandle.addIfUnique call that will allow me to compare the existing functions in the array with this function that is currently passed in? The comparison should compare the function name and the object, and if both are the same, then not add the function to the array. I want to avoid adding another argument to the addIfUnique call if at all possible.
In other words, is the below possible:
myApiCall.addIfUnique (someFunc) {
}
If so, what is the someFunc. And what would be the logic inside the API to detect if the function already exists in myArray?
The same problem occurs with addEventListener and removeEventListener, where the callback must be identical (in the === sense) for removeEventListener to remove it.
As you've found, obviously if you call addIfUnique like this:
addIfUnique(function() { })
the function passed each time will be a unique object. The solution is to create the function once:
var fn = function() { };
addIfUnique(fn);
addIfUnique(fn);
A related problem occurs when the function being passed in is a method invocation, so I need to bind it:
var x = { val: 42, method: function() { console.log(this.val); } };
I want to pass a bound version of it, so
addIfUnique(x.method.bind(x));
addIfUnique(x.method.bind(x));
But again, each call to x.method.bind(x) will return a separate function. So I need to pre-bind:
var boundMethod = x.method.bind(x);
addIfUnique(boundMethod);
addIfUnique(boundMethod);
First of all, comparing functions is meaningless, even if two functions are literally different, they may be functionally the same.
And for your problem, you can compare whether it's exactly the same object, or you can compare it literally by using toString() function and regExp.
var addIfUnique = (function() {
var arr = [];
return function(func) {
if (~arr.indexOf(func)) return false;
var nameArr = [];
var funcName = func.name;
var funcRegExp = new RegExp('[^\{]+\{(.+)\}$', 'i');
var funcStr = func.toString().match(funcRegExp);
funcStr = funcStr && funcStr[1];
if (!funcStr) return false;
var strArr = arr.map(function(v){
nameArr.push(v.name);
return v.toString().match(funcRegExp)[1];
});
if (~strArr.indexOf(funcStr) && ~nameArr.indexOf(funcName)) return false;
arr.push(func);
};
}());

How to dynamically change the contents of a function using JavaScript

To help understand this the function is in the html page and it is generated, I cannot change the generated code:
function Update_qu7260() {
var newVal = ''
for( var idx = 0; idx < 2; idx++ )
{
var test
if( idx == 0 ) test = text7263
else if( idx == 1 ) test = text7265
if( test.matchObj ) newVal += test.leftSel + "-" + test.matchObj.rightSel + ","
}
newVal = newVal.substring( 0, newVal.length-1 )
VarQuestion_0001.set( newVal )
qu7260.hasBeenProcessed=false;
doImmFeedback('qu7260');
}
var qu7260 = new Object();
...
qu7260.updFunc = Update_qu7260;
var qObj=[qu7260];
Note in the above the number "7260", the numbers start at 1 so there are lots of them and each Update_###() will be different so I cannot re-write them with "hard wired" code. My code is in an external JavaScript file and is executed onLoad:
...
var updFunc = qObj[0].updFunc.toString();
if(updFunc.indexOf('doImmFeedback(')!=-1){
updFunc = updFunc.replace('doImmFeedback','doImmQuestionFeedback'); // do my function
updFunc = updFunc.replace('function ',''); // remove the word function
var funcName = updFunc.substr(0,updFunc.indexOf('(')); // get the function name e.g. Update_qu7260
updFunc = "window['" + funcName + "']=function" + updFunc.replace(funcName,'');
eval(updFunc);
}
...
When I change the eval() to alert() I can see the that it's correct, however, the eval() is not raising any errors and my function doImmQuestionFeedback is not being called. When I subsequently do an alert(qObj[0].updFunc.toString()) I see the original function.
It would seem that I have provided information that is too complex, so the following code is a better example:
function hi(){alert('hi');}
function changeHi(){
hi(); // I get an alert box with hi
newHi = "function hi(){alert('hi there');}"
eval(newHi);
hi(); // I get an alert box with hi
window.setTimeout('hi()',500); // I get an alert box with hi
}
window.setTimeout('changeHi()',500);
The following is the original question:
I have a predefined function that I did not create, however, I know it's name so I can get the function itself and then I change it by doing:
var funcText = window.updateFunc.toString();
funcText = funcText.replace('doSomeOtherFunction(','doMyFunction(');
How do I update the actual function so it will do all that it did before except it will now call doMyFuntion()?
The following is an example to help visualize what I want to do, the actual function I need to change is very complex. I have:
function updateFunc(whatToUpdate,true){
... - do lots of stuff.
var retVal = doSomeOtherFunction(whatToUdate);
... - do lots of stuff based on retVal
}
I need to change this to:
function updateFunc(whatToUpdate,true){
... - do lots of stuff
var retVal = doMyFunction(whatToUdate);
... - do lots of stuff based on retVal, I have had a chance to change retVal
}
Then the first thing my function will do is call doSomeOtherFunction() check/change the returned value and subsequently return the value to the updateFunc().
I have tried to manipulate the funcText above to:
funcText = 'window.updateFunc = function(...';
eval(funcText);
Without success.
This may be closed enough to what you are looking for.
Assuming you have this original function:
function originalFunc(val) {
// this function converts input string to upper case
return val.toUpperCase();
}
Now you want to override it to something either before or after you execute that function (in this example, we execute before, of course before or after doesn't matter in this case).
// we preserve orignal function
var originalFunc_save = originalFunc;
// now we override the original function with this block
var originalFunc = function(text) {
// lets call the orignal function
text = originalFunc_save(text);
// now do our custom thing
return text.split('').reverse().join('');
}
So our test should work.
var text = 'This is a test';
console.log(originalFunc(text));
Output:
TSET A SI SIHT
This method also works if you have to override functions inside a class. The only thing we have to be careful of is to choose a saved name that doesn't interfere with the original class code. _save may not be good enough, but you get the idea.
UPDATE: I'm updating this code above to use a string variable pointing to the original function. I think this is what the OP wanted.
Original code which defined by some library
function originalFunc(val) {
// this function converts input string to upper case
return val.toUpperCase();
}
Now we use the func string variable to point to that function and execute it.
var text = 'This is a test';
var func = 'originalFunc';
text = window[func](text);
console.log(text);
Output: Of course we get the original intended result because we haven't overridden it.
THIS IS A TEST
Now we write our code to override the original function behavior using a string pointing to the function.
// let's define a new function string
var funcSaved = func + '___saved';
// now preserve the original function code
window[funcSaved] = window[func];
// override the original function code block
window[func] = function(text) {
// lets call the orignal function
text = window[funcSaved](text);
// now do our custom thing
return text.split('').reverse().join('');
}
// let's test the code
text = 'This is a test';
text = window[func](text);
console.log(text);
Output:
TSET A SI SIHT
You can make a clone of updateFunc function, edit it at your discretion and work with it in what follows.
function updateFunc(whatToUpdate, param){ // the initial function
...
var retVal = doSomeOtherFunction(whatToUpdate);
return retVal;
}
// formation of unnamed function as string
var newfunc = updateFunc.toString().replace('function updateFunc', 'function ').replace('doSomeOtherFunction(', 'doMyFunction(');
function doMyFunction(whatToUpdate){ // your new function, just for example
console.log(parseInt(whatToUpdate) * 10);
}
var newUpdateFunc;
// declaring new version of 'updateFunc' function
// which is stored in 'newUpdateFunc' variable
eval("newUpdateFunc = " + newfunc);
newUpdateFunc(3); // outputs '30'
I believe this is a valid use case for the forgotten JavaScript with feature.
Basic idea: you call original updateFunc supplying your own version of doSomeOtherFunction to it using with namespace injection:
function updateFunc(whatToUpdate,true){
... - do lots of stuff.
var retVal = doSomeOtherFunction(whatToUdate);
... - do lots of stuff based on retVal
}
function patchUpdateFunc() {
var original_doSomeOtherFunction = window.doSomeOtherFunction;
var original_updateFunc = window.updateFunc;
function doMyFunction() {
// call original_doSomeOtherFunction() here,
// do your own stuff here.
};
window.updateFunc = function() {
with ({doSomeOtherFunction: doMyFunction}) {
return original_updateFunc.apply(this, arguments);
}
}
}
patchUpdateFunc();
I think you are going at this way too complicated.
If you only have doMyFunction and doSomeOtherFunction to switch between, you could just create a flag somewhere telling you to use one or the other when used in an if-statement.
If you want to call a function with a name you do not know beforehand and you only get a name during runtime, you could either accept the function to call as a parameter or accept the name of the function as a parameter and call it like so: var retVal = window[functionName](); (assuming functionName is a property of the window object).
I would highly recommend directly accepting a function as a parameter since the function may not be defined in a global scope.
EDIT:
After your clarification, I think, I can give you a satisfying answer:
if you have a string like var functionString = "function updateFunc(whatToUpdate){var retVal = doMyFunction(whatToUpdate);}";
You can define a function using a Function object:
window.updateFunc = new Function("whatToUpdate", "return (" + functionString + ")(whatToUpdate)");
This will replace the already existing function and you can give it any valid function string you want as long as you know and specify the arguments.
If I understood correctly, you want to override the external function. You can achieve that with the following code
//Someone else's function
function externalFunction(foo){
return "some text";
}
//Your function
function myFunction(value){
//Do something
}
//Override
var externalFunction = (function(){
var original = externalFunction; //Save original function
return function(){
var externalFunctionReturnValue = original.apply(this, arguments);
return myFunction(externalFunctionReturnValue);
}
})();
I strongly sugest not to use eval, but since you want to parse javascript from string:
function hi(){alert('hi');}
function changedHi(){
hi(); // I get an alert box with hi
newHi = "window['hi'] = function(){alert('hi there');}"
eval(newHi);
hi(); // I get an alert box with hi there
window.setTimeout('hi()',500); // I get an alert box with hi there
}
window.setTimeout('changedHi()',500);
UPDATE:
This code snippet works which is your original code:
<script type="text/javascript">
function doImmFeedback(foo){
console.log("DoImmFeedback: " + foo);
}
function Update_qu7260() {
console.log("Some code")
doImmFeedback('qu7260');
}
</script>
<script type="text/javascript">
var qu7260 = new Object();
qu7260.updFunc = Update_qu7260;
var qObj=[qu7260];
var updFunc = qObj[0].updFunc.toString();
if(updFunc.indexOf('doImmFeedback(')!=-1){
updFunc = updFunc.replace('doImmFeedback','doImmQuestionFeedback'); // do my function
updFunc = updFunc.replace('function ',''); // remove the word function
var funcName = updFunc.substr(0,updFunc.indexOf('(')); // get the function name e.g. Update_qu7260
updFunc = "window['" + funcName + "']=function" + updFunc.replace(funcName,'');
console.log(updFunc);
eval(updFunc);
}
function doImmQuestionFeedback(foo){
//Your function
console.log("doImmQuestionFeedback: " + foo);
}
Update_qu7260(); //This executes your doImmQuestionFeedback
</script>
So if your function isn't running, your function isn't in the global scope, or something else is happening, and we can't know if don't have any more info. Check your developer's console for javascript errors.

How can I avoid passing null parameters to a function with multiple optional parameters?

I have a function that looks something like this:
function f(requiredParamA, requiredParamB, optionalObjectParamA, optionalObjectParamB) {
optionalObjectParamA = optionalObjectParamA || {};
optionalObjectParamB = optionalObjectParamB || {};
// rest of the function
}
Say I want to call f with a value for the optionalObjectParamB, but not for optionalObjectParamA. I could do this:
f("john", 100, null, {vegatarian: true});
But that makes for an ugly API.
Is there any other option? How should I design the function and how should I call it?
Pass an object rather than parameters. Then you can have unlimited optional parameters without blowing up your parameter list.
function f(requiredParamA, requiredParamB, context) {
var optional = context || {};
// handle optional.optionalObjectParamA..
// handle optional.optionalObjectParamB..
}

javascript - issue with using .apply on functions

Okay so I have an object and I want to apply a callback function to all of the methods in the object. This is what I have tried so far:
var namespace = {
foo : 'bar',
foobar : function() { console.log('call from foobar!')},
someFunc : function() { console.log('call from someFunc!')},
someFunc2 : function() { console.log('call from someFunc2!')}
}
var logger = {
_callback : function () {
console.log('call from logger!',arguments);
}
}
for (var m in namespace) {
if ( namespace.hasOwnProperty(m) && (typeof namespace[m]=='function') ) {
logger[m] = namespace[m];
namespace[m] = function() {
logger._callback(arguments);
logger[m].apply(this, arguments);
}
}
}
namespace.foobar('foo');
namespace.someFunc('bar');
namespace.someFunc2('bar2');
This is what is getting logged to the console:
call from logger! [["foo"]]
call from someFunc2!
call from logger! [["bar"]]
call from someFunc2!
call from logger! [["bar2"]]
call from someFunc2!
As you can see, for some reason all 3 methods of namespace are outputting 'call from someFunc2! which is wrong. I'm not sure what the issue here is.. what am I doing wrong?
Try
for (var m in namespace) {
if ( namespace.hasOwnProperty(m) && (typeof namespace[m]=='function') ) {
logger[m] = namespace[m];
(function(index){
namespace[index] = function() {
logger._callback(arguments);
logger[index].apply(this, arguments);
};
})(m);
}
}
otherwise the namespace[m] = function(){} will use whatever m is last
There's just one "m". The code inside that function you create in the for loop references the "live" value of "m", not a value frozen at the point the function was created. The last value it takes on is name "someFunc2", so that's the one that's called.
Step by step:
You create the "namespace" and "logger" objects.
The loop runs. The variable "m" takes on the successive values of the properties in the "namespace" object, and creates a new function for each relevant property of that object.
At the end of the loop, "m" has the value "someFunc2".
You call one of the "namespace" functions. That'll be a call to one of the functions created in the loop. That function will in turn call the "_callback" function. And now the important key point: it references a property of the "logger" object using the value of "m". What is the value of "m"? It's "someFunc2".

Dynamic javascript function declaration

I'm working on a code where I must pass a different function to some objects.
In this case, I'm trying to pass a different function for the onchange event. So currently what I got is something like this this:
var ArrayList; //Contains some data to use with ObjectArray format { n: data }
var ObjectArray; //Contains several objects format Array[n] = Object;
for(var key in ArrayList){
var doFunction = function() {
Object[key].doSomething(ArrayList[key]);
}
Object[key].onchange = doFunction;
}
The problem here I believe is that I'm afraid it will execute the code as it is declared and not with the values of the actual variables.
Is there a way to pass the function with the values as it executes? or will the variables get parsed the way its written?
It's the classic function in a loop problem. You need to understand how closures work.
Read the "Example 3" part of this answer carefully. The whole How do JavaScript closures work? question, too.
Another example that might help understand intuitively:
var key = 5;
var onchange = function () {
console.log(key);
};
onchange(); // 5
key = 10; // the loop reassigns the key on each iteration
onchange(); // 10
This is how it should be done:
var ArrayList; //Contains some data to use with ObjectArray format { n: data }
var ObjectArray; //Contains several objects format Array[n] = Object;
for(var key in ArrayList)
{
(function(key)
{
var doFunction = function()
{
Object[key].doSomething(ArrayList[key]);
}
Object[key].onchange = doFunction;
}(key))
}

Categories

Resources