ngx-sortablejs
ngx-sortablejs copied to clipboard
Wrong display order on Angular 9
Description
Only a single draggable div
as follows:
<div [sortablejs]="selectedItems" [sortablejsOptions]="selectedItemsOptions">
<button *ngFor="let item of selectedItems; let i = index;" [class.active]="item.isSelected" (click)="remove(item)">{{i + 1}}° {{item.label}}</button>
</div>
If an item is moved to the last position, when another item is addeded via code this.selectedItems.push(item)
the list of items visualized is sorted wrongly.
The array items order is correct, but the visual representation of it is not.
I guess it's some incompatibility between sortablejs
/ngx-sortablejs
and Angular 9
, since if I try to reproduce the issue on a Angular 8
project it doesn't happen.
How to reproduce
I uploaded a sample project here.
The code is very simple, it's just a new Angular application with changes on app.component.ts
and app.component.html
.
If you don't want to download the sample you can just follow these steps:
- Create a new project with:
ng new <project_name>
- Install dependencies:
npm i -S ngx-sortablejs sortablejs
npm i -D @types/sortablejs
- Import
SortablejsModule
inAppModule.ts
- Edit only
app.component.ts
andapp.component.html
according to the sample linked above.
Environment
I tested this on Angular 9.0.7
and Angular 8.2.14
.
Versions mentioned above are those indicated in <app-root ng-version="">
.
The issue shows up only on Angular 9
.
I have the same issue. Any update on this?
A temporary solution is to use the onUpdate hook of the sortable-js options object and overwrite the sortablejs bindingTarget in it. This forces Angular's change detection to run and re-render the ngFor correctly.
// We are in .ts component file
// For this example binding target is called "data"
const sortablejsOptions = {
onUpdate: () => {
const originalData = {...this.data};
this.data = {};
setTimeout(() => {
this.data = originalData;
)}
}
}
The data will then be rendered correctly.
Note: I also tried to omit the Angular2-Binding and use sortablejs directly with Angular, but this leads to the same problem. So maybe this problem is also/more related to the original sortablejs library.
I guess this is the same issue that Dragula faces. It seems to be related to the Ivy compiler. I experienced the exact same issue for Dragula and Sortable.
Here is a solution for Dragula: https://stackoverflow.com/questions/63532041/ng2-dragula-after-adding-new-item-its-getting-displayed-at-the-top/63609337#63609337
Can we somehow apply the same for Sortable? For now Iv'e switched to Dragula but I would love to switch back as soon as this bug is gone or can be worked around.
Hi there;
Can you please try version 3.1.3 and let me know if it' s working for you too or not?
I' m using 3.1.3 on Angular 10, it' s working fine. But later versions have this bug & i cannot update ngx-sortable because of that.
Related topic which i opened is: https://github.com/SortableJS/ngx-sortablejs/issues/202
Same problem here! Angular 10 with ngx-sortablejs 10.1.0.
Problem persists with:
- Angular @ 11.2.3
- ngx-sortablejs @ 11.1.0
Same problem here! Angular 13.1.0 with ngx-sortablejs 11.1.0.
A temporary solution is to use the onUpdate hook of the sortable-js options object and overwrite the sortablejs bindingTarget in it. This forces Angular's change detection to run and re-render the ngFor correctly.
// We are in .ts component file // For this example binding target is called "data" const sortablejsOptions = { onUpdate: () => { const originalData = {...this.data}; this.data = {}; setTimeout(() => { this.data = originalData; )} } }
The data will then be rendered correctly.
Note: I also tried to omit the Angular2-Binding and use sortablejs directly with Angular, but this leads to the same problem. So maybe this problem is also/more related to the original sortablejs library.
i think requestAnimationFrame
is better than setTimeout
Having the same issue. In my case, i am unable/willing to overwrite/rebind the data since its a FormArray originating from the parent component (and i do not want to sever that connection).
As a really rough temporary fix, i'm just hiding the whole element with ngIf for a split second (adding new items is done in a modal window in my solutin, so the flash is not too noticeable)
// we forcefully remove and re-render the sortable element before adding new tiles
hardRefresh() {
// relevant only if the order was changed beforehand
if (this.orderChanged) {
this.forceOrderRefresh = true;
this.orderChanged = false;
this.cdr.detectChanges();
setTimeout(() => {
this.forceOrderRefresh = false;
this.cdr.detectChanges();
}, 0);
}
}
added to the element:
<div
*ngIf="!forceOrderRefresh"
[sortablejs]="mediaArray.controls"
[sortablejsOptions]="sortablejsOptions"
class="ao-media-upload__multi-file-draggable-area"
>
and to make sure that the trick is only used in case drag was used, ive added "orderChanged " into dragend:
onDragEnd(event) {
this.cdr.detectChanges();
this.updateArrayValidators();
this.orderChanged = true;
}
Still experiencing this problem with:
Angular @ 13.3.2 ngx-sortablejs @ 11.1.0
For anyone suffering with this problem, I found this worked for me:
sortableOptions: SortableOptions = {
...other options...
onUpdate: () => {
// Replace this.arrayContent with your data
this.arrayContent = [
...this.arrayContent
];
},
};
It seems like re-assigning the data triggers Angular to re-render the *ngFor section, which means items are displayed in the correct order.
Any news?