H. Soares
H. Soares

Reputation: 203

Why is my mat-error only shown if I click two times on button?

I have a search bar that retrieves data from my database, whenever the search is successful the data retrieved populates a mat table, so far so good and it works.

The problem I'm facing is when that search is unable to retrieve anything from the database because the user provided invalid information.

When I provide data to the input form and the HTTP Response fails, a mat-error is supposed to trigger instantly, however that only happens if I click on the search button two times.

This is the code I have:

MyComponent.component.ts

public inputForm: FormControl;

ngOnInit() {
  this.inputForm = new FormControl('');
}

private searchForReturnByOrderNumber(value) {
  this.rest.getOrderByNumber(value).subscribe((orderNrData: {}) => {
    if (Object.entries(orderNrData).length !== 0) {
      this.rest.getReturnByOrderNumber(value).subscribe((returnOrdNrData: Return[]) => {
        if (Object.entries(returnOrdNrData).length !== 0) {
          this.returns = returnOrdNrData;
          this.setReturns();
        } else {
          this.openDialog(orderNrData, returnOrdNrData);
        }
      }, error => {
        if (error.status === 404) {
          this.openDialog(orderNrData, []);
        }
      });
    } else {
      this.inputForm.setErrors({ 'invalid': true });
    }
  }, error => {
    this.inputForm.setErrors({ 'invalid': true });
  });
}

private isInputInvalid() {
  if (this.inputForm.hasError('invalid')) {
    return true;
  }
  return false;
}

private getErrorMessage() {
  return this.inputForm.invalid ? 'Invalid Search!' : '';
}

MyComponent.component.html

<div class="row">
  <div class="col-2"></div>

  <div class="col-8 search-div">

    <mat-form-field class="search-form-field" appearance="outline">
      <mat-label>Search</mat-label>

      <input matInput class="search-input" placeholder="Ex: EU030327" [formControl]="inputForm" #searchInput>
      <mat-error *ngIf="isInputInvalid()">{{ getErrorMessage() }}</mat-error>

    </mat-form-field>

    <span matSuffix>
      <button mat-flat-button class="search-btn" color="accent" [disabled]="isInputInvalid()"
        (click)="searchForReturnByOrderNumber(searchInput.value)"><i class="fa fa-search"></i></button>
    </span>

  </div>

  <div class="col-2"></div>
</div>

If I input some invalid data (data that is not present in the database) and press my "search-btn", my mat error won't appear, only if I press the button again.

I've debugged this and the code correctly enters the intended code:

} else {
  this.inputForm.setErrors({ 'invalid': true });
}

It sets the inputForm as invalid but the mat-error won't appear until I click the button again...

Anyone knows what might be wrong?

Thanks!

Upvotes: 0

Views: 1769

Answers (3)

H. Soares
H. Soares

Reputation: 203

I was able to fix my problem so I'll post my solution for anyone who might bump into this issue...

I created a boolean isInputInvalid and set it to true by default. Whenever I press my search button, the HTTP request is executed and, depending on the response, the isInputInvalid boolean is set to true or false. If the HTTP response is invalid I set it to true, if it's valid I set it to false.

Then, if the boolean is true, I set my Form Control as invalid.

In my HTML file I surrounded my input with a <form> and created a [formGroup]. For error validation I set the *ngIf inside <mat-error> equal to searchForm.controls['inputForm'].invalid.

Here is my code:

MyComponent.component.ts

private searchForm: FormGroup;
private isInputInvalid: boolean = true;

ngOnInit() {
  this.searchForm = new FormGroup({
    inputForm: new FormControl('')
  });
}

private searchForReturnByOrderNumber(value) {
  this.rest.getOrderByNumber(value).subscribe((orderNrData: {}) => {
    if (Object.entries(orderNrData).length !== 0) {
      this.rest.getReturnByOrderNumber(value).subscribe((returnOrdNrData: Return[]) => {
        if (Object.entries(returnOrdNrData).length !== 0) {
          this.isInputInvalid = false;
          this.returns = returnOrdNrData;
          this.setReturns();
        } else {
          this.isInputInvalid = false;
          this.openDialog(orderNrData, returnOrdNrData);
        }
      }, error => {
        this.isInputInvalid = true;
      });
    } else {
      this.isInputInvalid = true;
    }
  }, error => {
    this.isInputInvalid = true;
  });

  if(this.isInputInvalid){
    this.searchForm.controls['inputForm'].setErrors({ 'invalid': true });
  }
}

private getErrorMessage() {
  return this.searchForm.controls['inputForm'].invalid ? 'Invalid Search!' : '';
}

MyComponent.component.html

<div class="row">
  <div class="col-2"></div>

  <div class="col-8 search-div">
    <form [formGroup]="searchForm">
      <mat-form-field class="search-form-field" appearance="outline">
        <mat-label>Search</mat-label>

        <input matInput class="search-input" placeholder="Ex: EU030327" formControlName="inputForm" #searchInput>
        <mat-error *ngIf="searchForm.controls['inputForm'].invalid">{{ getErrorMessage() }}</mat-error>

      </mat-form-field>
    </form>

    <span matSuffix>
      <button mat-flat-button class="search-btn" color="accent" [disabled]="searchForm.controls['inputForm'].invalid"
        (click)="searchForReturnByOrderNumber(searchInput.value)"><i class="fa fa-search"></i></button>
    </span>

  </div>

  <div class="col-2"></div>
</div>

Upvotes: 1

Caleb Kent
Caleb Kent

Reputation: 33

Does your mat-error have to be ngIf*? Can it not just use ngClass?

Would it not work if you had a property of isInputInvalid, set to false intially, and if your search method returns 'invalid', then set your isInputInvalid property to true?

Then you can just use ngClass:

<mat-error [ngClass]="{'your-class': isInputInvalid}">{{ getErrorMessage() }}</mat-error>

EDIT:

HTML:

<mat-error [ngClass="{'show-mat-error': isInputInvalid}">{{ getErrorMessage() }}</mat-error>

TS - you need an 'isInputInvalid' property to change:

isInputInvalid: boolean = false;

TS - you need to implement a method along these lines to change the value of 'isInputInvalid':

if (this.inputForm.hasError('invalid')) {
   this.isInputInvalid = true;
} else {
  this.isInputInvalid = false;
}

CSS:

mat-error {
display: none;
}

.show-mat-error {
display: block;
}

I would probably also do something like this in your component so that the error is hidden each time a user navigates to the page:

    constructor(private router: Router) {

  router.events.subscribe((event: Event) => {

    if (event instanceof NavigationStart) {
      this.isInputInvalid = false;
    }
  });

I hope this helps!

Upvotes: 1

Kevin FONTAINE
Kevin FONTAINE

Reputation: 855

The condition of your *ngIf is not recalculated. I think you can replace it by something like :

<mat-error *ngIf="inputForm?.hasError('invalid')">{{ getErrorMessage() }}</mat-error>

Also, you don't need the condition inside the getErrorMessage() method. The message will only be displayed if you have an error to start with. You might have a race condition leading this method to return an empty string. Try simplifying it.

Upvotes: 1

Related Questions