Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I getting two method calls in Angular JS?

I have the following example:

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
  $scope.testMethod = function() {
    alert('hi');
  }
});
<!DOCTYPE html>
<html>

<head>
  <script data-require="[email protected]" data-semver="1.5.0" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script>
</head>

<body ng-app="myApp">
  <div ng-controller="myCtrl">
    {{testMethod()}}
  </div>
</body>

</html>

I have called the method only once in view template. But why is it executed twice?

like image 942
Ganesh Babu Avatar asked Apr 20 '16 12:04

Ganesh Babu


People also ask

What is$ http in Angular?

Overview. The $http service is a core AngularJS service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP. For unit testing applications that use $http service, see $httpBackend mock. For a higher level of abstraction, please check out the $resource service ...

Can we use AngularJS with ASP NET MVC?

AngularJS can complement the server-side technology, ASP.NET MVC by simplifying and reducing the operations performed by the server. The JavaScript technology supports template engine, dependency injection and routing engine of its own.

What does ng click do?

The ng-click Directive in AngluarJS is used to apply custom behavior when an element is clicked. It can be used to show/hide some element or it can pop up an alert when the button is clicked.

What is AngularJS in asp net?

AngularJS is an open-source JavaScript framework developed by Google. It helps you to create single-page applications, one-page web applications that only require HTML, CSS, and JavaScript on the client side.


1 Answers

When you use functions in binding expressions Angular will reevaluate the expression on every $digest phase. The reason behind this is that functions can return a response but there is no way for Angular to know if the result won't change in the next function call.

Angular ensures that it will show the latest correct up to date value in the only way possible in this case - by calling the function every time there is a change in the scope.

You'll see people calling this "$digest phase". It can occur due to many reasons. For the sake of this explanation I'm simplifying stuff. If you want to know more read https://docs.angularjs.org/guide/scope

As a general rule - avoid binding to a function. Instead remember the function response in a $scope variable and bind to it instead. Binding many times to a function might lead to performance issues when bindings count grows in your project.

EDIT - Answer to sahbeewah comments (see below)

Let's change the post example. Open the console and run the code below.

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
  var i = 0;

  $scope.testMethod = function() {
    alert('hi');

    i++;
    return i; // Return a different value every time
  }
});
<!DOCTYPE html>
<html>

<head>
  <script data-require="[email protected]" data-semver="1.5.0" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
</head>

<body ng-app="myApp">
  <div ng-controller="myCtrl">
    {{testMethod()}}
  </div>
</body>

</html>

For the example purposes it's important for $scope.testMethod to return different value every time it's called.

You'll notice the following:

  • "Hi" alert will be shown multiple times
  • After you see it multiple times the following error will appear:

    Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!

So, what happened? Every time we change the $scope state here angular runs another digest phase until it "stabilizes" (there are no more changes). There is a limit on 10 consecutive $digest phases.

In the thread post angular calls $digest when the controller is attached and then, because we change the $scope, it calls one more digest. If we remove all the bindings from the code only one digest will occur.

We can easily check this. Remove the {{testMethod()}} line from the example and then place a breakpoints in angular code: $digest, line 16700 (angular 1.5.0).
On this line you'll see if ((dirty || asyncQueue.length) && !(ttl--)) {.

Your breakpoint will now be hit only once. Let's look 2 lines above. Angular team wrote an interesting comment there:

// `break traverseScopesLoop;` takes us to here

It's a self explanatory comment and if we go up to line 16629 we'll see

do { // "while dirty" loop

There is a variable in the code named "dirty" that creates a do...while loop. While the scope is dirty (there are changes detected) you'll stay in this loop. A change is any change on any scope variable used in any way by any kind of binding ($watch).

That's why we have 2 $digest phases here.

You can find Angular 1.5 here. For debugging purposes I've switched to the non-minified versions.

like image 84
avladov Avatar answered Oct 25 '22 19:10

avladov