Idsa Idsa - 3 months ago 6
jQuery Question

AngularJs ng-repeat with track by not updated

I have ng-repeat with track by clause (to optimize performance). I also have a custom directive which I use to process every tr:

<tr ng-repeat="item in Products track by item.Id"
on-row-rendered="rowRendered">

app.directive('onRowRendered', function($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
debugger;
$timeout(function() {
scope.$emit(attr.onRepeatDone, element);
});
}
}
};
});


It works nice during the loading time (when Products are set first). But when model is changed (e. g. one of the items is updated) onRowRendered's link method is not called (I put debugger call there to check it out) while bindings are updated and html is correctly rerendered.

I expected AngularJs to call it at least for those items that were updated. Was I wrong in my expectations? What should I do instead?

Answer

Today I've been digging into the code of the ngRepeat controller and I'm sorry to say, but this code won't work for the items that are being updated because the ngRepeat controller won't destroy the scope of the updated items, it will update the scope properties though ($index, $first, $last, etc), but it won't destroy the scope unless the item needs to be removed.

Your code would only work if all the scopes of all the items of the repeat were being destroyed and recreated every time that you make a change to the collection, but that is not how the ngRepeat controller operates.

But you may want to try this instead:

    .directive('onRowRendered', function($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {                                
            scope.$watch('item', function(){                      
                $timeout(function() {scope.$emit(attr.onRowRendered, element);});
            }, true);                
          }
        }
    });

This should $emit every time that an item is added or changed.

Example