Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Angular run controller function twice for each model change?

I have Angular application made of following tiers:

  • service() used for computations and data-munging
  • factory() used as common data storage for multiple controllers
  • few controllers()

My controller exposes function from factory that, in turn, calls function from service. In HTML, I run controller function and display output to user: {{ controller.function() }}.

I have noticed that when page is loaded, and on every subsequent model change, controller.function() is run twice. Why does it happen? How can I avoid unnecessary invocation?

See working example - open your browser JS console, click Run and observe that console.log() line is executed two times.


JavaScript

angular.module('myApp',[])
.service('Worker', [function() {
    this.i = 0;
    this.sample = function(data) {
    console.log(this.i.toString() + " " + Math.random().toString());
    return JSON.stringify(data);
  };
}])
.factory('DataStorage', ['Worker', function(worker) {
    var self = this;
    self.data = [{}, {}];
    self.getData = function() {
      return self.data;
    }
  self.sample = function() {
    return worker.sample(self.data);
  };
  return {
    getData: self.getData,
    sample: self.sample
  };
}])
.controller('MainController', ['DataStorage', function(DataStorage) {
    var self = this;
    self.data = DataStorage.getData();
  self.sample = DataStorage.sample;
}])
.controller('DataSource', [function() {
  var self = this;
  self.data = ["one", "two", "three", "four", "five", "six"];
}])

HTML

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <script data-require="[email protected]" data-semver="1.5.8" src="https://opensource.keycdn.com/angularjs/1.5.8/angular.min.js"></script>
    <script src="script.js"></script>
  </head>

  <body ng-controller="MainController as main">
    <div ng-controller="DataSource as datasource">
      <div ng-repeat="select in main.data">
        <select ng-model="select.choice" ng-options="value for value in datasource.data">
          <option value="">-- Your choice --</option>
        </select>
      </div>

    <pre>
      {{ main.sample() }}
    </pre>
  </div>
  </body>

</html>

Why does this function run multiple times for every single model change and how can I ensure that it runs only once?

I have tried assigning factory function output to controller variable (and use {{ controller.function }} in HTML - note lack of parentheses), but then function is run only once ever. It should run on new data when model is changed.

Similar problems reported on StackOverflow all refer to ng-route module, which I am not using.

like image 841
Mirek Długosz Avatar asked Dec 25 '16 22:12

Mirek Długosz


1 Answers

This is happened because you are calling function sample() in expression like this:

{{ main.sample() }}

This function will be called at least twice if you use expression like that.

Reason is because Angular runs digest cycle until everything is up to date (So, it will be at least twice). In your case it run first time to return value and second time to verify are there any changes left.

You can ensure that it runs only once if you place your function in ng-change directive instead.

In your case it will be:

 <select ng-model="select.choice" ng-change="main.sample()" ng-options="value for value in datasource.data">
      <option value="">-- Your choice --</option>
 </select>
like image 70
kat1330 Avatar answered Nov 19 '22 20:11

kat1330