重新排序元素后不会调用Dragstart

时间:2018-03-17 11:20:11

标签: javascript javascript-events aurelia aurelia-binding

我正在尝试创建一个列表,可以通过拖动其中的项目来重新排序。

第一次拖动元素时,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)"

1 个答案:

答案 0 :(得分:2)

重新排序元素后未调用dragstart的原因是因为您并未真正重新排序它们。您实际上是删除拖动的元素,然后插入它的新副本。

这个新副本不是由aurelia的合成引擎处理的,因此没有编译,所以html中任何特定于aurelia的表达式都不会做任何事情。此时.trigger只是一个死标签。

拖放是一种特殊的野兽,从来没有特别简单地以自然的方式实现,特别是当这些元素附加了所有类型的自定义框架行为时。

您有3个选项:

  1. 请勿使用aurelia&#39; trigger,而只需在首次创建时使用el.addEventListener,然后在创建新副本时使用{{p>

  2. 使用aurelia&#39; ViewEngine重新编译视图(部分视图),只要您删除元素,以便处理.trigger,在引擎盖下,真的有点像只是el.addEventListener无论如何

  3. 将其转换为带有repeat.for的自定义元素,让Aurelia处理html方面的内容。

  4. 现在选项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中处理dragstartdragend事件,然后调度一个带有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支持它。