双向绑定

不管是Angular还是Vue,他们的表单的双向绑定无非做了两件事,一件是接收输入的数据并赋值给元素的value属性,一件是监听input/change等事件,然后将$event.target.value赋值给绑定的值。这样就实现了基本的view-model和model-view的双向绑定。

输入+输出===双向绑定

总结为代码就是:

<input [value]="name" (input)="name = $event.target.value" />

在Angular里面:

<input [ngModel]="name" (ngModelChange)="name = $event" />

简写为语法糖的话:

<input [(ngModel)]="name" />

在Vue里面:

<input v-model="form.name"></input>

现在试想,如果我们触发数据改变的方法不是通过用户输入,而是通过在JS里改变,比如下面这样:

html

<input  [(ngModel)]="name"  id="nameEl"></input>

js:
let element=document.getElementById('nameEl');
element.target.value="xxxxxxx";

现在我们会发现我们绑定的name变量没有发生改变,原因很简单,我们没有触发任何输出的事件。

我们目前遇到这种场景是因为在拖拽结束的时候,需要改变目标元素的值为我们拖拽的这个元素的内容,这个事情必须在JS里来完成。

拖拽元素

拖拽元素的时候,被拖拽元素会触发以下事件

  • dragstart
  • drag
  • dragend

目标元素会触发以下事件

  • dragenter
  • dragover
  • dragleave
  • drop

这个场景只是很多情况中的一个,我们为什么不直接在拖拽结束的时候将value赋值给绑定的变量呢?比如:

source.ondragend = function(evt){
        this.name=evt.target.text;
 }

因为绑定的这个变量可能没这么简单,他可能是根据数组或者其他数据结构动态渲染出来的,这样我们不太方便直接操作绑定的那个变量。

那我们使用 element.target.value这种方式,如何才能触发绑定的name也跟着改变呢。答案是我们可以使用dispatchEvent派发事件。

什么是dispatchEvent

向一个指定的事件目标派发一个事件, 并以合适的顺序同步调用目标元素相关的事件处理函数。标准事件处理规则(包括事件捕获和可选的冒泡过程)同样适用于通过手动的使用dispatchEvent()方法派发的事件。

现在我们就来实现:

source.ondragstart = function(evt){
        ev.dataTransfer.setData("text/plain", ev.target.text);     
 }
 
target.ondrop=function(evt){
       ev.preventDefault();
       const data = ev.dataTransfer.getData("text");
       ev.dataTransfer.clearData();
       element.target.value=data;
       element.dispatchEvent(new Event('input'));
}