详解Angular使用ControlValueAccessor实现自定义表单控件


本文摘自PHP中文网,作者青灯夜游,侵删。

本篇文章给大家介绍一下Angular使用ControlValueAccessor实现自定义表单控件的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

Angular: [ControlValueAccessor] 自定义表单控件


我们在实际开发中,通常会遇到各种各样的定制化功能,会遇到有些组件会与 Angular 的表单进行交互,这时候我们一般会从外部传入一个 FormGroup 对象,然后在组件的内部写相应的逻辑对 Angular 表单进行操作。如果我们只是对表单中的一个项进行定制,将整个表单对象传入显然不合适,并且组件也会显得臃肿。

1

<form [formGroup]="simpleForm">                                <br>  <other-component [form]="simpleForm"></other-component>        <br></form><br>

那么,我们能不能像原生表单一样去使用这些自定义组件呢?目前,开源组件 ng-zorro-antd 表单组件能和原生表单一样使用 formControlName 这个属性,这类组件就叫自定义表单组件。【相关推荐:《angular教程》】

如何实现自定义表单控件

在 Angular 中,使用 ControlValueAccessor 可以实现组件与外层包裹的 form 关联起来。

ControlValueAccessor是用于处理以下内容的接口:

  • 将表单模型中的值写入视图/ DOM
  • 在视图/ DOM更改时通知其他表单指令和控件

ControlValueAccessor

ControlValueAccessor 接口定义了四个方法:

1

writeValue(obj: any): void<br><br>registerOnChange(fn: any): void<br><br>registerOnTouched(fn: any): void<br><br>setDisabledState(isDisabled: boolean)?: void<br>

writeValue(obj:any):将表单模型中的新值写入视图或DOM属性(如果需要)的方法,它将来自外部的数据写入到内部的数据模型。数据流向: form model -> component。

registerOnChange(fn:any):一种注册处理程序的方法,当视图中的某些内容发生更改时应调用该处理程序。它具有一个告诉其他表单指令和表单控件以更新其值的函数。通常在 registerOnChange 中需要保存该事件触发函数,在数据改变的时候,可以通过调用事件触发函数通知外部数据变了,同时可以将修改后的数据作为参数传递出去。数据流向: component -> form model。

registerOnTouched(fn: any):注册 onTouched 事件,基本同 registerOnChange ,只是该函数用于通知表单组件已经处于 touched 状态,改变绑定的 FormControl 的内部状态。状态变更: component -> form model。

setDisabledState(isDisabled: boolean):当调用 FormControl 变更状态的 API 时得表单状态变为 Disabled 时调用 setDisabledState() 方法,以通知自定义表单组件当前表单的读写状态。状态变更: form model -> component。

如何使用 ControlValueAccessor

搭建控件框架

1

@Component({<br>  selector: 'app-test-control-value-accessor',<br>  templateUrl: './test-control-value-accessor.component.html',<br>  providers: [{<br>    provide: NG_VALUE_ACCESSOR,<br>    useExisting: forwardRef(() => TestControlValueAccessorComponent),<br>    multi: true<br>  }]<br>})<br>export class TestControlValueAccessorComponent implements ControlValueAccessor {<br><br>  _counterValue = 0;<br> <br>  private onChange = (_: any) => {};<br><br>  constructor() { }<br><br>  get counterValue() {<br>    return this._counterValue;<br>  }<br><br>  set counterValue(value) {<br>    this._counterValue = value;<br>    // 触发 onChange,component 内部的值同步到 form model<br>    this.onChange(this._counterValue);<br>  }<br><br>  increment() {<br>    this.counterValue++;<br>  }<br><br>  decrement() {<br>    this.counterValue--;<br>  }<br><br>  // form model 的值同步到 component 内部<br>  writeValue(obj: any): void {<br>    if (obj !== undefined) {<br>      this.counterValue = obj;<br>    }<br>  }<br><br>  registerOnChange(fn: any): void {<br>    this.onChange = fn;<br>  }<br><br>  registerOnTouched(fn: any): void { }<br><br>  setDisabledState?(isDisabled: boolean): void { }<br><br>}<br>

注册 ControlValueAccessor

为了获得 ControlValueAccessor 用于表单控件,Angular 内部将注入在 NG_VALUE_ACCESSOR 令牌上注册的所有值,这是将控件本身注册到 DI 框架成为一个可以让表单访问其值的控件。因此,我们需要做的就是NG_VALUE_ACCESSOR 使用我们自己的值访问器实例(这是我们的组件)扩展 multi-provider 。所以设置 multi: true,是声明这个 token 对应的类很多,分散在各处。

这里我们必须使用 useExisting,因为TestControlValueAccessorComponent 可能在使用它的组件中被其创建为指令依赖项。这就得用到 forwardRef 了,这个函数允许我们引用一个尚未定义的对象。

1

@Component({<br>  ...<br>  providers: [<br>    { <br>      provide: NG_VALUE_ACCESSOR,<br>      useExisting: forwardRef(() => TestControlValueAccessorComponent ),<br>      multi: true<br>    }<br>  ]<br>})<br>export class TestControlValueAccessorComponent implements ControlValueAccessor {<br>  ...<br>}<br>

控件界面

  • test-control-value-accessor.component.html

1

<div class="panel panel-primary"><br>  <div class="panel-heading">自定义控件</div><br>  <div class="panel-body"><br>    <button (click)="increment()">+</button><br>    {{counterValue}}<br>    <button (click)="decrement()">-</button><br>  </div><br></div><br>

在表单中使用

  • app.component.html

1

<div class="constainer"><br>  <form #form="ngForm"><br>    <app-test-control-value-accessor name="message" [(ngModel)]="message"></app-test-control-value-accessor><br>    <button type="button" (click)="submit(form.value)">Submit</button><br>  </form><br>  <pre>{{ message }}</pre><br></div><br>

  • app.component.ts

1

@Component({<br>  selector: 'app-root',<br>  templateUrl: './app.component.html',<br>  styleUrls: ['./app.component.css']<br>})<br>export class AppComponent {<br><br>  message = 5;<br><br>  submit(value: any): void {<br>    console.log(value);<br>  }<br><br>}<br>

参考

  • https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html

  • https://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

  • https://juejin.im/post/597176886fb9a06ba4746d15

  • https://github.com/shhdgit/blogs/issues/11

更多编程相关知识,请访问:编程视频!!

以上就是详解Angular使用ControlValueAccessor实现自定义表单控件的详细内容,更多文章请关注木庄网络博客

相关阅读 >>

Angular中 “?” 和 “!”有什么用?

Angular7中如何引用ng zorro antd?

浅谈Angular中如何添加和使用font awesome

浅谈Angular模板指令:ng-template和ng-container的用法

了解Angularjs中的“模块”

浅谈Angular cli工具如何从零搭建并运行一个简单项目

聊一聊Angular中的路由(routing)

AngularAngularjs间有什么关系

2021年值得尝试的7个Angular前端组件库,快来收藏吧!

浅析Angular路由中的懒加载、守卫、动态参数

更多相关阅读请进入《Angular》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...