Angular学习笔记(五) - 自定义表单控件

1,991 阅读3分钟

本文简单介绍封装使用ngModel实现自定义表单控件的过程。

NgModel 相关

NgModel

NgModel用于从作用域创建一个FormControl实例,并将它绑定到一个表单控件元素。

ngModel继承自NgControl

NgControl

NgControl是所有控制指令继承的基础类。它将一个FormControl绑定到DOM元素中。

FormControlFormGroupFormArray,三者都用于angular表单的值和状态的跟踪,区别是一个控件、一组控件或者是它们的组合。

FormControl用于跟踪一个单独的表单控件的值和有效性状态。它对应一个HTML表单控件,不如输入框和下拉框。 FormGroup用于跟踪一组AbstractControl的实例的值和有效性状态。该组的属性中包含了它的子控件。组件中的顶级表单就是一个FormGroupFormArray用于跟踪AbstractControl实例组成的有序数组的值和有效性状态。

ControlValueAccessor

ControlValueAccessor用于在控制和原生元素之间建立联系,它封装了赋值到一个表现为input元素的DOM元素。

简单说,就是angular中的input是带有[(ngModel)]这个属性的,而我们想要自己控制这个input的写入过程,使用ControlValueAccessor就可以做到。

ControlValueAccessor提供以下接口:

  • writeValue(obj: any) : void: 写入值到元素
  • registerOnChange(fn: any) : void: 设置当控件接收到change事件时触发的回调
  • registerOnTouched(fn: any) : void: 设置当控件接收到touch事件时触发的回调
  • setDisabledState(isDisabled: boolean) : void: 该函数将在控件状态或者disabled值变化,根据值来对元素启用或失效

DefaultValueAccessor

DefaultValueAccessor提供值写入和监听变化的默认访问,像NgModel, FormControlDirective, 和FormControlName指令会使用。

DefaultValueAccessor提供类包括:

  • onChange : (_: any) => {} change事件变化监听
  • onTouched : () => {} touch事件变化监听

以及ControlValueAccessor(上面)的接口。

NG_VALUE_ACCESSOR

NG_VALUE_ACCESSOR提供一个ControlValueAccessor供表单控制使用。

创建自定义input控件

我们想要封装后的组件跟原生的angular组件一样,像表现为input的自定义控件,我们想要使用[(ngModel)]来进行双向绑定,我们需要使用ControlValueAccessor来拓展。

而这里ControlValueAccessor只是一个接口,我们应用它,还需要获取一些可用的服务,这时候需要注入NG_VALUE_ACCESSOR

{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputComponent),
    multi: true
};

这里简单讲讲几个概念:

  • 我们自定义了一个访问控制服务,该服务包装为NG_VALUE_ACCESSOR服务,主要用于控制ControlValueAccessor相关的访问。
  • 我们需要将自定义input控件提供给NG_VALUE_ACCESSOR,以便通过自定义方式控制父组件的[(ngModel)]以及其他表单相关的访问。
  • forwardRef用于将目前还未获取到的依赖关联起来,这里我们关联后面的自定义Input组件。
@Component({
  selector: 'app-custom-input',
  templateUrl: './custom-input.component.html',
  styleUrls: ['./custom-input.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomInputComponent),
      multi: true
    }
  ]
})
export class CustomInputComponent implements OnInit, ControlValueAccessor {
  /*
  * 自定义表单控件
  * 需要完成ControlValueAccessor内部的方法
  * */
  private _custom: any = '';

  // 定义ControlValueAccessor提供的事件回调
  private onChange = (_: any) => {
  };

  get custom(): any {
    return this._custom;
  }

  set custom(v: any) {
    if (v !== this._custom) {
      this._custom = v;
      this.onChange(this._custom);
    }
  }

  constructor() {
  }

  ngOnInit() {
  }

  writeValue(val: any): void {
    this._custom = val;
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
  }

}

具体的实现实例参考Angular2 + Connect custom component to ngModel。