Search code examples
javascriptangularjsangularjs-ng-repeatangularjs-track-by

Angular: What do you do when you need to 'track by' but you don't have a unique property to track by?


Say that I'm ng-repeating over images, which is an array of objects that have a src and a caption property.

var images = [
  {src: 'a.jpg', caption: 'a'},
  {src: 'b.jpg', caption: 'b'},
  {src: 'c.jpg', caption: 'c'},
  {src: 'd.jpg', caption: 'd'},
  {src: 'e.jpg', caption: 'e'},
  {src: 'f.jpg', caption: 'f'},
  {src: 'g.jpg', caption: 'g'},
  {src: 'h.jpg', caption: 'h'},
  {src: 'i.jpg', caption: 'i'},
  {src: 'j.jpg', caption: 'j'},
];

Assume that there could be duplicate images, so a src + caption combination doesn't ensure uniqueness. I want to track by, but there doesn't appear to be a way to do it other than $index.

$index seems like a bad solution to me. Imagine that the first image (a) was removed. Then the index of all subsequent images would be changed, and thus the whole list would have to be rerendered. This isn't too bad with a list of 10 items, but it is with bigger lists.

  1. Thoughts?

  2. Also, what if src + caption was ensured to be unique? How can I track by multiple properties?

  3. Does track by work by putting the tracked property onto $$watchers? Like is track by image.id doing something like $scope.$watch(image.id, cb)?


Solution

  • 1) You have it right. $index is the only way to go given your constraints, and it is not optimal.

    2) The syntax is "track by expression". Expression can be any valid angular expression, so

    track by image.src + image.caption
    

    is perfectly valid.

    3) track by doesn't use watchers. the ngRepeat directive maintains an internal map of models and DOM nodes indexed by the track by expression. It tries to reuse DOM nodes (and scopes) where possible. The only thing it $watches is the collection.