Search code examples
angulartypescriptangular-materialangular-errorhandlermat-error

After call setErrors method in FormControl doesn't show inmediately (Angular Material)


When I call setErrors for a textbox FormControl instance as shown in the images below, there should be an error message immediately but I don't see it until I remove the entire text.

A text input with a value entered, the input has a required field indicator but does not show an error state

The same text input with no value entered, an error message is shown and the control is now red

Here is my HTML code:

<mat-form-field appearance="outline" class="w-100">
  <mat-label>Registro Venta</mat-label>
  <input type="text"
  placeholder="Buscar..."
  matInput
  (input)="setFilter(input.value)"
  [matAutocomplete]="auto"
  [formControl]="registroVentaControl"
  (keyup.enter)="addOption()"

  #input required />
  <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn.bind(this)"
      (optionSelected)="optionSelected($event.option)"
      >
      <mat-option *ngFor="let option of registroVentaList" [value]="option">
          {{option.displayRegistroVentaConsecutivo}}
      </mat-option>
  </mat-autocomplete>
  <mat-error>El registro de venta no esta activo en SAP</mat-error>
</mat-form-field>

And here is when setErrors is called:

displayFn(data: RegistroVentaDto): string {

  let registroVentaDto = data as RegistroVentaDto;
  let displayRegistroVentaTemp: string;

  if (registroVentaDto.registroVentaConsecutivo) {
    displayRegistroVentaTemp = registroVentaDto.displayRegistroVentaConsecutivo;
  }

  if (!displayRegistroVentaTemp)
    return;

  if (!registroVentaDto.registroVentaId || registroVentaDto.registroVentaId <= 0) {
    displayRegistroVentaTemp = registroVentaDto.registroVentaConsecutivo;
  }
 this.contractService.getStatusSales(registroVentaDto.registroVentaConsecutivo).subscribe(
    res => {
      if (res) {
        this.enableUpdateButton = true;
      }
      else {
        this.notificationService.openError(`Registro de venta '${displayRegistroVentaTemp}' no activo en SAP.`);
        this.registroVentaControl.setErrors({ 'invalid': true });//Here
        this.registroVentaControl.updateValueAndValidity({ onlySelf: false, emitEvent: true });//Here
        this.enableUpdateButton = false;
      }
    });

  return displayRegistroVentaTemp;
}

I tried to use this approach but it didn't work:

this.registroVentaControl.updateValueAndValidity({ onlySelf: false, emitEvent: true });

How can I validate the input field without having to remove the value?


Solution

  • You can try using changeDetectorRef

    • Remove updateValueAndValidity method calls
    • Mark the control as touched when it has value and untouched when the input is empty
    • Set error value to true or false respectively
    • Call detectChanges to force Angular to render the error state
    constructor(private changeDetectorRef: ChangeDetectorRef) { }
    
    ngOnInit() {
        this.registroVentaControl.valueChanges.subscribe(val => {
          if(val.length === 0){
            this.registroVentaControl.markAsUntouched()
            this.registroVentaControl.setErrors({'invalid': false})
            this.changeDetectorRef.detectChanges(); 
          }
        })
    }
    
    this.contractService
      .getStatusSales(registroVentaDto.registroVentaConsecutivo)
      .subscribe((res) => {
        if (res) {
          this.enableUpdateButton = true;
        } else {
          this.notificationService.openError(
            `Registro de venta '${displayRegistroVentaTemp}' no activo en SAP.`
          );
          this.registroVentaControl.markAsTouched();
          this.registroVentaControl.setErrors({ invalid: true });
          this.changeDetectorRef.detectChanges();
          this.enableUpdateButton = false;
        }
      });