Angular 自定义表单控件

文章转自我的语雀:https://www.yuque.com/liuyin-zzwa0/angular/custom-form-control

Angular有两种类型的表单:响应式表单(reactive forms)、模板驱动表单(Template-driven forms)。
共同基础

  • FormControl 实例用于追踪单个表单控件的值和验证状态。
  • FormGroup 用于追踪一个表单控件组的值和状态。
  • FormArray 用于追踪表单控件数组的值和状态。
  • ControlValueAccessor 用于在 Angular 的 FormControl 实例和原生 DOM 元素之间创建一个桥梁。

实现自定义表单控件的核心在于:ControlValueAccessor,组件需要实现这个接口的功能。

interface ControlValueAccessor {
  writeValue(obj: any): void
  registerOnChange(fn: any): void
  registerOnTouched(fn: any): void
  setDisabledState(isDisabled: boolean)?: void
}
  • writeValue(obj: any):该方法用于将模型中的新值写入视图或 DOM 属性中。
  • registerOnChange(fn: any):设置当控件接收到 change 事件后,调用的函数
  • registerOnTouched(fn: any):设置当控件接收到 touched 事件后,调用的函数
  • setDisabledState?(isDisabled: boolean):当控件状态变成 DISABLED 或从 DISABLED 状态变化成 ENABLE 状态时,会调用该函数。该函数会根据参数值,启用或禁用指定的 DOM 元素。

在组件实现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

你可能感兴趣的:(Angular 自定义表单控件)