Search code examples
angularsortingngforangularjs-track-by

sorting an *ngFor array with trackBy in Angular 4


i'm having trouble sorting and array that has a trackBy function. The use case is as follows:

I have an item array. All these items have a z-index property. I also have a layer manager that can edit the z-index of each item. When I want to save my items, I want to sort my array according to the z-index property of each item. I also have a trackBy on my ngFor to limit the amount of change detection on the *ngFor.

With a trackBy function on my *ngFor, the items switch places when I sort the array (which is bad). If I remove the trackBy function this doesn't happen, and the items are sorted according to z-index.

The *ngFor:

<div class="item" *ngFor="let item of items; let i = index;trackBy:getUniqueIdentifier">

The trackBy function:

getUniqueIdentifier(index, item) {
    return this.items.length + ', ' + this.languages._previewLanguage;
}

As you can see, the *ngFor is changed when the length of the items changes or if I switch my language settings. I've tried adding + ', ' + item.zIndex but this doesn't work.

The sorting function:

sortItemsArrayByZIndex() {
    this.items.sort((i1, i2) => {
        return (i1.zIndex - i2.zIndex);
    });
}

So it seems that trackBy and this sort function are in conflict for some reason. The result now is that when i use the sort function the the items array gets sorted according to z-index but the items also switch places, which means that their dimensions properties are switched for some reason.

What i want is that the items array is sorted according to z-index but that the items keep their dimensions.

How do i need to change my trackBy function or my sort function so that i get the result that i want?


Solution

  • If you don't mind, I'll add my own answer to explain why your code wasn't working, since you only provided a working code with no explanation.

    The trackBy function

    The trackBy function is made to improve performances, and to avoid unecessary change detection. You can provide a custom function, as you did, and it will create a value that will be checked to see if the change detection need to be triggered.

    The issue

    Your trackBy function was this :

    getUniqueIdentifier(index, item) {
      return this.items.length + ', ' + this.languages._previewLanguage;
    }
    

    This will return a value of this type :

    90, en-US
    

    Where 90 won't change unless you push/withdraw items from your array, and where en-US can be applied to several items of the array. This means that if you change either the index or another field, the change detetction won't happen.

    The solution

    Since you want to sort your array, you have to at least add the index to the return of your custom function. This means that if the index of the item changes (and since you sort your array, it will), the change detection will occur.

    The minial code would be :

    getUniqueIdentifier(index, item) {
      return index + ', ' + this.languages._previewLanguage + ', ' + this.items.length;
    }