我正在尝试创建一个列表,可以通过拖动其中的项目来重新排序。
第一次拖动元素时,expr
会调用'yes'
。在dragstart.trigger="drag($event)"
中,我设置了拖动元素的数据。
删除拖动的元素drag(e)
时会调用drag(e)
。
在drop.trigger="drop($event)"
中,我获取了拖动的元素,并将其从列表/父元素drop(e)
中删除。
之后,我将拖动的元素插入到删除的位置。
问题是一旦元素被拖动。我无法将其再次拖动到其他目标,因为drop(e)
未调用<ul>
。
如何调用dragstart.trigger="drag($event)"
?
drag(e)
JS:
dragstart.trigger="drag($event)"
答案 0 :(得分:2)
重新排序元素后未调用dragstart
的原因是因为您并未真正重新排序它们。您实际上是删除拖动的元素,然后插入它的新副本。
这个新副本不是由aurelia的合成引擎处理的,因此没有编译,所以html中任何特定于aurelia的表达式都不会做任何事情。此时.trigger
只是一个死标签。
拖放是一种特殊的野兽,从来没有特别简单地以自然的方式实现,特别是当这些元素附加了所有类型的自定义框架行为时。
您有3个选项:
请勿使用aurelia&#39; trigger
,而只需在首次创建时使用el.addEventListener
,然后在创建新副本时使用{{p>
使用aurelia&#39; ViewEngine
重新编译视图(部分视图),只要您删除元素,以便处理.trigger
,在引擎盖下,真的有点像只是el.addEventListener
无论如何
将其转换为带有repeat.for
的自定义元素,让Aurelia处理html方面的内容。
现在选项1肯定是让它工作的最快方式,而选项2会稍微强一些,也很棘手,但两者都非常苛刻。
我强烈主张利用框架而不是黑客攻击,因为在长期内事情会更容易维护,并且随着项目的发展,您可以更轻松地添加额外的花哨行为。
它可能看起来比你现在所做的要多得多,但是通过使用更多的框架来处理低级别的东西,你将拥有&#34;生活&#34;具有功能齐全的Aurelia的可拖动元素,你可以做更多的事情。
所以这里只是您如何处理选项3的一个示例:
在app.js中,将列添加到javascript对象列表中:
items = [
{ text: "A", id: "item1" },
{ text: "B", id: "item2" },
{ text: "C", id: "item3" },
{ text: "D", id: "item4" },
{ text: "E", id: "item5" }
];
在app.html中,将这些项目传递给columns
自定义元素(为了使html与您的示例类似,我将使用as-element
)
<template>
<require from="./resources/elements/columns"></require>
<ul as-element="columns" items.bind="items"></ul>
</template>
在resources / elements / columns.js中,针对单个项目视图模型而不是针对html元素进行处理:
import { customElement, children, bindable } from "aurelia-templating";
@customElement("columns")
export class Columns {
// keeps a list of the viewmodels of the direct "li" children
@children("li") children;
// the columns
@bindable() items;
// the currently dragged column
dragColumn;
// the customEvent we dispatch from the child "column" element
handleColDragStart(e) {
// the viewmodel we passed into the customEvent
this.dragColumn = e.detail.column;
}
allowDrop(e) {
console.log("handleDragover");
e.preventDefault();
}
drop(e) {
console.log("handleDrop");
if (e.stopPropagation) {
e.stopPropagation();
}
// source drag index
let dragIdx = this.children.indexOf(this.dragColumn);
// if we can't resolve to a sibling (e.g. dropped on or outside the list),
// naively drop it at index 0 instead
let dropIdx = 0;
// try to find the drop target
let dropTarget = e.srcElement;
while (dropTarget !== document.body) {
let dropTargetVm = dropTarget.au && dropTarget.au.controller && dropTarget.au.controller.viewModel;
if (dropTargetVm) {
dropIdx = this.children.indexOf(dropTargetVm);
break;
} else {
dropTarget = dropTarget.parentElement;
}
}
if (dragIdx !== dropIdx) {
// only modify the order in the array of javascript objects;
// the repeat.for will re-order the html for us
this.items.splice(dropIdx, 0, this.items.splice(dragIdx, 1)[0]);
}
return false;
}
}
在resources / elements / columns.html中,只需侦听我们从column
元素发送的customEvent,而不只是处理drop
:
<template id="columns" drop.trigger="drop($event)" dragover.trigger="allowDrop($event)">
<require from="./column"></require>
<li as-element="column" repeat.for="col of items" column.bind="col" coldragstart.trigger="handleColDragStart($event)">
</li>
</template>
在resource / elements / column.js中处理dragstart
和dragend
事件,然后调度一个带有viewModel引用的customEvent(这样你就不必处理html了)太多了):
import { customElement, bindable } from "aurelia-templating";
import { inject } from "aurelia-dependency-injection";
@customElement("column")
@inject(Element)
export class Column {
el;
constructor(el) {
this.el = el;
}
@bindable() column;
dragstart(e) {
this.el.dispatchEvent(
new CustomEvent("coldragstart", {
bubbles: true,
detail: {
column: this
}
})
);
return true;
}
}
最后,在resources / elements / column.html中,只需监听dragstart
事件:
<template draggable="true" dragstart.trigger="dragstart($event)">
<header>${column.text}</header>
</template>
这个解决方案的一部分可能看起来有点奇怪,也是我仍然认为有点hacky的部分,是我们尝试通过el.au.controller.viewModel
获取ViewModel的地方。
这是你只需要了解的事情&#34;。自定义元素/ html行为始终具有au
属性,其中包含对具有控制器,视图等的行为实例的引用。
这实际上是最简单的(有时是唯一的)&#34;获得&#34; aurelia直接对抗html。像拖放这样的东西,我不相信有任何办法可以避免这种情况,因为遗憾的是没有原生的aurelia支持它。