我想在组件内封装一个角材料的matInput以便在应用程序的其他地方重用它,因为我需要管理其内部状态,以将输入类型从文本更改为密码,反之亦然。
我通过实现ControlValueAccessor做到了这一点,但是没有显示验证错误的样式。
密码字段组件:
Minor.cs
密码字段模板:
Minor.cs
答案 0 :(得分:2)
我评论中的代码是“最简单的自定义表单控件,内部具有材料输入”。这个想法是创建询问控件本身的自定义ErrorStateMatcher。因此,内部材料输入显示错误不是在无效时显示错误,否则在自定义控件无效时显示错误
此ErrorStateMatcher需要了解我们的控件,因此我们将创建一个构造函数以注入该控件(我在构造函数中注入另一个对象“错误”以使材料输入“无效”
)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),(模糊)将自定义formControl标记为“ touched”。我添加了mat-error * ngIf =“ errors?.errorMatch。这是一个@Input()
,它获取Form的error值。这是因为如果两个字段“密码”和“重复密码”不匹配。
我们的自定义表单控件就像
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 : '';
}