包角材料输入的组件未显示错误样式

时间:2019-10-19 00:44:56

标签: angular angular-material

我想在组件内封装一个角材料的matInput以便在应用程序的其他地方重用它,因为我需要管理其内部状态,以将输入类型从文本更改为密码,反之亦然。

我通过实现ControlValueAccessor做到了这一点,但是没有显示验证错误的样式。

密码字段组件:

Minor.cs

密码字段模板:

Minor.cs

enter image description here

1 个答案:

答案 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>

stackblitz

将此侦听器添加到自定义组件上。您也可以将其“模糊化”。

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 : '';
}