typescript - فیلد فرم سفارشی Angular Material خطای 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) پیچیده و این کامپوننت را به عنوان یک کامپوننت واکنشی استفاده کند. بنابراین کنترل سفارشی من داخل خود یک کنترلی دارد که وضعیتها را مدیریت میکند.

من تمام موارد غیرضروری را از کد اصلی خود حذف کردم و چند نکته جزئی از راهنما را اضافه کردم.

پس این هم نمونه کاری من (به صورت ساده شده) است - برای مواقعی که کسی دوباره به آن نیاز داشته باشد.