Код из моих комментариев - сделать «самый простой пользовательский элемент управления формой, внутри которого есть материал». Идея заключается в создании пользовательского 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), (blur) пометьте пользовательский formControl как "тронутый". Я добавляю mat-error * ngIf = "errors? .ErrorMatch. Это @Input()
, который получает значение ошибки Form. Это потому, что мы создаем FormGroup, в которой есть пользовательская ошибка, если два поля" пароль "и «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;
})
}
}
Посмотрите, как получить ngControl в ngAfterViewInit, как 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;
};
}
.html app.component равен
<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>
stackblitz