Search code examples
angularngforangularjs-track-by

ngFor trackBy stil re-rendering DOM?


I activated trackBy in my ngFor and i confirmed that it is called and working, but I still notice the DOM is re-rendered in my browser which cause the rows to flicker. What is happening here? In Angular1 I can see in my browser/chrome debugger that the DOM isn't updated/replaced when using track by item.id and no flicker occurs, why isn't this the case in angular2. Is there some error in my code or does stuff happen under the hood that I am not aware off actually doing something good?

    <tr *ngFor="let item of items| async; trackBy:itemTrackBy">

    itemTrackBy(index: number, item: MyItem) {
        return item.id;
    }

EDIT I tracked the error to be in the observable in my API service causing this, but still don't know why.

this.items= this.apiService.getItems(searchText).share();


getItems(search?: string): Observable<Item[]> {
    let searchParams = new URLSearchParams();
    if (search) {
        searchParams.set('searchText', search);
    }
    return this.http
        .get(API_BASE_URL + '/items', {search: searchParams})
        .map(response => response.json())
        .catch(this.handleError);
}

Solution

Solved with using the same observable this.items instead of replacing it with a new one each time I reload new data.

this.items = Observable.concat(Observable.of(''), this.searchInput.valueChanges)
            .debounceTime(200)
            .map((value:string) => value.trim())
            .distinctUntilChanged()
            .switchMap(search => this.apiService.getItems(searchText);

Solution

  • I'm not sure how you implement the change function but I have implemented this code and it works fine:

    export class AppComponent {
      episodes = Observable.of([
        { title: 'Winter Is Coming', id: 0 }
      ]);
    
      add() {
        this.episodes =  Observable.of([
          { title: 'Winter Is Coming', id: 0 },
          { title: 'The Kingsroad', id: 1 }
        ])
      }
    
      itemTrackBy(index: number, item) {
        return item.id;
      }
    }
    
    <button (click)="add()">Add</button>
    <ul>
      <li *ngFor="let episode of episodes | async; trackBy: itemTrackBy">
        {{episode.title}}
      </li>
    </ul>
    

    When you click on the add button you will see in the devtools -> inspect element only the added item flicker.

    Working plunker