角度双向绑定,多一点理解

来自翻译
从翻译人员那里:两年前,我在Angular(2+)上开始了我的第一个项目,具有丰富而成功的AngularJS背景。 过渡需要一种明显的思维方式,因为在A1和A2 +上做的太多“有些不同”。 过渡的痛苦极大地减少了想法 。 一年前,我获得了翻译这篇文章的许可,“关于每个人都基本且容易理解的”。 但是它们就是这样的手(他们的文章是一堆未完成的文章)。 令人惊讶的是,该文章在Google翻译上的翻译效果很好。 但是此翻译中的一些细微差别已丢失,更不用说作者的风格了。 在我的版本中,作者的风格尚未完全保留。 但是,我希望能够传达出本文的心情和想法。

我知道Angular不是哈布雷(Habré)上最受欢迎的话题,但是我希望翻译能够对某人有所帮助,就像原始文章曾经对我有所帮助一样。

这就是在旧版AngularJS中引起哇效果的原因,所以它是“双向绑定”。 这种魔力立即爱上了AngularJS,打破了所有有关无聊的页面编程和(令人恐惧的!)Web表单的想法。 数据更改立即显示在屏幕上,反之亦然。 那些以前开发jQuery应用程序的人将链接视为童话。 留着胡须的怪兽,在jQuery之前吸引了很多胖客户,开始疯狂地计算那些愚蠢的迷途工友。

而且,双向绑定的魔力不仅适用于特殊符号和选定的组件。 我们可以在我们自己的指令和组件中轻松使用它(仅通过设置配置参数即可)。

在Angular2 +中,创建者放弃了内置的双向数据绑定 (通过ngModel除外)。 但这并不意味着我们不能在自己的指令中使用双向绑定...仅仅是免费赠品已经结束,现在我们需要自己做一些事情。 并且,最好是了解它在Angular中的工作方式。

目录



双向绑定


在A2 +中,只有一个指令可实现双向数据绑定: ngModel 。 乍一看,这与AngularJS中的魔术相同(只是用不同的表示法)。 但是到底是什么呢?

令人惊讶的是,在幕后,一切都相对简单且合乎逻辑:双向绑定简化为属性绑定和事件绑定。 是两个单边绑定,而不是一个双边绑定? 好,我们两个。

并立即举一个例子:

<input [(ngModel)]="username"> <p>Hello {{username}}!</p> 

是的,是的,这是2009年的一个美丽而令人惊叹的Angular2演示。 不开玩笑,美丽。 更改字段时, 用户名值将落入模型中,并立即反映在表单上的欢迎消息中。

但是它是如何工作的呢? 回想一下,Angular2中的双向绑定是属性绑定和事件绑定。 是的,它们可以在一个指令中同时可用。 而且,即使没有ngModel ,我们也可以轻松实现双向数据绑定。 例如,像这样:

 <input [value]="username" (input)="username = $event.target.value"> <p>Hello {{username}}!</p> 

输出{{username}}很清楚,但是输入中写了什么? 让我们了解一下:

  • [value] =“用户名” -用方括号表示,将用户名表达式与value属性相关联
  • (input)=“ expression” -带括号的表示法,该表达式附加到输入事件(是的,有这样的事件)。 在我们的情况下:
    • username = $ event.target.value-该表达式将响应输入事件而执行
    • $ event是Angular事件中的一个合成变量,带有一个有效负载:在这种情况下,它包含有关发生的情况及其周围环境的信息

越来越清楚了吗? 我们修复它。

我们将Angular模型的username属性绑定到浏览器输入元素的value属性(从模型到视图的单向绑定)。

我们还将表达式绑定到元素的输入事件。 它将$ event.target.value的值分配给模型的username属性。

什么是$ event.target.value ? 如前所述, $ event充满了有关该事件的各种有用信息。 在这种情况下,它是一个InputEventObject ,其中target属性引用触发事件的DOM元素(即我们的input元素)。

因此,我们基本上要做的就是在用户输入值时读取输入元素( $ event.target )的内容( )。 当我们分配该用户名值时,视图数据将发送到模型。

仅此而已。 这是“简而言之双向绑定”。 美丽吗

但是ngModel什么时候起作用? 使用输入元素的场景非常普遍并且需求很大。 出于某种原因,我想拥有一个隐藏实现并从额外的击键中省下来的指令。

了解ngModel


如果您查看源代码,则可以确保ngModel也具有对属性和事件的绑定。 这是我们的ngModel示例的样子,但是没有使用速记语法:

 <input [ngModel]="username" (ngModelChange)="username = $event"> <p>Hello {{username}}!</p> 

几乎所有事物都是一样的。 [ngModel]属性的绑定负责更新输入元素的值。 事件绑定(ngModelChange)通知世界DOM中正在发生更改。

并且您注意到处理程序表达式仅使用$ event ,而不使用$ event.target.value 。 这有什么问题吗? 一点也不。 如上所述, $ event是一个携带有效载荷的综合变量。 Angular会决定哪些内容有用。 换句话说, ngModelChange负责从内部$事件中提取target.value ,并且仅提供我们想要的内容,而无需包装和手鼓。 从技术上讲,这些是DefaultValueAccessor的对象 :正是他提取数据并将其传输到基本DOM对象,尽管...您根本无法考虑它)。

最后但并非最不重要的一点是,由于两次写入用户名ngModel仍然是多余的,因此Angular允许使用缩写语法[[]] ,也称为“盒子里的香蕉”。 这与前面的示例相似,并从本节的开头使我们返回到该示例,但对ngModel实现有所了解。 提供相同的双向绑定。

 <input [(ngModel)]="username"> <p>Hello {{username}}!</p> 


创建自己的双向数据绑定


现在我们足够了解创建自己的双向数据绑定。 您需要做的只是遵循与ngModel相同的规则,即:

  • 输入属性绑定(例如: [foo]
  • 绑定到具有相同名称和后缀Change的事件(例如: (fooChange)
  • 确保事件绑定负责检索属性(如有必要)

注意,创建双向数据绑定比AngularJS需要更多的工作吗? 如果我们尝试尽可能使用自己的双向绑定,这可能会使我们非常沮丧。 在现实生活中,您应该始终考虑我们是否需要双向绑定,如果有必要,利用ngModel是否更容易。 例如,后者在创建自定义表单控件时发生

但是,假设我们创建了一个自定义计数器组件(并且不想使用自定义表单控件)。

 @Component({ selector: 'custom-counter', template: ` <button (click)="decrement()">-</button> <span>{{counter}}</span> <button (click)="increment()">+</button> ` }) export class CustomCounterComponent { counterValue = 0; get counter() { return this.counterValue; } set counter(value) { this.counterValue = value; } decrement() { this.counter--; } increment() { this.counter++; } } 

我们具有计数器组件的属性以显示计数器的当前值。 要为它提供双向绑定,首先要做的是将其转换为Input参数。 为此, @Input()装饰器非常有用:

 @Component() export class CustomCounterComponent { counterValue = 0; @Input() get counter() { return this.counterValue; } ... } 

这已经允许您将组件属性绑定到使用者,如下所示:

 <custom-counter [counter]="someValue"></custom-counter> 

现在我们需要将@Output()事件设置为相同的名称( counter )和后缀Change (原来是counterChange)。 每当计数器更改时,我们都希望引发此事件。 为什么要添加@Output()属性。 然后,我们在几个getter中完成了计数器设置器,在该方法中,我们将拦截值的更改,并使用当前计数器值抛出事件:

 @Component() export class CustomCounterComponent { ... @Output() counterChange = new EventEmitter(); set counter(val) { this.counterValue = val; this.counterChange.emit(this.counterValue); } ... } 

就是这个! 现在,我们可以使用双向数据绑定语法将表达式绑定到此属性:

 <custom-counter [(counter)]="someValue"></custom-counter> <p>counterValue = {{someValue}}</p> 

查看演示并尝试!

同样,请记住,最好使用自定义窗体控件来实现诸如自定义计数器之类的组件,并利用ngModel来实现双向数据绑定,如本文所述

结论


Angular不再带有内置的双向数据绑定。 相反,框中有一些API,可让您将完全绑定作为绑定属性和事件来实现。

ngModel在FormsModule中作为内置的双向绑定指令来提供(请记住将其添加到@NgModule声明的imports部分:大约。)。 创建用作自定义表单控件的组件时,应首选通过ngModel进行链接。 否则,一切都取决于您的想象力。

译者的PS: A2 +中的绑定实现变得更加现代。 现在,几乎“免费的”设置器通常被“风水”用来监视更改(尽管很明显,脏检查的机制仍然存在,至少对于高级用户组件而言)。 这样就可以放弃100,500个观察者(用于监视“其”数据中的更改的过程)。 A1中的哪个喜欢在浏览器上造成恶意负载,并且在计划丰富的交互式页面时需要异常直接的帮助。

通过正确设计的组件,开箱即用的A2响应速度大大提高。 让我们以牺牲程序员的工作为代价。 现在,您可以在页面上放置大量组件,而不必担心处理器资源。

硬币的另一面是A2 +中“输入过程”的初始成本,这影响了框架的普及。 但是A1的进入成本也很高,只是降级到了大联盟。 由于缺乏对如何组织大型应用程序的了解,许多原型在A1上“起飞”,然后“崩溃”并对应于React和Vue。

我希望通过这篇文章,我将有助于稍微降低A2 +首次进入的门槛,而A2 +仍然仍然是需求(我是第一手知道的)。

Source: https://habr.com/ru/post/zh-CN453696/


All Articles