数据绑定发生referenceerror_都2019了,你还对双向数据绑定念念不忘?

      双向数据绑定是AngularJs的一大卖点,当初问世时开发人员无不惊讶,“Wow, it's so crazy"。但是用过AngularJs的,都对它又爱又恨,爱的是它确实给开发提供了一定的便利,恨的是基于‘脏检查’的变更检测机制会随着watch的数据量的增加拖慢应用运行的速度。于是乎,goolge在2016年推出了angular彻底改变了检测机制,这次并没有大力吆喝双向数据绑定,但仍会有人习惯的问一句,“有没双向数据绑定?”。如果你只是随口一问,我会告诉你,有。如果你仍然“死缠烂打”的追问倒底有没有,我会告诉你,没有

像AngularJs中一样使用双向绑定

在AngularJs中,双向数据绑定的写法:

ng-model="name" />

//controller.js

...

$scope.name = 'John';

...

Angular中的写法

[(ngModel)]="name" />

// component.ts

...

name = 'John';

...

写法上略有不同,目的和实现的效果却是一样的,当js或ts文件中的name值发生变化时,html模板中的值会发生改变,反之,当用户在input中输入值的时候,js或ts文件中name的值也会发生相应的改变,这就是让很多人念念不忘的双向数据绑定。

AngularJs接下来会设置$watch,进入digest循环,然后循环检测等等,背后发生的一切各位看官有兴趣自行google,这里就不再赘述。你肯定会关心的是,Angular不是明明实现了双向绑定吗,为什么文章开头会说,没有?已经2019了,该忘的东西还是忘了吧,这不是喜新厌旧,应该是与时俱进。

Angular中的’双向数据绑定‘

没有黑魔法

Angular努力拥抱web标准,不创造新名词,也不使用什么黑魔法,那么双向绑定是如何实现的呢?事实上通过属性绑定和事件,这并不难做到。

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

// component.ts

...

name = 'John';

...

上面这段代码中,组件中的属性绑定到了input元素的value属性,自然input的初始值就应该是’John‘。input元素上会产生input事件,通过监听这个事件把name重新赋值。

与其关心双向绑定等黑魔法(实际还算不上黑魔法),倒不如去关心‘输入和输出’。

模板上[]的语法代表了输入,html元素或组件通过这种语法接收输入值。 模板上()的语法代表了输出,html元素通过事件或者组件通过EventEmitter向外输出值。 $event可以视作获取输出的关键字,不同场景下代表的对象是不同的,上面这段代码中由于是监听了input事件,所以它代表的就是 InputEvent,通过属性查询我们获取到了事件上传递的值。

照葫芦画瓢

上面代码现在看起来和之前使用的‘双向绑定’不太一样,但是这只不过是表象。

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

ts代码没什么变化,这里就省略了。

依然是有输入,有输出,只不过属性名称由value变成了ngModel,事件名称由input变成了ngModelChange。

在不看源码的情况下,如果是让你去实现 ngModel 这个指令,相信你肯定有思路。

  1. 肯定要把输入属性 ngModel 和input元素的value值关联起来。

  2. input的值发生变化后需要使用 ngModelChange 把它发送出来,那ngModelChange肯定是一个EventEmitter。

  3. 在赋值的时候直接用的是$event,而不是$event.target.value。这也很容易,要内部实现时取出inputEvent对象的值传递给 ngModelChange 就Ok了。

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

现在,我们只需要使用简写写法把它们合起来,这就是‘双向绑定’

[(ngModel)]="name" />

为什么这样写组件中的数据会被修改?肯定是Angular内部帮你做了啊,要不怎么叫简写定法呢?这些小事框架都不帮忙,要框架何用?当然这只是开个玩笑,如果你愿意的话可以看下源码。对于实现来说需要记住的是,输入属性名称加一个‘Change’后缀,把它定义成EventEmitter就可以了。

自定义双向绑定

按照上面的思路,实现一个双向绑定的步骤:

  1. 定义一个输入属性(如:name)。

  2. 定义一个输出属性,名称就是输入属性名加‘Change’后缀(如:nameChange)。

  3. 确保nameChange输出最新的值。

name.component.ts

@Component({

selector: 'name',

template: `

add prefix

My name is: {{ name }}

add suffix

`,

})

export class NameComponent {

@Input() name: string;

@Output() nameChange: EventEmitter = new EventEmitter();

addPrefix() {

this.name = '* ' + this.name;

this.nameChange.emit(this.name); // 记得输出新的值

}

addSuffix() {

this.name = this.name + ' *';

this.nameChange.emit(this.name); // 记得输出新的值

}

}

在其它组件中使用这个组件:

app.component.ts

@Component({

selector: 'my-app',

template: ``,

})

export class AppComponent {

name = 'Angular';

log() {

console.log(this.name);

}

}

注意app组件中的log方法并没有接收参数,而是直接log出组件上name属性的值,这里是为了说明当name的值在子组件中被修改以后,angular帮助我们把 AppComponent 上name的值进行了修改。

下面是输出结果:

fddc60bb4f819b9985faad2b47eea509.gif

可见所谓的双向绑定只不过是在输入和输出的基础上的语法糖。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值