Angular Material 自定义表单域不显示 mat-error
Angular Material custom form field doesn't show mat-error
我以为我几乎 但不知何故控制欺骗了我 :-/
<form [formGroup]="form">
<app-ref-urlcheck [maxLen]="20" formControlName="url"></app-ref-urlcheck>
</form>
模板看起来像
<mat-form-field>
<input matInput #inUrl="ngModel" [(ngModel)]="value" type="url" [attr.maxlength]="maxLen" [errorStateMatcher]="errorStateMatcher"
(input)="changeInput(inUrl.value)" [disabled]="isDisabled" [value]="strUrl"
placeholder="Homepage" />
<mat-error>test error</mat-error> <!-- doesn't show up - neither the next -->
<mat-error *ngIf="(inUrl.touched && inUrl.invalid)">This field is required</mat-error>
</mat-form-field>
及主要内容
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { AbstractControl, ControlValueAccessor, FormControl, NgControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { Observable } from 'rxjs';
@Component({
selector: 'app-ref-urlcheck',
templateUrl: './ref-urlcheck.component.html',
styleUrls: ['./ref-urlcheck.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: RefURLcheckComponent
},
{
provide: NG_VALIDATORS,
multi: true,
useExisting: RefURLcheckComponent
}
]
})
export class RefURLcheckComponent implements OnInit, ControlValueAccessor, MatFormFieldControl<any>, Validator {
@Input() maxLen = 254;
strUrl: string;
onChange = (changedUrl) => { };
onTouched = () => { };
isDisabled = false;
touched = false;
@HostListener('focusin', ['$event.target.value']) onFocusIn;
constructor() { }
onContainerClick(event: MouseEvent): void {
throw new Error('Method not implemented.');
}
setDescribedByIds(ids: string[]): void {
throw new Error('Method not implemented.');
}
userAriaDescribedBy?: string;
autofilled?: boolean;
controlType?: string;
errorState: boolean;
disabled: boolean;
required: boolean;
shouldLabelFloat: boolean;
empty: boolean;
focused: boolean;
ngControl: NgControl;
placeholder: string;
id: string;
stateChanges: Observable<void>;
value: any;
ngOnInit(): void {
}
setDisabledState?(isDisabled: boolean): void {
this.isDisabled = isDisabled;
}
registerOnTouched(onTouched: () => {}): void {
this.onTouched = onTouched;
}
registerOnChange(onChange: (changedValue: string) => {}): void {
this.onChange = onChange;
this.onFocusIn = (inputVal) => {
console.log('focus in', inputVal);
this.markAsTouched();
};
}
writeValue(value: string): void {
this.strUrl = value;
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
changeInput(inVal: string) {
this.onChange(inVal);
this.markAsTouched();
}
readonly errorStateMatcher: ErrorStateMatcher = {
isErrorState: (ctrl: FormControl) => {
console.log('errorStateMatch...')
this.errorState = true;
return (ctrl && ctrl.invalid);
}
};
validate(control: AbstractControl): ValidationErrors | null {
if (control?.value.length <= 5) {
this.errorState = true;
return {
tooShort: true
};
}
this.errorState = false;
return null;
}
}
与参考示例中的问题相同:如何显示 <mat-error>
?它甚至没有出现。
重新使用了附加的代码,怀疑 FormControl
在验证失败时没有更新错误。
当验证失败时,应将错误设置为FormControl
,如下所示:
this.inUrl.control.setErrors({ tooShort: true });
import { ViewChild } from '@angular/core';
export class RefURLcheckComponent
implements OnInit, ControlValueAccessor, MatFormFieldControl<any>, Validator
{
@ViewChild('inUrl', { static: true }) inUrl: NgControl;
...
validate(control: AbstractControl): ValidationErrors | null {
if (control?.value?.length <= 5) {
this.errorState = true;
this.inUrl.control.setErrors({ tooShort: true });
return {
tooShort: true,
};
}
this.errorState = false;
this.inUrl.control.setErrors(null);
return null;
}
}
感谢 @Yong Shun
我知道了如何正确地进行管理。似乎需要使用常规模板驱动方法(用于 input
字段的更新)包装 input
字段并将此组件用作反应组件。所以我的自定义控件内部有一个处理状态的控件。
我从我的原始代码中删除了所有不必要的东西并包含了一些来自 guideline 的小提示。
这是我的(简化版)working example - 以备有人再次需要时使用。
我以为我几乎
<form [formGroup]="form">
<app-ref-urlcheck [maxLen]="20" formControlName="url"></app-ref-urlcheck>
</form>
模板看起来像
<mat-form-field>
<input matInput #inUrl="ngModel" [(ngModel)]="value" type="url" [attr.maxlength]="maxLen" [errorStateMatcher]="errorStateMatcher"
(input)="changeInput(inUrl.value)" [disabled]="isDisabled" [value]="strUrl"
placeholder="Homepage" />
<mat-error>test error</mat-error> <!-- doesn't show up - neither the next -->
<mat-error *ngIf="(inUrl.touched && inUrl.invalid)">This field is required</mat-error>
</mat-form-field>
及主要内容
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { AbstractControl, ControlValueAccessor, FormControl, NgControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { Observable } from 'rxjs';
@Component({
selector: 'app-ref-urlcheck',
templateUrl: './ref-urlcheck.component.html',
styleUrls: ['./ref-urlcheck.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: RefURLcheckComponent
},
{
provide: NG_VALIDATORS,
multi: true,
useExisting: RefURLcheckComponent
}
]
})
export class RefURLcheckComponent implements OnInit, ControlValueAccessor, MatFormFieldControl<any>, Validator {
@Input() maxLen = 254;
strUrl: string;
onChange = (changedUrl) => { };
onTouched = () => { };
isDisabled = false;
touched = false;
@HostListener('focusin', ['$event.target.value']) onFocusIn;
constructor() { }
onContainerClick(event: MouseEvent): void {
throw new Error('Method not implemented.');
}
setDescribedByIds(ids: string[]): void {
throw new Error('Method not implemented.');
}
userAriaDescribedBy?: string;
autofilled?: boolean;
controlType?: string;
errorState: boolean;
disabled: boolean;
required: boolean;
shouldLabelFloat: boolean;
empty: boolean;
focused: boolean;
ngControl: NgControl;
placeholder: string;
id: string;
stateChanges: Observable<void>;
value: any;
ngOnInit(): void {
}
setDisabledState?(isDisabled: boolean): void {
this.isDisabled = isDisabled;
}
registerOnTouched(onTouched: () => {}): void {
this.onTouched = onTouched;
}
registerOnChange(onChange: (changedValue: string) => {}): void {
this.onChange = onChange;
this.onFocusIn = (inputVal) => {
console.log('focus in', inputVal);
this.markAsTouched();
};
}
writeValue(value: string): void {
this.strUrl = value;
}
markAsTouched() {
if (!this.touched) {
this.onTouched();
this.touched = true;
}
}
changeInput(inVal: string) {
this.onChange(inVal);
this.markAsTouched();
}
readonly errorStateMatcher: ErrorStateMatcher = {
isErrorState: (ctrl: FormControl) => {
console.log('errorStateMatch...')
this.errorState = true;
return (ctrl && ctrl.invalid);
}
};
validate(control: AbstractControl): ValidationErrors | null {
if (control?.value.length <= 5) {
this.errorState = true;
return {
tooShort: true
};
}
this.errorState = false;
return null;
}
}
与参考示例中的问题相同:如何显示 <mat-error>
?它甚至没有出现。
重新使用了附加的代码,怀疑 FormControl
在验证失败时没有更新错误。
当验证失败时,应将错误设置为FormControl
,如下所示:
this.inUrl.control.setErrors({ tooShort: true });
import { ViewChild } from '@angular/core';
export class RefURLcheckComponent
implements OnInit, ControlValueAccessor, MatFormFieldControl<any>, Validator
{
@ViewChild('inUrl', { static: true }) inUrl: NgControl;
...
validate(control: AbstractControl): ValidationErrors | null {
if (control?.value?.length <= 5) {
this.errorState = true;
this.inUrl.control.setErrors({ tooShort: true });
return {
tooShort: true,
};
}
this.errorState = false;
this.inUrl.control.setErrors(null);
return null;
}
}
感谢 @Yong Shun
我知道了如何正确地进行管理。似乎需要使用常规模板驱动方法(用于 input
字段的更新)包装 input
字段并将此组件用作反应组件。所以我的自定义控件内部有一个处理状态的控件。
我从我的原始代码中删除了所有不必要的东西并包含了一些来自 guideline 的小提示。
这是我的(简化版)working example - 以备有人再次需要时使用。