我正在Angular Material网站上关注this tutorial,以创建自定义表单控件。教程中没有示例涉及验证错误时如何遵守表单控制错误。
custom-text.component.html
<mat-form-field class="example-full-width">
<mat-select [placeholder]="placeholder" #select [formControl]="control">
<mat-option *ngFor="let food of foods" [value]="food">
{{food}}
</mat-option>
</mat-select>
<mat-error>This is required.</mat-error>
</mat-form-field>
custom-text.component.ts
import { Component, ViewChild, HostBinding, Input, ChangeDetectionStrategy, Optional, Self, DoCheck, OnInit, NgZone } from '@angular/core';
import { ControlValueAccessor, NgControl, NgForm, FormGroupDirective, FormControlDirective, FormControlName, FormControl, FormBuilder } from '@angular/forms';
import { MatFormFieldControl, MatSelect, CanUpdateErrorState, ErrorStateMatcher } from '@angular/material';
@Component({
selector: 'custom-text',
templateUrl: './custom-text.component.html',
styleUrls: [
'./custom-text.component.scss'
],
providers: [
{
provide: MatFormFieldControl,
useExisting: CustomTextComponent
}
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CustomTextComponent implements ControlValueAccessor, OnInit, DoCheck {
@Input()
foods: string[];
@Input()
get errorStateMatcher(): ErrorStateMatcher {
return this.select.errorStateMatcher;
}
set errorStateMatcher(val) {
this.select.errorStateMatcher = val;
}
@Input()
get placeholder() {
return this.select.placeholder;
}
set placeholder(plh) {
this.select.placeholder = plh;
this.stateChanges.next();
}
@Input()
get value() {
return this.select.value;
}
set value(val) {
this.select.value = val;
this.stateChanges.next();
}
@ViewChild('select')
select: MatSelect;
control: FormControl;
constructor(
@Optional() @Self() ngControl: NgControl,
@Optional() private _controlName: FormControlName) {
if (ngControl) {
ngControl.valueAccessor = this;
}
}
ngOnInit(): void {
this.control = this._controlName.control;
}
ngDoCheck(): void {
this.select.updateErrorState();
}
writeValue(obj: any): void {
this.value = obj;
}
registerOnChange(fn: any): void {
this.select.registerOnChange(fn);
}
registerOnTouched(fn: any): void {
this.select.registerOnTouched(fn);
}
setDisabledState?(isDisabled: boolean): void {
this.select.setDisabledState(isDisabled);
}
}
app.component.html
<div style="text-align:center">
<form class="example-form" [formGroup]="myForm" (submit)="submitForm()">
<custom-text [foods]="[null, 'burger', 'spaghetti', 'fries']"
formControlName="selectedFood"
[errorStateMatcher]="matcher"></custom-text>
<button>Submit</button>
</form>
</div>
基本上,我将FormControlName
实例注入到自定义控件中。
constructor(
@Optional() private _controlName: FormControlName) {
....
ngOnInit(): void {
this.control = this._controlName.control;
}
然后我将其control
属性绑定到内部mat-select
控件中。
<mat-select [placeholder]="placeholder" #select [formControl]="control">
然后我在this.select.updateErrorState
内部呼叫ngDoCheck
。
以下是StackBlitz的链接: https://stackblitz.com/edit/angular-c4ufpp
是否有更好或更标准的方式?
答案 0 :(得分:1)
与其使用Validator接口创建循环错误依赖关系,不如在您的自定义组件中添加errorState
,以检查注入到构造函数中的ngControl
,如下所示:
get errorState() {
return this.ngControl.errors !== null && !!this.ngControl.touched;
}
这使DoCheck
变得不必要。在您的父组件中,而不是使用errorMatcher
,而是使用常规的Angular验证器,如下所示:
selectedFood = new FormControl('burger', [Validators.required]);
答案 1 :(得分:0)
您可以实现验证器接口。
import { NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator, Validators } from'@angular/forms';
..other imports
@Component({
...
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => YourComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => YourComponent),
multi: true,
}
]
})
validate(c: AbstractControl): ValidationErrors | null {
... put your validation check in here and return null if it´s valid
}