拖放 cdk Angular material
Drag and drop cdk Angular material
我创建了 1 个带有图像的容器(使用 ngb-carousel)和 3 个用于放置这些图像的放置区,但是当我尝试在它们之间传输项目时遇到了一些问题。首先,每当我尝试将一个项目从我的图像容器移动到任何拖放区时,我总是得到相同的索引。
这是我的模板
<mat-card class="card">
<div
cdkDropList
#todoList="cdkDropList"
[cdkDropListData]="images"
[cdkDropListConnectedTo]="[doneList, intermedio, menos]"
cdkDropListOrientation="horizontal"
class="contenedor">
<ngb-carousel *ngIf="images">
<ng-template *ngFor="let img of images" ngbSlide>
<div class="picsum-img-wrapper" [cdkDragData]="img" cdkDrag>
<img [src]="path+img.imagen" width="100%" alt="Random first slide">
</div>
</ng-template>
</ngb-carousel>
</div>
<div class="results">
<div class="talents">
<div class="example-container">
<h4>(+) Talento más desarrollado</h4>
<div cdkDropList #doneList="cdkDropList" [cdkDropListData]="list2"
[cdkDropListConnectedTo]="[intermedio, menos]"
class="example-list" (cdkDropListDropped)="drop($event)">
<ol class="example-box">
<li *ngFor="let l2 of list2" [cdkDragData]="l2" cdkDrag>
{{l2.nombre}}
</li>
</ol>
</div>
</div>
</div>
<div class="talents">
<div class="example-container">
<h4>Talento Intermedio</h4>
<div cdkDropList #intermedio="cdkDropList" [cdkDropListData]="list3"
[cdkDropListConnectedTo]="[doneList, menos]"
class="example-list" (cdkDropListDropped)="drop($event)">
<ol class="example-box">
<li *ngFor="let l3 of list3" [cdkDragData]="l3" cdkDrag>
{{l3.nombre}}
</li>
</ol>
</div>
</div>
</div>
<div class="talents">
<div class="example-container">
<h4>(-) Talento menos desarrollado</h4>
<div cdkDropList #menos="cdkDropList" [cdkDropListData]="list4"
[cdkDropListConnectedTo]="[intermedio, doneList]"
class="example-list" (cdkDropListDropped)="drop($event)">
<ol class="example-box">
<li *ngFor="let l4 of list4" [cdkDragData]="l4" cdkDrag>
{{l4.nombre}}
</li>
</ol>
</div>
</div>
</div>
</div>
</mat-card>
这是我的放置函数
drop(event: CdkDragDrop<any>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
/*transferArrayItem(event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex);*/
const newArray = event.previousContainer.data.filter(e => e.id !== event.item.data.id);
event.previousContainer.data = newArray;
event.container.data.push(event.item.data)
}
}
我尝试使用 transferArrayItem 但它没有用,因为我得到了错误的索引所以我尝试更改 event.previousContainer.data 和 event.container.data,我使用 event.container.data 但它没有改变 event.previousContainer.data.
知道为什么它不起作用吗?
提前致谢
您的代码几乎一切正常。
问题在于,由于所有带有 cdkDropList
指令的 <div>
容器都没有 children 它们的高度等于零,所以没有区域,其中元素可以删除。
一个可能的解决方案是将 cdkDropList 移到更高的位置,例如 <div class="example-container">
,或者如果您不希望 headers(如 (+) Talento más desarrollado</h4>
)触发拖动事件,那么您需要在下面提供一些高度,以便可以将项目拖到那里。
<div class="results">
<div class="talents">
<div class="example-container" cdkDropList
#doneList="cdkDropList"
[cdkDropListData]="list2"
[cdkDropListConnectedTo]="[intermedio, menos]"
class="example-list"
(cdkDropListDropped)="drop($event)"
>
<h4>(+) Talento más desarrollado</h4>
<!-- Probably no need for this div then !-->
<div>
<ol class="example-box">
<li *ngFor="let item of list2" [cdkDragData]="item" cdkDrag>
{{item.name}}
</li>
</ol>
</div>
</div>
</div>
Moving directive up in DOM tree stackblitz example.
使用这种方法,CdkDragDrop
事件的 previousIndex 始终等于 "images" 数组的长度。我认为可能需要使用 CdkDropListGroup
,但看起来将 <ngb-carousel>
与 DragDropModule 结合使用会出现问题。如果一起使用,则 DragDropModule 无法正确识别拖动项的索引。
如我所料,如果只列出图像,则索引是正确的:
Without ngb-carousel - stackblitz example.
您的示例中的另一个问题是可拖动项目应该是 HTML 元素的直接 children,带有 angular cdkDropList 指令。否则在拖动第二个元素时会出现以下错误:
Error: Failed to execute 'insertBefore' on 'Node': The node before
which the new node is to be inserted is not a child of this node.
说了这么多,现在让我们尝试使用这些知识并将这段代码重构为一个工作示例。
首先是我们的模板:
<div class="talents">
<div class="example-container" >
<h4>(+) Talento más desarrollado</h4>
<ol class="example-box" cdkDropList
#doneList="cdkDropList"
[cdkDropListData]="list2"
[cdkDropListConnectedTo]="[intermedio, menos]"
(cdkDropListDropped)="drop($event)">
<li *ngFor="let item of list2" [cdkDragData]="item" cdkDrag>
{{item.name}}
</li>
</ol>
</div>
</div>
现在一些CSS:
.example-box:empty {
height:50px;
border: 5px dashed #cecece;
}
.example-box:empty:hover:after {
content: 'Drag & Drop items here';
color: #aeaeae;
font-weight: bold;
font-size: 18px;
width:50%;
position: relative;
left:25%;
top:5px;
}
通过在没有 children 的元素上设置高度,我们创建了一个拖放区。添加边框和文本是一个很好的 UX 设计,它让我们的最终用户知道,可以将项目拖到给定区域。
最后但并非最不重要的 TypeScript:
drop(event: CdkDragDrop<any>) {
// indexes are wrong due to ngb-carousel
if (event.previousContainer === event.container) {
// this is okay, no moving around in ngb-carousel, since it always displays one element;
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
let previousIndex = event.previousIndex;
const element = event.previousContainer.element.nativeElement;
const id = element.getAttribute('id');
// Only indexes of items dragged from carousel needs to be fixed
if (id === this.carouselContainerId) {
previousIndex = event.previousContainer.data.indexOf(event.item.data);
}
transferArrayItem(event.previousContainer.data,
event.container.data,
previousIndex,
event.currentIndex);
}
}
我创建了 1 个带有图像的容器(使用 ngb-carousel)和 3 个用于放置这些图像的放置区,但是当我尝试在它们之间传输项目时遇到了一些问题。首先,每当我尝试将一个项目从我的图像容器移动到任何拖放区时,我总是得到相同的索引。
这是我的模板
<mat-card class="card">
<div
cdkDropList
#todoList="cdkDropList"
[cdkDropListData]="images"
[cdkDropListConnectedTo]="[doneList, intermedio, menos]"
cdkDropListOrientation="horizontal"
class="contenedor">
<ngb-carousel *ngIf="images">
<ng-template *ngFor="let img of images" ngbSlide>
<div class="picsum-img-wrapper" [cdkDragData]="img" cdkDrag>
<img [src]="path+img.imagen" width="100%" alt="Random first slide">
</div>
</ng-template>
</ngb-carousel>
</div>
<div class="results">
<div class="talents">
<div class="example-container">
<h4>(+) Talento más desarrollado</h4>
<div cdkDropList #doneList="cdkDropList" [cdkDropListData]="list2"
[cdkDropListConnectedTo]="[intermedio, menos]"
class="example-list" (cdkDropListDropped)="drop($event)">
<ol class="example-box">
<li *ngFor="let l2 of list2" [cdkDragData]="l2" cdkDrag>
{{l2.nombre}}
</li>
</ol>
</div>
</div>
</div>
<div class="talents">
<div class="example-container">
<h4>Talento Intermedio</h4>
<div cdkDropList #intermedio="cdkDropList" [cdkDropListData]="list3"
[cdkDropListConnectedTo]="[doneList, menos]"
class="example-list" (cdkDropListDropped)="drop($event)">
<ol class="example-box">
<li *ngFor="let l3 of list3" [cdkDragData]="l3" cdkDrag>
{{l3.nombre}}
</li>
</ol>
</div>
</div>
</div>
<div class="talents">
<div class="example-container">
<h4>(-) Talento menos desarrollado</h4>
<div cdkDropList #menos="cdkDropList" [cdkDropListData]="list4"
[cdkDropListConnectedTo]="[intermedio, doneList]"
class="example-list" (cdkDropListDropped)="drop($event)">
<ol class="example-box">
<li *ngFor="let l4 of list4" [cdkDragData]="l4" cdkDrag>
{{l4.nombre}}
</li>
</ol>
</div>
</div>
</div>
</div>
</mat-card>
这是我的放置函数
drop(event: CdkDragDrop<any>) {
if (event.previousContainer === event.container) {
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
/*transferArrayItem(event.previousContainer.data,
event.container.data,
event.previousIndex,
event.currentIndex);*/
const newArray = event.previousContainer.data.filter(e => e.id !== event.item.data.id);
event.previousContainer.data = newArray;
event.container.data.push(event.item.data)
}
}
我尝试使用 transferArrayItem 但它没有用,因为我得到了错误的索引所以我尝试更改 event.previousContainer.data 和 event.container.data,我使用 event.container.data 但它没有改变 event.previousContainer.data.
知道为什么它不起作用吗? 提前致谢
您的代码几乎一切正常。
问题在于,由于所有带有 cdkDropList
指令的 <div>
容器都没有 children 它们的高度等于零,所以没有区域,其中元素可以删除。
一个可能的解决方案是将 cdkDropList 移到更高的位置,例如 <div class="example-container">
,或者如果您不希望 headers(如 (+) Talento más desarrollado</h4>
)触发拖动事件,那么您需要在下面提供一些高度,以便可以将项目拖到那里。
<div class="results">
<div class="talents">
<div class="example-container" cdkDropList
#doneList="cdkDropList"
[cdkDropListData]="list2"
[cdkDropListConnectedTo]="[intermedio, menos]"
class="example-list"
(cdkDropListDropped)="drop($event)"
>
<h4>(+) Talento más desarrollado</h4>
<!-- Probably no need for this div then !-->
<div>
<ol class="example-box">
<li *ngFor="let item of list2" [cdkDragData]="item" cdkDrag>
{{item.name}}
</li>
</ol>
</div>
</div>
</div>
Moving directive up in DOM tree stackblitz example.
使用这种方法,CdkDragDrop
事件的 previousIndex 始终等于 "images" 数组的长度。我认为可能需要使用 CdkDropListGroup
,但看起来将 <ngb-carousel>
与 DragDropModule 结合使用会出现问题。如果一起使用,则 DragDropModule 无法正确识别拖动项的索引。
如我所料,如果只列出图像,则索引是正确的:
Without ngb-carousel - stackblitz example.
您的示例中的另一个问题是可拖动项目应该是 HTML 元素的直接 children,带有 angular cdkDropList 指令。否则在拖动第二个元素时会出现以下错误:
Error: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
说了这么多,现在让我们尝试使用这些知识并将这段代码重构为一个工作示例。
首先是我们的模板:
<div class="talents">
<div class="example-container" >
<h4>(+) Talento más desarrollado</h4>
<ol class="example-box" cdkDropList
#doneList="cdkDropList"
[cdkDropListData]="list2"
[cdkDropListConnectedTo]="[intermedio, menos]"
(cdkDropListDropped)="drop($event)">
<li *ngFor="let item of list2" [cdkDragData]="item" cdkDrag>
{{item.name}}
</li>
</ol>
</div>
</div>
现在一些CSS:
.example-box:empty {
height:50px;
border: 5px dashed #cecece;
}
.example-box:empty:hover:after {
content: 'Drag & Drop items here';
color: #aeaeae;
font-weight: bold;
font-size: 18px;
width:50%;
position: relative;
left:25%;
top:5px;
}
通过在没有 children 的元素上设置高度,我们创建了一个拖放区。添加边框和文本是一个很好的 UX 设计,它让我们的最终用户知道,可以将项目拖到给定区域。
最后但并非最不重要的 TypeScript:
drop(event: CdkDragDrop<any>) {
// indexes are wrong due to ngb-carousel
if (event.previousContainer === event.container) {
// this is okay, no moving around in ngb-carousel, since it always displays one element;
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
} else {
let previousIndex = event.previousIndex;
const element = event.previousContainer.element.nativeElement;
const id = element.getAttribute('id');
// Only indexes of items dragged from carousel needs to be fixed
if (id === this.carouselContainerId) {
previousIndex = event.previousContainer.data.indexOf(event.item.data);
}
transferArrayItem(event.previousContainer.data,
event.container.data,
previousIndex,
event.currentIndex);
}
}