Angular有两种类型的表单:响应式表单(reactive forms)、模板驱动表单(Template-driven forms)。
共同基础
[FormControl](https://angular.cn/api/forms/FormControl)实例用于追踪单个表单控件的值和验证状态。[FormGroup](https://angular.cn/api/forms/FormGroup)用于追踪一个表单控件组的值和状态。[FormArray](https://angular.cn/api/forms/FormArray)用于追踪表单控件数组的值和状态。[ControlValueAccessor](https://angular.cn/api/forms/ControlValueAccessor)用于在 Angular 的[FormControl](https://angular.cn/api/forms/FormControl)实例和原生 DOM 元素之间创建一个桥梁。
实现自定义表单控件的核心在于:[ControlValueAccessor](https://angular.cn/api/forms/ControlValueAccessor),组件需要实现这个接口的功能。
interface ControlValueAccessor {writeValue(obj: any): voidregisterOnChange(fn: any): voidregisterOnTouched(fn: any): voidsetDisabledState(isDisabled: boolean)?: void}
- writeValue(obj: any):该方法用于将模型中的新值写入视图或 DOM 属性中。
- registerOnChange(fn: any):设置当控件接收到 change 事件后,调用的函数
- registerOnTouched(fn: any):设置当控件接收到 touched 事件后,调用的函数
- setDisabledState?(isDisabled: boolean):当控件状态变成
DISABLED或从DISABLED状态变化成ENABLE状态时,会调用该函数。该函数会根据参数值,启用或禁用指定的 DOM 元素。
在组件实现[ControlValueAccessor](https://angular.cn/api/forms/ControlValueAccessor)接口后,要能正常使用的话,还需要执行注册操作。
实现ngModel双向绑定
导入FormsModule
import { NgModule } from '@angular/core';import { CommonModule } from '@angular/common';import { FormsModule } from '@angular/forms';import { NgZorroAntdModule } from 'ng-zorro-antd';import { City3Component } from './city3.component';@NgModule({imports: [CommonModule, FormsModule, NgZorroAntdModule],declarations: [City3Component],exports: [City3Component],})export class City3Module {}
组件需要注入一个Provider去实现。
/*** template-driven forms 模板驱动表单* 实现 ngModel*/const EXE_CITY3_VALUE_ACCESSOR: ExistingProvider = {provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() => City3Component),multi: true,};@Component({selector: 'app-city3',templateUrl: './city3.component.html',styleUrls: ['./city3.component.less'],encapsulation: ViewEncapsulation.None,changeDetection: ChangeDetectionStrategy.OnPush,providers: [EXE_CITY3_VALUE_ACCESSOR]})export class City3Component implements OnInit, ControlValueAccessor {// ...}
实现formControl
导入ReactiveFormsModule
import { NgModule } from '@angular/core';import { CommonModule } from '@angular/common';import { ReactiveFormsModule } from '@angular/forms';import { NgZorroAntdModule } from 'ng-zorro-antd';import { City3Component } from './city3.component';@NgModule({imports: [CommonModule, ReactiveFormsModule, NgZorroAntdModule],declarations: [City3Component],exports: [City3Component],})export class City3Module {}
实现自定义校验器
- 使用 ValueProvider 注册
/*** 直接提供给provider使用的校验器* @param control AbstractControl*/const validateSelectValue: ValidatorFn = (control: AbstractControl): ValidationErrors => {if (control.touched) {const reg = CITY_REGEXPS[2];if (control.value) {return reg.test(control.value) ? null : { invalid: true };} else {return null;}}};/*** provider 默认校验使用的方式*/const EXE_CITY3_VALIDATOR: ValueProvider = {provide: NG_VALIDATORS,useValue: validateSelectValue,multi: true,};@Component({selector: 'app-city3',templateUrl: './city3.component.html',styleUrls: ['./city3.component.less'],encapsulation: ViewEncapsulation.None,changeDetection: ChangeDetectionStrategy.OnPush,providers: [EXE_CITY3_VALIDATOR]})export class City3Component implements OnInit, ControlValueAccessor {// ...}
- 使用 ExistingProvider 注册
/*** 自定义校验所使用的校验方式,组件要额外实现 Validator*/const EXE_CITY3_VALIDATOR_2: ExistingProvider = {provide: NG_VALIDATORS,useExisting: forwardRef(() => City3Component),multi: true,};@Component({selector: 'app-city3',templateUrl: './city3.component.html',styleUrls: ['./city3.component.less'],encapsulation: ViewEncapsulation.None,changeDetection: ChangeDetectionStrategy.OnPush,providers: [EXE_CITY3_VALIDATOR_2]})export class City3Component implements OnInit, ControlValueAccessor, Validator {// .../*** 实现自定义校验,写在组件类可介入入参* @param control AbstractControl*/validate(control: AbstractControl): ValidationErrors | null {if (control.touched) {const reg = CITY_REGEXPS[2];if (control.value) {return reg.test(control.value) ? null : { invalid: true };} else {return null;}}}}
参考:https://segmentfault.com/a/1190000009070500、https://github.com/NG-ZORRO/ng-zorro-antd/tree/master/components/select;
自定义表单控件完整代码:https://github.com/superchow/ng-login-demo/tree/master/src/app/components/city3
