Victor Laerte Oliveira Victor Laerte Oliveira - 13 days ago 9
Javascript Question

Function inside a template is called many times (Angular JS)

Today I had to fix a performance issue caused by this code: Pay attention to updateStats called inside the template

<script type="text/ng-template" id="entityGrouper">

<section>

<div>
<ul ng-click="hideEntityBox = !hideEntityBox">
<li>
{{ entityNode.name }}
</li>
<li ng-repeat="breadcrumbItem in entityNode.breadcrumb">
{{ breadcrumbItem }}
</li>
</ul>
{{ updateStats(entityNode) }}
<span ng-include="'/mypath/views/resume.html'"></span>
</div>

<div>

<div ng-repeat="entityNode in entityNode.group" ng-include="'entityGrouper'"></div>
<div ng-repeat="entity in entityNode.group" ng-include="'entityBox'"></div>

</section>

</script>


Template use:

<div ng-repeat="entityNode in entityNode.group" ng-include="'entityGrouper'"></div>


After debugging this code I discovered that this function was called many more time than the array size (my array has 4 objects and the function was called more than 100 times), even mouse hover called this function. I fixed that by just putting a ng-init inside the template and now it's working properly, but I didn't figure out why this function has been called so many times. Is there something about two way data binding?

Answer

This is happening not because of two-way data binding (it is applicable to form inputs), but rather because of angular change detection. Angular periodically checks if any of the values, bound to a template, changed, and if so - updates its value in the view. Typically, it is triggered by user generated events. In order to check if the value of updateStats(entityNode) changed, Angular evaluates it, which causes the performance hit.

To fix this, you may use previously mentioned one-time binding updateStats(entityNode) if the result is set once and will never change in the future. I guess, this is your case, and you have already moved the evaluation to ng-init. For values that update with time, it would be better to create a separate property in entityNode like entityNode.stats, display it in the template and run the updateStats(entityNode) in your controller or service only when necessary. By doing that, you will prevent running updateStats(entityNode) on each digest cycle (you have already seen how often that is) and thereby improve your app's performance.