[Angular] Forms - Control Value Accessor [上]

前言

在前两篇的介绍中我们了解到了什么是Angular中的form,并且对Reactive form进行了实作,不过当我们在开发专案的时候,由于form的code过于庞大,会希望将重複使用到的部分建立成子组件(sub-component),在传统方法下,如果需要与子组件沟通会需要使用@Input()@Output(),但是Angular提供了另一种方便简单的方法可以达到一样与子组件沟通的效果,这个方法便是Control Value Accessor(CVA)


自订 NG_VALUE_ACCESSOR

我们在子组件(sub-component)中需要建立NG_VALUE_ACCESSOR设定,当Angular在执行程式的时会检查此组件是否有设定NG_VAULE_ACCESSOR,若有便会将此组件(component)视为一个表单控制项(formControl)。

// MyCVAComponent.tsimport { Component, forwardRef } from '@angular/core';@Component({  selector: 'app-my-cva-component',  template: '',  styleUrls: ['./my-cva-component.css'],  providers: [    {      provide: NG_VALUE_ACCESSOR, // set NG_VALUE_ACCESSOR      useExisting: forwardRef(() => MyCVAComponent),      multi: true,    },  ],})

我们在sub-component中设定了NG_VALUE_ACCESSOR,代表我们将这个Component设定为一个FormControl,而forwardRef(() => MyCVAComponent)中的forward代表把快转,因为我们必须确保MyCVAComponent要先被建立后才将它建立为FormControl,所以可以避免建立FormControl的时候找不到Component实体

multi: true代表着NG_VALUE_ACCESSOR是可以被注入到多个不同的实体中,只要是设定了NG_VALUE_ACCESSOR的实体都可以被正确拿到而不会被覆盖。


建立Control Value Accessor

当我们建立好这个Component的token后,我们正式开始建立这个Control Value Accessor Component,我们要将这个Component建立为Angular规定的ControlValueAccessor形式,首先我们需要引入它的interface,Angular提供的Interface规定了需要有以下几种method:

writeValue(value: any): 当form data被元件外部元件(父组件)更改时调用。registerOnChange(fn: any): 当CVA Component中的form值发生更改时调用(传值给父组件)。registerOnTouched(fn: any): 当CVA Component中的form被点击时调用。setDisabledState(isDisable: boolean)?: 当disabled状态改变时调用。
// MyCVAComponent.tsexport Clsaa MyCVAComponent inplements ControlValueAccessor {    private disabled = false;    constructor() {}        writeValue(value: any) {};        registerOnChange(fn: (_: any) => void): void {};        registerOnTouched(fn: (_: any) => void): void {};        setDisabledState(isDisable: boolean) {        this.disabled = isDisable;    };}

这样我们就建立好一个CVA Component,接下来需要从父组件中绑定和给定form default value。

父组件绑定

我们将CVA Component建立完成后,需要在父组件中绑定这个建立好的CVA并给予他初始值。

// app.componentimport { Component } from '@angular/core';import { FromControl, FormGroup, FormBuilder } from '@angular/forms'@Component({  selector: 'app-root',  template: `      <form [formGroup]="myForm">          <app-my-cva-component formControlName="name"></app-my-cva-component>      </form>      <pre>{{myForm.value | json}}</pre>  `,>   styleUrls: ['./app.component.css'],})export class AppComponent {  title = 'control-value-accessor';  myForm: FormGroup;  constructor(private fb: FormBuilder) {      this.myFrom = this.fb.group({          name: new FromControl({              value: 'Fandix Huang',              disabled: false          }) // get default value      })  }}

在父组建中建立一个formGroup,必在其中new一个名为nameFormControl并给予Fandix Huang的初始值,而我们在templete中引用子组件的tag,透过FormControlName将我们的CVA Component绑定到父组件中的formControl,这样就可以完成form绑定并给予CVA Component default value。

绑定CVA Component

我们设定好父组件中的FormControl与将值绑定给CVA Component后,我们要对CVA Component进行一些设定。

export class MyCVAComponent implements ControlValueAccessor {  private formControl: FormControl;  private disabled: boolean;    constructor(private fb: FormBuilder) {    this.formControl = new FormControl('');  }  writeValue(val): void{    this.formControl.setValue(val); // set default value  }  registerOnChange(fn: (_: any) => void): void {    this.formControl.valueChanges.subscribe(fn);  }  registerOnTouched(fn: (_: any) => void): void {};}

这样我们就可以将父组件与CVA组建进行绑定。
img

Form Disable

对于开发一些专案中会需要对表单的更改权限进行设定,当权限不足时便将form表格disable以不让其更改。

我们在父组件的templete中新增个button让我们控制是否要disable这个formControl。

<!-- app.component.html --><button (click)="onDisable()">disable</button>

在父组件中新增一个onDisable method已更改disable的状态。

export class AppComponent {  title = 'control-value-accessor';  myForm: FormGroup;  private isDisable;    constructor(private fb: FormBuilder) {      this.myFrom = this.fb.group({          name: new FromControl({              value: 'Fandix Huang',              disabled: false          }) // get default value      })  }    // create method onDisable  onDisable(): void{      this.isDisable = !this.isDisable      if (isDisable) {          this.myForm.disable();      } else {          this.myForm.enable();      }  }}

接下来我们在CVA Component中新增setDisabledState,他会接收到从父组件传递下来的disable status。

export class MyCVAComponent implements ControlValueAccessor {  formControl: FormControl;  private disable: boolean;  constructor(private fb: FormBuilder) {    this.formControl = new FormControl('');  }  writeValue(val): void{    this.formControl.setValue(val);  }  registerOnChange(fn: (_: any) => void): void {    this.formControl.valueChanges.subscribe(fn);  }  registerOnTouched(fn: (_: any) => void): void {};  // create  setDisabledState(isDisable: boolean) {    this.disable = isDisable;    if (this.disable) {      this.formControl.disable();    } else {      this.formControl.enable();    }  }}

img


参考文献:
[Angular 大师之路] Day 08 - 自订表单控制项
Angular Control-Value_Accessor


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章