Jacques Cornat
Jacques Cornat

Reputation: 1642

Function launched too many times on AngularJS

I have a serious performance problem.

I want to show details in a list, but the function is called too many times.

You can try demo here

Here is HTML code :

<div ng-controller="MyCtrl as ctrl">
<p>Watch your console, then click on a letter. Why "myterious" function is launched so many time ???</p>
<button ng-click="ctrl.setValue('B')">Display B</button>
<button ng-click="ctrl.setValue('C')">Display C</button>
<button ng-click="ctrl.setValue('D')">Display D</button>
  <div ng-repeat="item in ctrl.vitals track by $index">
    <p ng-click="ctrl.toggleDetail($index)" ng-bind="item.name"></p>
    <div ng-show="ctrl.myterious(item.name, ctrl.value)">This should only be display under {{item.name}}</div>
    <div ng-show="ctrl.display[$index]">...</div>
    <br>
  </div>
</div>

And there is my JavaScript code :

var app = angular.module('myApp',[]);

app.controller('MyCtrl', [function() {
    var self = this;

    self.vitals = [
            {name:'A'},
            {name:'B'},
            {name:'C'},
            {name:'D'},
            {name:'E'},
            {name:'F'},
            {name:'G'},
            {name:'H'},
            {name:'I'},
            {name:'J'},
            {name:'K'}
    ];

    self.display = [];
    self.toggleDetail = function(id) {
            console.log("Toggle launched");
        self.display[id] = !self.display[id];
    };

    self.value = 'B';
    self.setValue = function (val) {
        self.value = val;
    }

    self.myterious = function (a, b) {
        console.log(a + ' ' + new Date().getTime());
        if (a === b) {
            return true;
        } else {
            return false;
        }
    }
}]);

I'd like to know where is the problem and how to fix it, because it is a simplification of my original code.

Upvotes: 0

Views: 644

Answers (3)

Austin Pantall
Austin Pantall

Reputation: 106

The reason for all of the function calls is the ng-show binding. Every time the DOM is updated, it runs the mysterious function to determine whether or not to display the item. In order to avoid all of these function calls, your ng-show bindings should be based on something static. See modified example below:

JSFiddle Example

self.vitals = [
      {name:'A', mysterious: false},
      {name:'B', mysterious: true},
      {name:'C', mysterious: false},
      {name:'D', mysterious: false},
      {name:'E', mysterious: false},
      {name:'F', mysterious: false},
      {name:'G', mysterious: false},
      {name:'H', mysterious: false},
      {name:'I', mysterious: false},
      {name:'J', mysterious: false},
      {name:'K', mysterious: false}
    ];

self.setValue = function (val) {
        self.vitals.forEach(function(obj) {
         obj.mysterious = obj.name == val
         })
        self.value = val;
    }

<div ng-repeat="item in ctrl.vitals track by $index">
    <p ng-click="ctrl.toggleDetail($index)" ng-bind="item.name"></p>
    <div ng-show="item.mysterious">
        This should only be display under {{item.name}}  
   </div> </div>

Without knowing the specifics of your goal, there is typically nothing wrong with a lightweight function executing frequently to determine dom visibility. The only time I have seen this become a problem is when this gets out of control. If you have a legitimate business reason for calculating a dom's visibility dynamically, use it as sparingly as possible. Otherwise, do your best to design your visibility logic based on static (non-function) bindings.

Upvotes: 2

Derlin
Derlin

Reputation: 9891

This is because AngularJS re-execute the ng-repeat loop any time the data (here ctrl.vitals) or any flag, variable, etc. used inside the loop might have changed.

Here, your mysterious is inside an ng-if, hence it needs to be executed for each iteration.

There are also more subtleties, which are better explained in this article: Banging Your Head Against an AngularJS Issue? Try This

Upvotes: 2

user2752661
user2752661

Reputation:

To be more precise your function gets called twice the times it would be expected to. As ng-repeat is a directive and directives can be executed twice or more, this is perfectly normal.

This has been answered multiple times, see Why is ng-style function applied twice? for a nice explanation.

Upvotes: 1

Related Questions