首页 文章

Angular 2/4 Observable - 为什么ng2-dragula在UI中单次拖放后发送多个drop事件?

提问于
浏览
1

EDIT regarding question title change

最初的问题 Headers 是这样的:

Angular 2/4 Observable - 如何在不激发更多事件的情况下修改在订阅中发出值的对象?

经过广泛的调查,这不是问题的原因,但在这里的评论中有一些建议,关于如何解决这个特定问题 . ACTUAL问题与多个ng2-dragula订户同时存在有关,因此为什么我更新了问题 Headers 以反映这一点 .


我正在使用ng2-dragula插件并订阅了dropModel事件 .

我面临的问题是在订阅代码中我需要通过重新安排模型中的其他项来修改模型 . 这会导致dropModel再次触发dropModel事件 - 它显然认为是因为我更改了列表中的模型位置,用户执行了拖放操作,而不是我的代码 .

我试过take(1)但是没有解决问题 - 它只是继续进行一个事件,所以当我在subscribe中更改模型时,显然它会再次使用下一个(1) .

这是代码:

this._dragulaService.dropModel.take(1).subscribe(() => {
  // For ease, we just copy all the values into the model and then destroy
  // the itemsControl, then patch all model values back to the form
  // This is easier than working out which item was dragged and dropped where
  // (and on each drop we want to save the model, so we would need to update it anyway)
  this.itemsControl['controls'].forEach((formGroup, index) => {
    this.template.template_items[index].sort = index; // ensures the API will always return items in the index order
    console.log('copying ID', formGroup['controls'].id.value);
    this.template.template_items[index].id = formGroup['controls'].id.value;
    this.template.template_items[index].item_type = formGroup['controls'].item_type.value;
    this.template.template_items[index].content = formGroup['controls'].content.value;
    this.template.template_items[index].is_completed = formGroup['controls'].is_completed.value;
  });
}

理想情况下,我想“ grab ”第一个丢弃事件(用户启动),然后在订阅代码中,取消订阅或停止接收更多事件,然后处理模型,最后重新订阅 .

我知道这有点奇怪 - 在订阅异步代码中我需要以某种方式“暂停”订阅 . 虽然'暂停'不是很正确 - 但实际上我想以某种方式阻止在我处理完当前事件之前触发新事件 . 暂停只会导致我处理自己的事件(如果这是有道理的) . 这可能吗?

Note

这里的dragula绑定的模型是itemsControls的动态数组,而不是正常意义上的纯数据模型 . 因此,为什么我从表单控件中提取数据并插入到实际的数据模型中 .

UPDATE 1

我决定用我绑定的itemsControl(一个AbstractControls数组)记录dragula正在做什么 .

在拖动之前,我记录了数组中的实际内容:

itemsControl is now this: (4) [FormGroup, FormGroup, FormGroup, FormGroup]

在dropModel订阅处理程序中,我记录了一个“已删除”事件和数组的长度 . 这是我拖放任何项目时的输出,总是相同的输出:

dropped
length is 3
dropped
length is 3
dropped
length is 4
dropped
length is 5
dropped
length is 5
dropped
length is 4
dropped
length is 4

但是,如果我删除上面发布的代码(即所以我不触及底层数据模型),这是输出:

dropped
length is 4
dropped
length is 4

所以至少证明通过重新排序数据我不仅导致更多事件被触发(我怀疑),而且还有控件数组长度增加和减少的奇怪副作用(不知道为什么会这样) .

鉴于此输出,我需要一种方法来仅对最后发出的事件采取行动 .

Is there a way to only get the last event from an Observable?

UPDATE 2

根据this,这里真正的问题是ng2-dragula不支持将dropModel绑定到FormArray . 但似乎有一个解决方法......仍在搜索!

2 回答

  • 2

    因此,在所有这些之后,似乎有一种正确的方式和错误的方式来订阅ng2-dragula:

    错误的方式,这就是我所拥有的:

    dragulaService.dropModel.subscribe((result) => { ... }
    

    正确的方式:

    private destroy$ = new Subject();
    
    constructor (private _dragulaService: DragulaService) {
      this._dragulaService.dropModel.asObservable()
        .takeUntil(this.destroy$).subscribe((result) => { ... }
    }
    
    ngOnDestroy() {
      this.destroy$.next();
    }
    

    取自here .

    现在我只在每个drop上获得一个事件,并且对AbstractControls进行排序不会触发进一步的drop事件 .

    UPDATE

    感谢@RichardMatsen的评论,我进一步研究了为什么上面的代码解决了这个问题 . 它没有使用asObservable()这样做,所以我不确定为什么在我上面提供的问题714的链接中建议这样做 . 可能asObservable()需要我们可以正确取消订阅(假设dragulaService是一个EventEmitter)?

    以前我用过我读过的in the docs

    dragulaService.destroy(name)销毁名为name的drake实例 .

    摧毁'德雷克':

    ngOnDestroy(){
        this._dragulaService.destroy('bag-container');
      }
    

    这并没有取消订阅dragulaService . 因此,当我导航回组件时,它仍然会发出事件,这就是我获得多次丢弃的原因 .

    事实证明订阅和销毁是 not the recommended way 使用dragulaService,所以我提交了this PR来更新ng2-dragula的自述文件,以便为将来的用户清楚地说明这一点 .

  • 1

    如果你总是得到两次发射,一个便宜的答案可能是使用一个标志来区分第一和第二

    const haveModified = false;
    this._dragulaService.dropModel.subscribe(() => {
      if (!haveModified) {
        this.itemsControl['controls'].forEach((formGroup, index) => {
          ...
        });
        haveModified = true;
      } else {
        haveModified = false;
      }
      });
    }
    

    更多Rx方法 - 检查下标值,如果两个发射相同(或属性相同),则使用 distinctUntilChanged(compare: function) .

    Update

    刚刚玩了Plunker,对类中的数据数组做了简单的排序,而且我没有从dragula中获得第二次发射 .

    constructor(dragulaService: DragulaService) {
        dragulaService.dropModel.subscribe(value => {
          console.log(value)
          console.log('target before sort', this.target)
          this.target.sort();
          console.log('target after sort', this.target)
        })
      }
    

    我无法理解您正在进行的操作,但我发现您正在使用HTML控件的引用 . 你能通过操纵基础数据来做到吗?

    Update #2

    一直在使用 distinctUntilChanged(comparer) 的比较器,它涉及比较项目的 index ,这应该使该解决方案可行 .

    但是,索引无法获得,因为有点来源中的黑客攻击(dragula.provider.ts,第97行)

    target.removeChild(dropElm); // element must be removed for ngFor to apply correctly
    

    dropModel事件是

    this.dropModel.emit([name, dropElm, target, source]);
    

    但是dropElm既不是目标也不是源,所以我们不知道索引是什么 . 如果 dropIndex 被发射也会更好

    this.dropModel.emit([name, dropElm, target, source, dropIndex]);
    

    然后我们可以捕获重复事件 .

相关问题