5

I have a directive ct-steps-tooltip on an element along with ng-repeat like so:

<div id='courseSteps' 
     class='relative ease {{step.action.toLowerCase()}}' 

     ng-repeat='step in currentItem.userData.steps | filter:{action: filterSteps} track by $index' 
     ng-class='{"completed": step.endDate, "pointer": step.action==="Submit" && !step.endDate}' 
     ng-click='getRelated(step, $index)'

     ct-steps-tooltip 
     ct-step-name="step.stepName" 
     ct-step-description="step.description" 
     ct-action="step.action" 
     ct-value-text="step.valueText" 
     ct-quantity='steps.length' 
     ct-completed='currentItem.steps.completedSteps' 
     ct-start-date='step.startDate' 
     ct-end-date='step.endDate'>

    <div ng-show='step.endDate' class="stepCompleteCheck absolute"></div>
    <div> {{ step.stepName }} </div>
</div>

My goal was to get the directive to re-bind/get called again whenever currentItem.userData.steps changed (I am actually completely clearing currentItem and then reassigning it). This actually works great in this simplified fiddle I made. In the fiddle you can clearly see "I got called!" in the console every time the data is changed. For some reason, the ct-steps-tooltip directive in the real code only gets called the first time, and doesn't get called again when the data changes (I have a similar "I got called" message in the real directive's link function). What am I doing in the real code that has broken this functionality?

EDIT: Noticing that OCCASIONALLY the directive IS called again on data change. Its happening like 10% of the time. My question is now seeming similar to this one.

2
  • So, can we see some of that "real" code you're talking about? How do you change the data in it? Commented Jul 2, 2014 at 20:41
  • The whole thing is too massive to reasonably post here. To change the data I am calling a function on a button click which does:$scope.currentItem = newItem; pretty much how I do it in the fiddle. Commented Jul 2, 2014 at 20:53

1 Answer 1

9

The data is not refreshing because in your ngRepeat expression, you specify track by $index.

When currentItem is updated, the indices of objects in your steps array remain the same even if the actual objects are different.

Therefore angular does not update the repeater because the $index has not changed and we are tracking the step objects by this $index variable. Remove the track by part of the repeater expression and your problem will be solved.

track by is really only useful when you are adding and removing objects to/from an array in the repeater, not when you are changing the entire array. For example, if you have a list of 100 books and you add one to this array (without track by), the entire array of 101 books would be re-rendered. You could use track by book.id to tell angular that the original 100 books in the repeater have not changed and to only render dom for the single book we are adding.

Sign up to request clarification or add additional context in comments.

3 Comments

+1 this is correct. With the track by $index clause, it's not going to destroy/recreate DOM elements unless the size of the watched array changes. That means that it won't re-$compile the ng-repeat's transcluded content for anything that is within an array of the same size. You should probably still use track by because of the efficiencies you gain in doing so, but you might want to track the object by something else, an id, or a hash perhaps.
Awesome. Knew this would be related to some little angular nugget I hadn't gleaned yet. I'm my case I'm only dealing with an array that shouldn't ever be more than 15 items or so, so track by is out!
I actually found another solution to this too. I basically ajax in a template with some angular mark up in it, and $compile it with the directives link scope. This seems to work even with track by $index. Why is that? Here's the fiddle: jsfiddle.net/UX5N4/1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.