使用验证器创建自定义 FormControl

Create custom FormControl with Validators

我创建了一个使用 Material 设计的组件 (AComponent),我想将它用作我的 Reactive Form(在 AppComponent 中)中的 formControl。 我已经在这个组件 (AComponent) 中实现了 ControlValueAccessor

export class AComponent implements ControlValueAccessor {
  fm = new FormControl();

  constructor(private ngControl: NgControl) {
    ngControl.valueAccessor = this;

    this.fm.valueChanges.subscribe(v => {
      this.onChange(v);
    })
  }

  onChange = (_: any) => {}
  onTouched = () => {}

  writeValue(obj: any) {
    this.fm.setValue(obj);
  }
  registerOnChange(fn: any) {
    this.onChange = fn;
  }
  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }

}

我可以为这个组件(在 AppComponent 中)设置 Validators.required

  ngOnInit() {
    this.formGroup = this.fb.group({
      control: ['', Validators.required],
      controlOne: ['', Validators.required]
    })

    this.formGroup.statusChanges
      .subscribe(console.log);
  }

它会影响整个表单。 (可以在控制台看到:ACompnent无效时无效)。

但问题是我无法强制 AComponent 看起来无效(红色输入),以防它无效。 看起来内部 formControl(在 AComponent 中)没有从 AppComponent 获得 Validators.required

问题是:如何以优雅的方式将验证器设置为 AComponent?

DEMO

您需要提供NG_VALIDATORS

https://medium.com/@tarik.nzl/angular-2-custom-form-control-with-validation-json-input-2b4cf9bc2d73

providers: [
    {
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => CustomInputComponent),
        multi: true
    },
    {
        provide: NG_VALIDATORS,
        useExisting: forwardRef(() => CustomInputComponent),
        multi: true
    }  
]

您也可以选择使用 @Hostbinding

设置 ng-error 类

注意 当我们创建自定义表单控件时,Angular 添加 ng-invalid ng-touched 到我们的组件。所以,通常我们使用 .css

来改变控件的外观
.ng-invalid.ng-touched
{
color:red;
}

问题是我们想要在自定义表单控件中更改 mat-input 的外观。所以我们需要知道什么时候我们的控制无效。

this stackblitz 中,您有自己的自定义表单控件。

关键是知道您的自定义表单控件何时无效。为此,whe 可以注入 ngControl。必须使用 inject 来避免循环依赖。所以,我们的构造函数就像

constructor(public injector: Injector) { }
  ngOnInit() {
    this.ngControl = this.injector.get(NgControl);
    this.fm.valueChanges.subscribe(v => {
      this.onChange(v);
    })
  }

好吧,我们知道自定义控件何时有效、触及、原始...

我们将定义一个 .css like

.customError,.custom
{
  display:inline-block;
}
.customError .mat-form-field-empty.mat-form-field-label {
    color: red!important;
}
.customError .mat-form-field-underline {
    background-color: red!important;
}

并在我们的组件中使用 ViewEncapsulation.None。 ViewEncapsulation.None 使 .css 在所有应用程序中。这是因为我们在.mat-form-field-empty.mat-form-field-label和mat-form-field-underline之前添加了class“.customError”。否则我们所有的 mat-components 看起来都有错误。

@Component({
   selector: 'app-a',
   templateUrl: './a.component.html',
   styleUrls: [ './a.component.css' ],
   encapsulation:ViewEncapsulation.None,
   providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => AComponent),
    multi: true
   }]
})

在此之后,我们使用 [ngClass] 添加 classes

<div class="custom" 
    [ngClass]="{'customError':ngControl.invalid && ngControl.touched}" >

  <mat-form-field 
      [color]="ngControl.invalid && ngControl.touched?'warn':null" >
     <input matInput  placeholder="Some value" [formControl]="fm" (blur)="onTouched()">
  </mat-form-field>
</div>

看到我们在mat-form-field中使用属性[颜色]使波纹下划线也变成红色以及我们如何使用(模糊)标记为已触摸控件