Angular cdkDragDrop 行为随着 Angular 的更新而改变,拖动开始时 UI 不再更新

发布于 2025-01-13 23:33:06 字数 2025 浏览 1 评论 0原文

我们有一个包含可以拖放项目的组件,我们使用 CDK 拖/放模块来管理它。

部分功能是当用户开始拖动时更改底层对象的属性。为了更新 UI,我们调用 ChangeDetectorRef.detectChanges()。

在 Angular 版本 9 上,这工作得很好,但是自从更新到版本 12.1 后,UI 不再更新,直到拖动的项目被放下,我找不到让它工作的方法,

这是该组件的简化版本:

HTML:

<div cdkDropList>
<div cdkDrag 
 (cdkDragStarted)="onCdkDragStarted($event)"
 *ngFor="let step of steps">Step: {{step.id}}  {{step.text}}</div>
</div>

代码:

import { ChangeDetectorRef, Component} from '@angular/core';

@Component({
  selector: 'drag-poc',
  templateUrl: 'drag-poc.component.html',
  styles: []
})
export class DragPocComponent { 
  steps: any[] = [
    { id: 1, text: '' },
    { id: 2, text: '' },
    { id: 3, text: '' },
    { id: 4, text: '' },
    { id: 5, text: '' },
  ];

  constructor(private ref: ChangeDetectorRef){

  }

  onCdkDragStarted($event){
    // this is just a hack to get a reference to the array object that is being dragged
    const draggedItem = $event.source.__ngContext__[8].ngForOf[$event.source.__ngContext__[8].index];

    draggedItem.text = "MOVED";
    this.ref.detectChanges();
  }
}

使用 Angular 9,一旦开始拖动,UI 就会更新:

使用更新的 UI 进行拖动

使用 Angular 12(或 13),代码完全相同,但 UI 不同t 更新,直到拖动的项目被删除:

使用未更新的 UI 进行拖动

有没有办法让 UI 在拖动开始后更新?

Stackblitz Angular 9:https://stackblitz.com/edit/angular-ivy-s4pmmm

StackBlitz 最新 Angular:https://stackblitz.com/edit/angular-ivy-ztdufb

的答案后显示问题

编辑:最新的 Angular StackBlitz 已更新,以在 @Eliseo

We have a component which has items that can be dragged and dropped, we use the CDK drag/drop module for managing this.

Part of the functionality is to change a property of the underlying object when the user starts dragging. To get the UI to update we call ChangeDetectorRef.detectChanges().

When on version 9 of Angular this worked fine, however since updating to version 12.1 the UI no longer updates until the dragged item is dropped and I cannot find a way to get it working

Here is a simplified version of the component:

HTML:

<div cdkDropList>
<div cdkDrag 
 (cdkDragStarted)="onCdkDragStarted($event)"
 *ngFor="let step of steps">Step: {{step.id}}  {{step.text}}</div>
</div>

Code:

import { ChangeDetectorRef, Component} from '@angular/core';

@Component({
  selector: 'drag-poc',
  templateUrl: 'drag-poc.component.html',
  styles: []
})
export class DragPocComponent { 
  steps: any[] = [
    { id: 1, text: '' },
    { id: 2, text: '' },
    { id: 3, text: '' },
    { id: 4, text: '' },
    { id: 5, text: '' },
  ];

  constructor(private ref: ChangeDetectorRef){

  }

  onCdkDragStarted($event){
    // this is just a hack to get a reference to the array object that is being dragged
    const draggedItem = $event.source.__ngContext__[8].ngForOf[$event.source.__ngContext__[8].index];

    draggedItem.text = "MOVED";
    this.ref.detectChanges();
  }
}

With Angular 9 the UI updates once the dragging starts:

Dragging with updated UI

With Angular 12 (or 13), the code is the exact same, but the UI doesn't update until the dragged item is dropped:

Dragging with non-updated UI

Is there a way to get the UI to update once the dragging starts?

Stackblitz Angular 9: https://stackblitz.com/edit/angular-ivy-s4pmmm

StackBlitz Latest Angular: https://stackblitz.com/edit/angular-ivy-ztdufb

EDIT: the Latest Angular StackBlitz is updated to show the problem after the answer from @Eliseo

updated problem

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

你是暖光i 2025-01-20 23:33:06

恐怕您需要创建一个 *cdkDragPreview。还有一个 cdkDrag 它有点?@X##??!!因为您需要考虑几件事:拖动的大小、拖动元素时的点...

为了考虑所有这些,我通常创建两个变量

  style: any = null;
  offset:any=null

并且 cdkDrag 使用 (cdkDragMoved) 和 (mousedown) -您还需要使用 (cdkDragStarted) 和可能的 (cdkDragEnded) 所以你的 .html 变得像

<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
    <div class="example-box" *ngFor="let movie of movies;let i=index" cdkDrag 
    [cdkDragData]="movie"
    (cdkDragStarted)="onDragStarted($event,i)"
    (cdkDragMoved)="onDragMove($event)"
    (cdkDragEnded)="onDragEnded($event,i)"
    
     (mousedown)="setStyle($event)" >{{movie.title}}{{movie.text}}
      <div  *cdkDragPreview class="example-box" [ngStyle]="style">
        {{movie.title}}{{movie.text}}
      </div>
    </div>
  </div>

和 .ts

  drop(event: CdkDragDrop<any[]>) {
    moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
  }
  onDragStarted(event:CdkDragStart<any>,index:number)
  {
      //you can use
      this.movies[index].text="moved"
      //or use
      event.source.data.text="moving...."

  }
  onDragEnded(event:CdkDragEnd<any>,index:number)
  {
    this.movies[index].text=""
  }
  setStyle(event: MouseEvent) {
    const rect = (event.target as HTMLElement).getBoundingClientRect();
    this.style = { width: rect.width + 'px', height: rect.height + 'px'}
    this.offset={x:event.offsetX,y:event.offsetY };
  }
  public onDragMove(event: CdkDragMove<any>): void {
    const el=(document.getElementsByClassName('cdk-drag-preview')[0])as any
    const xPos = event.pointerPosition.x - this.offset.x;
    const yPos = event.pointerPosition.y - this.offset.y;
    el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
  }

请参阅 stackblitz 我如何传递“索引”到函数 (onDragStarted) 和 (onDragEnded) 来更改“对象”数组(您不需要使用像您的复杂的$event.source.__ngContext__[8].ngForOf[$eve....

注意:您还可以检查属性 event.source 或 event.source.element 来获取 HTML 元素,您可以还将属性 cdkDragData 添加到您的 Drag

I'm afraid that you need create a *cdkDragPreview. And a cdkDrag it's a bit ?@X##??!! because you need take acount several thing: the size of the drag, the point when you drag the element...

To take account all of this I usually create two variables

  style: any = null;
  offset:any=null

And the cdkDrag use (cdkDragMoved) and (mousedown) -you also need use (cdkDragStarted) and possible (cdkDragEnded) so your .html becomes like

<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
    <div class="example-box" *ngFor="let movie of movies;let i=index" cdkDrag 
    [cdkDragData]="movie"
    (cdkDragStarted)="onDragStarted($event,i)"
    (cdkDragMoved)="onDragMove($event)"
    (cdkDragEnded)="onDragEnded($event,i)"
    
     (mousedown)="setStyle($event)" >{{movie.title}}{{movie.text}}
      <div  *cdkDragPreview class="example-box" [ngStyle]="style">
        {{movie.title}}{{movie.text}}
      </div>
    </div>
  </div>

And the .ts

  drop(event: CdkDragDrop<any[]>) {
    moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
  }
  onDragStarted(event:CdkDragStart<any>,index:number)
  {
      //you can use
      this.movies[index].text="moved"
      //or use
      event.source.data.text="moving...."

  }
  onDragEnded(event:CdkDragEnd<any>,index:number)
  {
    this.movies[index].text=""
  }
  setStyle(event: MouseEvent) {
    const rect = (event.target as HTMLElement).getBoundingClientRect();
    this.style = { width: rect.width + 'px', height: rect.height + 'px'}
    this.offset={x:event.offsetX,y:event.offsetY };
  }
  public onDragMove(event: CdkDragMove<any>): void {
    const el=(document.getElementsByClassName('cdk-drag-preview')[0])as any
    const xPos = event.pointerPosition.x - this.offset.x;
    const yPos = event.pointerPosition.y - this.offset.y;
    el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
  }

See in the stackblitz how I pass the "index" to the functions (onDragStarted) and (onDragEnded) to change the array of "object" (you needn't use some complex like your $event.source.__ngContext__[8].ngForOf[$eve....

NOTE: You can also check the property event.source or event.source.element to get the HTML element, you can also add a property cdkDragData to your Drag

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文