<p dir="auto">If using <code class="notranslate">trackBy</code>/<code class="notranslate">ngForTrackBy</code> to track the addition/removal of an element to the array being iterated in <code class="notranslate">ngFor</code>, it appears the elements are actually redrawn rather than re-ordered. I'm using 2.0.0-beta.14.</p>
<p dir="auto">Issue can be seen here using the network/Docs tab in chrome console: <a href="http://plnkr.co/edit/qZf6yv?p=preview" rel="nofollow">http://plnkr.co/edit/qZf6yv?p=preview</a></p>
<p dir="auto">Open the Network tab and then remove one of the Heroes near the top of the list. Every hero item below that one will be redrawn, as seen by the iframe being reloaded. This behavior can be repoduced without an iframe present, but this makes it easier to see the behavior.</p>
<p dir="auto">It's possible that I misunderstood the functionality of <code class="notranslate">trackBy</code>, but it was my understanding that it would leave elements in place if they retained the same reference property from the custom track-by function.</p>
<p dir="auto"><strong>EDIT</strong>: I previously thought that the iframes were triggered by a reflow and this somehow caused their content to refresh, but that's apparently not always the case. <a href="https://plnkr.co/edit/RTEHgQ?p=preview" rel="nofollow">This plunker</a> recreates the functionality of adding a node with an iframe child, but the other iframes don't reload their content. It appears that ngTrackBy may not fully be working in 2.0.0-beta.14.</p>
<p dir="auto">Type: bug (or possibly non-optimal behavior)<br>
Plunkr: <a href="http://plnkr.co/edit/WHJs6F4lfkDZNyrQHYbQ?p=preview" rel="nofollow">http://plnkr.co/edit/WHJs6F4lfkDZNyrQHYbQ?p=preview</a><br>
Version (UPDATED): Angular 2 beta 11</p>
<p dir="auto">The current combined behavior for <code class="notranslate">DefaultIterableDiffer</code>/<code class="notranslate">ngFor</code> currently exposes this bad behavior:</p>
<ol dir="auto">
<li>remove the head item in an array</li>
<li>On check, the <code class="notranslate">DefaultIterableDiffer</code> will now produce a "removed" record for the first item and "moved" records for the rest</li>
<li>This leads <code class="notranslate">ngFor</code> to remove all items, and then add back the non-removed items, thereby leading to a lot of unnecessary DOM removals and adds, losing DOM state (such as focused elements/selection etc.) ( see <div class="Box Box--condensed my-2">
  <div class="Box-header f6">
    <p class="mb-0 text-bold">
      <a href="https://github.com/angular/angular/blob/f9fb72fb0e9bcbda7aeebbf8321ce5d70d78ecee/modules/angular2/src/common/directives/ng_for.ts#L106">angular/modules/angular2/src/common/directives/ng_for.ts</a>
    </p>
    <p class="mb-0 color-fg-muted">
         Line 106
      in
      <a data-pjax="true" class="commit-tease-sha" href="/angular/angular/commit/f9fb72fb0e9bcbda7aeebbf8321ce5d70d78ecee">f9fb72f</a>
    </p>
  </div>
  <div itemprop="text" class="Box-body p-0 blob-wrapper blob-wrapper-embedded data">
    <table class="highlight tab-size mb-0 js-file-line-container" data-tab-size="8" data-paste-markdown-skip="">

        <tbody><tr class="border-0">
          <td id="L106" class="blob-num border-0 px-3 py-0 color-bg-default" data-line-number="106"></td>
          <td id="LC106" class="blob-code border-0 px-3 py-0 color-bg-default blob-code-inner js-file-line"> <span class="pl-s1">changes</span><span class="pl-kos">.</span><span class="pl-en">forEachMovedItem</span><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-s1">movedRecord</span>: <span class="pl-smi">CollectionChangeRecord</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> </td>
        </tr>
    </tbody></table>
  </div>
</div>
)</li>
</ol>
<p dir="auto">On a related note, the current diff structure doesn't impose any necessary order between removals, moves, and adds (each having an independent forEach method). (There's just a todo by rado on improving this.) Which means that it's unclear (aside from any documentation notes) in which order they should be applied for indices to be valid. To me it seems much more logical that the <code class="notranslate">DefaultIterableDiffer</code> deduces an <strong>array</strong> of operations to perform on the previous data structure to reach the current state. If you accept that notion, the moves are extraneous and can be removed. Which is something I'd like to propose. Thus, a head removal would just translate to something like:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[{type: 'removed', item: 'A', previousIndex=0, ... }]"><pre class="notranslate"><code class="notranslate">[{type: 'removed', item: 'A', previousIndex=0, ... }]
</code></pre></div>
<p dir="auto">...rather than (the currently implicit through different forEachXXX)</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="{
  removed: [{type: 'removed', item: 'A', previousIndex=0, ... }],
  moved: [
    {type: 'moved', item: 'B', previousIndex=1, currentIndex=0, ... },
    {type: 'moved', item: 'C', previousIndex=2, currentIndex=1, ... },
  ]
}"><pre class="notranslate"><code class="notranslate">{
  removed: [{type: 'removed', item: 'A', previousIndex=0, ... }],
  moved: [
    {type: 'moved', item: 'B', previousIndex=1, currentIndex=0, ... },
    {type: 'moved', item: 'C', previousIndex=2, currentIndex=1, ... },
  ]
}
</code></pre></div>