前言

  • 之前看过vue的双向绑定原理,并实现过简易的demo,但是很少见过有讲解angular下,双向绑定是如何实现的,所以看了下NgModel源码,发现大同小异,特来说下
  • 目前来说只说下ngModel是如何实现的,不涉及属性绑定及指令方面的知识
  • 本篇文章需要知道如下知识才可以阅读

一个ngModel,使用了几个指令?

  • 一般当这么说,那么肯定意味着使用了不只一个指令,否则也不会浪费时间说一个确定的事实
  • NgModelControlValueAccessor实现类,当你要使用NgModel时,实际上是使用了两个指令
  • NgModel 负责注册一些函数,所有ControlValueAccessor实现类实现的4个方法(一个可选禁用方法),最终都是通过NgModel的来使用的
  • ControlValueAccessor实现类是规定绑定的内部逻辑,比如什么时候发送值被变更,当出现值写入时如何处理

流程示意图

注册

  • 所谓注册就是当值变更时.实现类应该用哪些方法去通知已经变更
  • 实现类中的registerOnChange(fn),fn就是在注册阶段传入的,当在这个阶段调用实现类.registerOnChange(fn),函数被赋值到实现类中,而当值变更时实现类则调用此函数通知值已变更
  • 此阶段,总共有6个方法被注册(验证器分为同步,异步)
  • 传入函数已经写在shared.ts文件中

输入

  • 当[ngModel]传入的值变更后会被NgModelngOnchanges钩子捕获到
  • 如果值变更或为初始值,那么会调用钩子中的this._updateValue(this.model)
  • 此方法会触发类中FormControl实例的值变更
  • 当值变更时,上述注册方法中的3会执行传入函数,函数中会触发实现类中的writeValue方法,此时实现类收到了变更值
  • 在默认的方法中,收到变更值后,是利用angular的渲染器,写入到input元素的value中
  • 输入流程结束

输出

  • inputcompositionend事件时,会调用传入函数onChange.当blur事件时,会调用传入函数onTouched
  • 函数收到值后判断是不是处在更新时机,如果是那么更新调用NgModel中的viewToModelUpdate
  • 此方法更新值并发射信号ngModelChange告知值已经变更

难点

  • 大家应该都知道自定义表单控件怎么写,明明没有写过ngModel input属性和ngModelChange output事件,但是却在实现了ControlValueAccessor类后神奇的实现了.
  • 这里其实就是因为NgModel指令的选择器是[ngModel]:not([formControlName]):not([formControl]),也就是说,只要不是响应式表单,那么所有带ngModel的都会自动使用这个指令
  • 并且该指令在构造时会找他的ControlValueAccessor实现类,当找到后,通过将一些变更需要出发的函数告知实现类而建立了关联

源码注释

疑问

  • 如果有哪里描述的有问题或者不对的请多提宝贵意见
03-05 14:06