我想将 Angular Material 的 matInput 封装在组件中,以便在应用程序的其他位置重用它,因为我需要管理其内部状态以将输入类型从文本更改为密码,反之亦然。
我设法通过实现 ControlValueAccessor 来做到这一点,但验证错误的样式没有显示。
密码字段组件:
export class PasswordFieldComponent
implements OnInit, ControlValueAccessor {
@ViewChild(DefaultValueAccessor) private valueAccessor: DefaultValueAccessor;
@Input() customClass: string;
@Input() customPlaceholder: string;
@Input() required = true;
hide = true;
constructor() { }
ngOnInit() {
}
private propagateChange = (_: any) => { };
private onChange(event) {
this.propagateChange(event.target.value);
}
private onTouch() { }
registerOnChange(fn: any): void {
this.valueAccessor.registerOnChange(fn);
}
registerOnTouched(fn: any): void {
this.valueAccessor.registerOnTouched(fn);
}
setDisabledState(isDisabled: boolean): void {
this.valueAccessor.setDisabledState(isDisabled);
}
writeValue(value: any): void {
this.valueAccessor.writeValue(value);
}
}
密码字段模板:
<mat-form-field class="full-width {{ customClass }}">
<input
matInput
ngDefaultControl
placeholder="{{ customPlaceholder }}"
[required]="required"
[type]="hide ? 'password' : 'text'"
(input)="onChange($event)">
<button mat-icon-button matSuffix (click)="hide = !hide" [attr.aria-label]="'Hide password'" [attr.aria-pressed]="hide">
<mat-icon>{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
</mat-form-field>
最佳答案
我的评论中的代码是制作“最简单的自定义表单控件,内部有 Material 输入”。这个想法是创建自定义 ErrorStateMatcher 来询问控件本身。因此,内部 Material 输入在无效时不会显示错误,否则在我们的自定义控件无效时会显示错误
此 ErrorStateMatcher 需要了解我们的控件,因此我们将创建一个构造函数来注入(inject)此控件(我在构造函数中注入(inject)另一个对象“错误”以允许使 Material 输入“无效”)
class CustomFieldErrorMatcher implements ErrorStateMatcher {
constructor(private customControl: FormControl,private errors:any) { }
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
return this.customControl && this.customControl.touched &&(this.customControl.invalid || this.errors);
}
}
.html 就像
<mat-form-field>
<input #input="ngModel" [ngModel]="value" (ngModelChange)="value=$event;onChange($event)"
matInput
[errorStateMatcher]="errorMatcher()"
[placeholder]="placeholder"
[type]="hide ? 'password' : 'text'"
(blur)="onTouched()"
>
<button mat-icon-button matSuffix (click)="hide = !hide" [attr.aria-label]="'Hide password'" [attr.aria-pressed]="hide">
<mat-icon>{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
</button>
<mat-error *ngIf="control?.errors?.required">
Please enter a {{placeholder}}
</mat-error>
<mat-error *ngIf="errors?.errorMatch">
Must match
</mat-error>
</mat-form-field>
最重要的部分是这个
[errorStateMatcher]="errorMatcher()"
请参阅使用 [ngModel] 和 (ngModel), (blur) 将自定义 formControl 标记为“已触摸”。我添加了一个 mat-error *ngIf="errors?.errorMatch。这是一个 @Input()
,它获取 Form 的错误值。这是因为我们制作了一个具有自定义的 FormGroup如果“password”和“repeatpassword”两个字段不匹配,则会出现错误。
我们的自定义表单控件就像
export class CustomSelectComponent implements AfterViewInit, ControlValueAccessor {
control: FormControl
onChange: any = () => { };
onTouched: any = () => { };
value: any;
@Input() disabled: boolean;
@Input() placeholder = '';
@Input() errors:any=null;
errorMatcher() {
return new CustomFieldErrorMatcher(this.control,this.errors)
}
constructor(public injector: Injector) {
}
ngAfterViewInit(): void {
const ngControl: NgControl = this.injector.get(NgControl, null);
if (ngControl) {
setTimeout(() => {
this.control = ngControl.control as FormControl;
})
}
}
了解如何在 ngAfterViewInit 中获取 ngControl、errorMatcher() 如何返回新的 CustomFieldErrorMatcher 以及如何传递“control”和“errors”的值。
我们的 app.component 就像
ngOnInit() {
this.myForm = new FormGroup(
{
password: new FormControl("", Validators.required),
repeatpassword: new FormControl("", Validators.required)
},
this.matchControls("password", "repeatpassword")
);
}
matchControls(field1, field2) {
return (group: FormGroup) => {
const control1 = group.get(field1);
const control2 = group.get(field2);
return control1 && control2 &&
control1.value && control2.value &&
control1.value != control2.value
? { errorMatch: "must match" }: null;
};
}
app.component 的 .html 是
<form [formGroup]="myForm" autocomplete="off">
<app-custom-input placeholder="Password" formControlName="password" >
</app-custom-input>
<app-custom-input placeholder="Repeat password" formControlName="repeatpassword" [errors]="myForm.errors?.errorMatch?myForm.errors:null" >
</app-custom-input>
</form>
在自定义组件上添加了此监听器。您还可以执行“模糊”事件。
https://stackoverflow.com/a/59086644/12425844
@HostListener('focusout', ['$event.target'])
onFocusout() {
this.onTouched();
}
And also calling onTouched when setting any value.
writeValue(value: any) {
this.onTouched();
this.Value = value ? value : '';
}
关于angular - 用于包裹 Angular Material 输入的组件不显示错误样式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58459617/