2

I'm coming from Knockout and I'm trying to understand how Angular updates the scope. I'm a bit confused as to why a function defined on the scope (e.g. $scope.doStuff = function()) gets executed on every single scope refresh.

Plnkr link: http://plnkr.co/edit/YnvOELGkRbHOovAWPIpF?p=preview

For example:

HTML

<div ng-controller="one">
  <input type="text" ng-model="a">
  {{b()}}
</div>

JS

angular.module('test', [])
 .controller('one', ['$scope', function ($scope) {
   $scope.a = 'one';
   $scope.b = function () {
     console.log('b has executed');
   }
}])

So whenever an event happens in the input form field for $scope.a, function $scope.b gets executed. Why does that happen? There are no dependencies on that function, it seems inefficient that it is always refreshing.

If I add another controller in the same structure, like such:

HTML

<div ng-controller="one">
  <input type="text" ng-model="a">
  {{b()}}
</div>
<div ng-controller="two">
  <input type="text" ng-model="x">
  {{y()}}
</div>

JS

angular.module('test', [])
.controller('one', ['$scope', function ($scope) {
  $scope.a = 'one';
  $scope.b = function () {
    console.log('b has executed');
  }
}])

.controller('two', ['$scope', function ($scope) {
  $scope.x = 'two';
  $scope.y = function () {
    console.log('y has executed');
  }
}])

Every time I update $scope.a in controller one the output is:

b has executed
y has executed

Why is controller two executing $scope.y? I thought creating a new controller creates a new child scope. Is it because the child scopes are linked to the parent scope?

More interestingly, if I update $scope.x in controller two then the output is:

b has executed
y has executed
b has executed <-- a second time... what?

Why does function $scope.b get executed a second time?

So why do functions in Angular get executed on every scope refresh?

  • angular must be sure that not changed view, so it execute all function that you use inside view, and also check chenged or not all variables used inside view – Grundy Apr 06 '15 at 15:47
  • So if I have a `ng-repeat` with 1000 items, and each one has a `ng-show="function()"` that means that every single time I update a text field, Angular will execute that function 1000 times? That seems terrible performance wise. –  Apr 06 '15 at 15:56
  • 1
    so not use it with 1000 items :-) – Grundy Apr 06 '15 at 15:57
  • 3
    Oh, so don't use Angular.JS? Okay, got it. :P –  Apr 06 '15 at 16:03
  • 1
    if you can provide sample what you try, possibly we help you reduce items or function calling. Also if your function for one parameter set can return only one constant value you can use **memoization**: _save result function for concrete params, and return it if params not changed_ – Grundy Apr 06 '15 at 16:07
  • @Grundy is right, it's a bad idea to use a function on 1000 item, it's not the framework's fault if the developper writes non performant code ! – Khalid Apr 06 '15 at 16:09
  • As I said in the original question, I am coming from Knockout. In Knockout you can have 1000 items and bind a function to each one, but the functions will only execute if a dependency inside the function changed. I see now the different approaches in Angular vs Knockout. –  Apr 06 '15 at 16:22

3 Answers3

2

Angular uses what is called dirty checking. In order to maintain the binding between the view and controller, any variable which is tied to a function must be validated.

Using like you have demonstrated is generally a bad idea and can effect performance of a medium to large scale app.

Using fixed variables to bind to the view and changing when required is recommended, this will lead to greater performance overall and only re-render the parts that have changed.

In general you don't 'call' the function from the view, but sometimes this is the only way if using dynamic data in a ng-repeat then I would place that data into a object/array and return that object/array, then even tho angular will continue to call the function on it's digest cycle it won't 'update' the view if not changed.

Dave Jones
  • 344
  • 1
  • 7
  • 1
    Oh... I see! This makes sense. So another way to accomplish what I'm trying to do is evaluate the function in a different context and store the answer in the scope, after which Angular will pick up the change via dirty checking. Thanks! –  Apr 06 '15 at 16:50
1

Here I get think so, each time the page load, angular js initiates that function, that means each time the page load the will be executed so instead calling directly, call it using the ng-change.

<div ng-controller="one">
  <input type="text" ng-model="a" ng-change=b()>

 </div>
 <div ng-controller="two">
  <input type="text" ng-model="x" ng-change=y()>
</div>

And in controller you can assign that function to your required ng-model as follows,

angular.module('test', [])
.controller('one', ['$scope', function ($scope) {

  $scope.b = function () {
 $scope.a = 'one';

console.log('b has executed');
  }
}])

.controller('two', ['$scope', function ($scope) {

 $scope.y = function () {
  $scope.x = 'two';
  console.log('y has executed');
   }
  }]) 

Or else you can also return the function value to the your ng-model by assiging to ng-model and it will give your correct answer rather than calling each time.

Nitin Agarwal
  • 943
  • 1
  • 13
  • 26
0

simply because it's impossible to know what are all the dependencies on a function. let's say your function b ( on controller one) would be like this :

  $scope.b = function () {
    console.log('b has executed');
    another_func($scope);
  }

and the definition of the function another_func would be like this :

function another_func (obj) { 
    obj.a = 'something';
    return obj.a;
}

how can you programmatically know that the function $scope.b would call a function that will call another function to get a value that depends on $scope.a ?

Khalid
  • 4,730
  • 5
  • 27
  • 50
  • That makes sense, thanks for the explanation. Perhaps this is outside the scope of this question, but is there any way to limit when a function gets executed? In Knockout you can trigger a function to execute only when a dependency in the function changed, is there any similar way to achieve the same thing in Angular? –  Apr 06 '15 at 16:26