Angular.js: How does $eval work and why is it different from vanilla eval?
Asked Answered
N

3

154

I was curious about the $scope.$eval you so often see in directives, so I checked out the source and found the following in rootScope.js:

  $eval: function(expr, locals) {
    return $parse(expr)(this, locals);
  },

$parse appears to be defined by ParseProvider in parse.js, which appears to define some kind of mini-syntax of its own (the file is 900 lines long).

My questions are:

  1. What exactly is $eval doing? Why does it need its own mini parsing language?

  2. Why isn't plain old JavaScript eval being used?

Newsboy answered 27/3, 2013 at 23:24 Comment(1)
$eval evaluates an Angular expression against/on the current scope.Autrey
H
187

$eval and $parse don't evaluate JavaScript; they evaluate AngularJS expressions. The linked documentation explains the differences between expressions and JavaScript.

Q: What exactly is $eval doing? Why does it need its own mini parsing language?

From the docs:

Expressions are JavaScript-like code snippets that are usually placed in bindings such as {{ expression }}. Expressions are processed by $parse service.

It's a JavaScript-like mini-language that limits what you can run (e.g. no control flow statements, excepting the ternary operator) as well as adds some AngularJS goodness (e.g. filters).

Q: Why isn't plain old javascript "eval" being used?

Because it's not actually evaluating JavaScript. As the docs say:

If ... you do want to run arbitrary JavaScript code, you should make it a controller method and call the method. If you want to eval() an angular expression from JavaScript, use the $eval() method.

The docs linked to above have a lot more information.

Humbert answered 27/3, 2013 at 23:36 Comment(7)
You said $eval not actually evaluating JavaScript but if I do $eval("{id: 'val'}") I get a JS object. (Angular 1.0.8)Spheroidal
@Yappli $eval doesn't evaluate JavaScript; it evaluates AngularJS expressions, which are kind of like a safer subset of JavaScript. "{id: 'val'}" is a valid AngularJS expression and should return a valid JS object. See the link above for difference between expressions and regular JS.Humbert
Nice answer but I'm not sure that "e.g. no control flow statements" is accurate. You can do something like this... ng-click="someVal ? someFunc(someVal) : noop" which is a perfectly valid angular expressionMartsen
@CharlieMartin The documentation specifically says "no control flow statements", but your point is valid. However, I definitely wouldn't recommend doing that in an ngClick; that kind of logic almost certainly belongs in the controller.Humbert
Interesting that the docs say that as the support for the ternary operator was intentionally added... github.com/angular/angular.js/blob/master/src/ng/… The ng-click was just a simple example and I agree that logic should be in a controller, but I don't see anything wrong with using a ternary operator in the bracket notation and the angular team obviously doesn't either or they wouldn't have added support for it. I suppose if a correction were to be made, it should happen in the docs before this answer thoughMartsen
I'm not arguing with the usefulness of the ternary operator generally, but just the wisdom of using it in the specific case of API control flow with ng-click. But anyway, the ternary operator was added somewhat recently and when I wrote this answer (and probably when the docs were last upated) it was not supported and there were truly no control flow statements supported. Either way, I updated the answer. :-)Humbert
What you're talking about is called expression sandbox and will be removed on Angular 1.6: angularjs.blogspot.com.ar/2016/09/…Knox
P
22

From the test,

it('should allow passing locals to the expression', inject(function($rootScope) {
  expect($rootScope.$eval('a+1', {a: 2})).toBe(3);

  $rootScope.$eval(function(scope, locals) {
    scope.c = locals.b + 4;
  }, {b: 3});
  expect($rootScope.c).toBe(7);
}));

We also can pass locals for evaluation expression.

Patsy answered 16/12, 2013 at 15:45 Comment(0)
C
3

I think one of the original questions here was not answered. I believe that vanilla eval() is not used because then angular apps would not work as Chrome apps, which explicitly prevent eval() from being used for security reasons.

Convulsant answered 26/8, 2015 at 22:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.