Reputation: 1642
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
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:
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
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
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