3
votes

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?

2
Why would you use a trackBy function there ? You're iterating over non-primitive values, and you're returning a value that can not be unique. What is your purpose with this track by ?user4676340
As i understand it trackBy can be used to determine when ngFor needs to do a change detection i.e. being rebuilt. This piece of code is in a prety big project and for performace reasons i dont want the ngFor to do a change detection every time something changes, so thats why i have the trackBy. But maybe my understanding of trackBy is wrong?Martijn van den Bergh
If the language changes, every item will be concerned, but if any of the other fields (or their index) change, no item will be concerned. That's why your sorting may not be working : since the item indexes are changing and you're not tracking it, then the change detection isn't happening. This is an assumption, but you should probably test it though :)user4676340
SImply add the index into the return statement of the track by, this should do the trick !user4676340
Well glad I could help !user4676340

2 Answers

6
votes

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;
}
0
votes

As user @trichetriche mentioned i needed to add the index, and also the zIndex property to the trackBy to let the TrackBy work as intended.

The trackBy function looks something like this:

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