简体   繁体   中英

ng2-dragula after adding new item it's getting displayed at the top

I am using ng2-dragula for drag and drop feature. I am seeing issue when I drag and drop first element(or any element) at the end and then try to add new item to the array using addNewItem button, new item is not getting added to the end. If i don't drop element to the end, new item is getting added at the end in UI. I want new items to be displayed at the bottom in any scenario. Any help is appreciated. This issue is not reproducible with Angular 7. I see this happening with Angular 9

JS

export class SampleComponent {

  items = ['Candlestick','Dagger','Revolver','Rope','Pipe','Wrench'];
  constructor(private dragulaService: DragulaService) { 
    dragulaService.createGroup("bag-items", {
      removeOnSpill: false
    });
  }

  public addNewItem() {
    this.items.push('New Item');
  }
}

HTML

<div class="container" [dragula]='"bag-items"' [(dragulaModel)]='items'>
    <div *ngFor="let item of items">{{ item }}</div> 
</div>

<button id="addNewItem" (click)="addNewItem()">Add New Item

在此处输入图像描述

I edited the stackblitz from the comment to help visualize the issue. This seems to be triggered when a unit is dragged to the bottom of the list. Updated stackblitz: https://stackblitz.com/edit/ng2-dragula-base-ykm8fz?file=src/app/app.component.html ItemsAddedOutOfOrder

You can try to restore old item position on drop.

constructor(private dragulaService: DragulaService) {
  this.subscription = this.dragulaService.drop().subscribe(({ name }) => {
    this.dragulaService.find(name).drake.cancel(true);
  });
} 

Forked Stackblitz

Explanation

There is some difference between how Ivy and ViewEngine insert ViewRef at specific index. They relay on different beforeNode

Ivy always returns ViewContainer host(Comment node) ref if we add item to the end:

export function getBeforeNodeForView(viewIndexInContainer: number, lContainer: LContainer): RNode|
    null {
  const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1;
  if (nextViewIndex < lContainer.length) {
    const lView = lContainer[nextViewIndex] as LView;
    const firstTNodeOfView = lView[TVIEW].firstChild;
    if (firstTNodeOfView !== null) {
      return getFirstNativeNode(lView, firstTNodeOfView);
    }
  }

  return lContainer[NATIVE]; <============================= this one
}

ViewEngine returns last rendered node(last <li/> element) ref

function renderAttachEmbeddedView(
    elementData: ElementData, prevView: ViewData|null, view: ViewData) {
  const prevRenderNode =
      prevView ? renderNode(prevView, prevView.def.lastRenderRootNode!) : elementData.renderElement;
  ...
}

The solution might be reverting the dragged element back to original container so that we can let built-in ngForOf Angular directive to do its smart diffing.

Btw, the same technique is used in Angular material DragDropModule . It remembers position of dragging element and after we drop item it inserts it at its old position in the DOM which is IMPORTANT.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM