5

Is there any way to inject JavaScript code stored in a string in AngularJS controllers dynamically?

var dynamicJS = "function DoSomething(value){var x = 1+1  return 2;}"

I have to dynamically inject the above function into my AngularJS controller and have it be called on selection change of drop-down, the values of which are bound to the AngularJS controller. The reason is the JavaScript function would vary based on my each row of data which I have based on my configuration at an application level. I am aware we can make use of $eval but would like to get some better approaches, if any exist.

Can anyone give me any idea on this?

Note: I am using AngularJS v1.4.5

Sᴀᴍ Onᴇᴌᴀ
  • 8,218
  • 8
  • 36
  • 58
Sasi
  • 279
  • 1
  • 3
  • 12
  • Why are you storing a function as a string in the first place? – AJ Funk Apr 05 '17 at 17:37
  • http://stackoverflow.com/questions/939326/execute-javascript-code-stored-as-a-string – enno.void Apr 05 '17 at 17:39
  • Per my requirement I have to do some data population or some validations based on the data which I have. In some case I dont need to execute any but for some other case I may need to execute it with the configured JS functions for that data. the JS function is not consistent and I can execute it based on configured for the specific data per my requirement. make sense ?. – Sasi Apr 05 '17 at 17:44
  • This is a **very dangerous** thing to do; it is **extremely easy** to create attack vectors into your app by modifying these strings. – Claies Apr 05 '17 at 17:49
  • it is intranet application and not going to outer world. no issues on this for this req. thank you though. – Sasi Apr 05 '17 at 18:55
  • Is there a fixed number of functions that would be used? Could those be defined with the main angular controller code (or in a provider) and utilized based on a certain attribute of the markup or data? – Sᴀᴍ Onᴇᴌᴀ Apr 06 '17 at 04:19

3 Answers3

3

There are multiple ways to achieve this.

  • Function object

    Create a Function, passing the one argument (i.e. value) and functionBody as parameters:

    var dynamicJS = "var x = 1+1;  return 2;"
    var DoSomething = new Function("value", dynamicJS );
    
  • eval()

    While arguably more dangerous1, eval() can be used.

    var dynamicJS = "function DoSomething(value){var x = 1+1  return 2;}"\
    eval(dynamicJS);
    

    Because you mentioned in a comment

    "it is intranet application and not going to outer world. no issues on this for this req."

    this would likely be fine but please read the section below.

    Caution

    From the this section of the MDN documentation about eval():

    Don't use eval needlessly!

    eval() is a dangerous function, which executes the code it's passed with the privileges of the caller. If you run eval() with a string that could be affected by a malicious party, you may end up running malicious code on the user's machine with the permissions of your webpage / extension. More importantly, third party code can see the scope in which eval() was invoked, which can lead to possible attacks in ways to which the similar Function is not susceptible.

    eval() is also generally slower than the alternatives, since it has to invoke the JS interpreter, while many other constructs are optimized by modern JS engines.

    There are safer (and faster!) alternatives to eval() for common use-cases. 2

See a demonstration of these techniques utilized below. Click on the buttons corresponding to each technique to see the output on the console.

var dynamicJS = "function DoSomething(value){var x = 1+1;  return 2;}"
var functionBody = "var x = 1+1;  return 2;";
document.addEventListener('DOMContentLoaded', function() {
  document.getElementById('eval').addEventListener('click', function() {
    eval(dynamicJS);
    console.log('DoSomething(3) -> ',DoSomething(3));
  });
  document.getElementById('function').addEventListener('click', function() {
    var dynamicFunction = new Function("value", functionBody);
    console.log('dynamic function(3) ->',dynamicFunction(3));
  });
});
<button id="eval">click to eval</button>
<button id="function">click to use Function</button>

1https://stackoverflow.com/a/4599946/1575353

2https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Don't_use_eval_needlessly!

Community
  • 1
  • 1
Sᴀᴍ Onᴇᴌᴀ
  • 8,218
  • 8
  • 36
  • 58
2

I would believe the easier way will be to parse the String and then use the function constructor.

Something like this:

var DoSomething = new Function('value', 'var x = 1+1  return 2');
Pritam Banerjee
  • 17,953
  • 10
  • 93
  • 108
1

Perhaps try something like:

function myFunc(obj){
    var param = obj.hasOwnProperty('param') ? obj.param : undefined;
    console.log(param);
}

var funcString = "myFunc({ param: 'something' });",
    Construct = new Function(funcString);

Construct();

Haven't tested it to be honest ... but this way you avoid eval().

more info on Function object

Tim Vermaelen
  • 6,869
  • 1
  • 25
  • 39