Riscie
Riscie

Reputation: 4639

Confused about why my function gets called by a multiply of the ng-repeat

I have the following (simplified) example where I dont get why my function gets called a lot more often then my ng-repeat renders content. (Code shows the issue better than I can explain it).

(I did read about ng-repeat-start / ng-repeat-end but I can't see how this helps me here.)

Thanks in advance for any helping input!

<table>
<tr>
    <th><strong>Date</strong></th>
    <td ng-repeat="day in days"> 
    <!-- Where days is an array containing 6 dates -->
      {{day | date : 'EEE d'}}
    </td>
</tr>

<tr>
    <th><strong>Participants</strong></th>
    <td ng-repeat="day in days">
        <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
        <!-- This gets rendered 6 times but the function userParticipates() gets called 36 times (6*6?) - Why is that the case?-->
    </td>
</tr>

Update

Thank you for your answer, Woot4Moo! I am sorry, I am still confused:

You say my code generates this:

<span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
<span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
<span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
<span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
<span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
<span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 

But thats actually not what I see. I have the 'User does not participate' only once each row (6 times). It's only the function inside the ng-if which gets called 36 times. I don't see why, because my ng-repeat's don't seem to be nested into each another...?

Upvotes: 0

Views: 1212

Answers (3)

Rob Lucha
Rob Lucha

Reputation: 115

Do not think of ng-repeat as a for loop. Ng-repeat will call the function as many times as needed in the digest cycle not just once per item. Assign a model variable to the ng-if and update it in the controller

Upvotes: 0

Jason Winnebeck
Jason Winnebeck

Reputation: 1274

Since the ng-if condition starts a $watch, you don't know how many times it will be called, but you can be assured it will be called a lot, and each method will be called multiple times in a digest cycle. A digest cycle is triggered any time the page route changes or essentially any event on the page (AJAX completion, button click, etc.) A typical digest cycle will run through each watch at least twice (once for the initial change, and a second time to confirm no further changes). Registering the $watch causes an extra call in itself as well.

It's not atypically for such expressions to be evaluated 100s or 1000s of times in the course of the application.

If your function does anything besides a simple condition check like a == b then you should be calculating the result and caching it. For example in your controller you can have a $watch on userId then when userId changes, you set currentUserParticipating = userParticipates(userId) and then you would use currentUserParticipating in your ng-if.

Upvotes: 3

Woot4Moo
Woot4Moo

Reputation: 24316

It does that because for each day (in your example there are 6) it generates this html:

    <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
    <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
    <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
    <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
    <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
    <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 

You effectively want this:

<div ng-repeat="day in days">
       <td>
            <span ng-if="!userParticipates(1)"><a href="">User does not participate</span> 
   </div>
        </td>

https://stackoverflow.com/a/18156912/205426

<table>
    <tbody ng-repeat="obj in rows">
        <tr ng-repeat="e in obj.row">
            <td>{{e}}</td>
        </tr>
        <tr>
            <td colspan="4">{{obj.description}}</td>
        <tr>
    </tbody>
</table>

Upvotes: 1

Related Questions