Angular 自定义表单控件永远不会弄脏我的表单

Angular Custom Form Control never make my Form dirty

我创建了一个自定义表单控件,但每当值发生变化时,它都不会将表单标记为脏。我不明白应该做什么。

这是我的自定义组件的代码:

    import { ThrowStmt } from '@angular/compiler';
    import { Component, forwardRef, Input, OnInit, Output } from '@angular/core';
    import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
    import { EventEmitter } from 'events';
    
    @Component({
      selector: 'app-multiple-checkbox',
      templateUrl: './multiple-checkbox.component.html',
      styleUrls: ['./multiple-checkbox.component.scss'],
      providers: [
        {
          provide: NG_VALUE_ACCESSOR,
          useExisting: forwardRef(() => MultipleCheckboxComponent),
          multi: true
        },
        {
          provide: NG_VALIDATORS,
          useExisting: forwardRef(() => MultipleCheckboxComponent),
          multi: true
        }]
    })
    export class MultipleCheckboxComponent implements OnInit, ControlValueAccessor {
    
      value = [];
      disabled = false;
    
      @Input() public options: { key, value }[];
      @Output() haschanged = new EventEmitter();
    
      onChange: any = (key) => {
        this.haschanged.emit(key, this.value);
      }
    
      onTouched: any = () => { };
    
      constructor() { }
    
      validate({ value }: FormControl) {
        const isNotValid = this.value.length === 0;
        return isNotValid && {
          invalid: true
        };
      }
    
      ngOnInit(): void {
      }
    
      evaluate(event, key): void {
        if (event.target.checked) {
          this.value.push(key);
        } else {
          this.value.splice(this.value.findIndex(i => key === i), 1);
        }
    
        this.onTouched(this.value);
        this.onChange(key, this.value);
      }
    
      isChecked(key): boolean {
        return this.value.findIndex(e => e === key) > -1;
      }
    
      writeValue(value: any): void {
        this.value = value;
      }
      registerOnChange(fn: any): void {
        this.onChange = fn;
      }
      registerOnTouched(fn: any): void {
        this.onChange = fn;
      }
      setDisabledState?(isDisabled: boolean): void {
        this.disabled = isDisabled;
      }
    
    }

视图如下:

    <div *ngFor="let option of options">
        <label [attr.for]="option.key">{{option.value}}</label>
        <input [id]="option.key" type="checkbox" [checked]="isChecked(option.key)" [disabled]="disabled" (change)="evaluate($event, option.key)">
    </div>

缺少什么使表格变脏?

基于official documentation它说

On blur (or equivalent), your class should call the registered function to allow the forms API to update itself:

host: {
   '(blur)': '_onTouched()'
}

change 事件发生同样的事情,而且你没有达到 ControlValueAccessor 的目的,它应该与 ngModel 一起使用,或者与 FormControls 的 ReactiveForms 一起使用 这是在表单中用于复选框的指令

source code

所以你可以看到一个 class 是如何实现的 ControlValueAccessor 这可能是一个好的开始

您的实施中有几处不太正确。

  1. onChange 函数将在行 onChange = fn 中分配一个值,因此不会使用下面的代码
  onChange: any = (key) => {
    this.haschanged.emit(key, this.value);
  }

所以将其更改为 onChange: any;

  1. 下面一行重新赋值了我们的onChange函数,这里肯定会出现意想不到的结果
registerOnTouched(fn: any): void {
  this.onChange = fn;
}

把上面的改成

onTouched: any;
registerOnTouched(fn: any): void {
  this.onTouched = fn;
}
  1. 删除EventEmitter。您可能正在尝试发出事件,但是在使用 ControlValueAccessor 时 angular 应该为您完成剩下的工作,包括事件发出。另外 非常重要 EventEmitter 是从 @angular/core 而不是从 events

    导入的
  2. 更改评估函数以调用 onTouchedonChange 函数,如下所示

  evaluate(event, key): void {
    if (event.target.checked) {
      this.value.push(key);
    } else {
      this.value.splice(this.value.findIndex(i => key === i), 1);
    }
    this.onChange(this.value);
    this.onTouched();
  }

完成这些更改后,Angular 将为您完成剩下的工作,包括将表单设置为脏状态、触摸状态和有效状态

See this demo on stackblitz