angulartypescriptangular-reactive-formsangular-template-form

Why don't getters for form errors work in my Angular Reactive Form component template, but a direct reference does?


quick question. It's one of those small annoying bugs. I have an Angular reactive form in a lazy-loaded signup module. The code structure is as follows:

TS

get email() {
  return this.myForm.get('email');
}
// This code works from within the component, but not when referenced from within the template.

HTML

<div class="errors" *ngIf="email.errors?.required && email.touched">Must enter email</div>

In the template, the div is never shown since the expression never evaluates to true, even when there are errors present. I've checked in the console.

What's more confusing is that I have no problem using the getter from within the component.ts, in fact, the way I console.log it is by writing console.log(this.email.errors). And That works fine. But not in the template.

My messy solution is to access the errors directly, and that does work:

<div class="errors" *ngIf="myForm.controls.email.errors.required && myForm.controls.email.touched">
  Must enter email
</div>
// This works! As you can see the expression is very long and messy.

Any help appreciated!


Solution

  • Do you by any chance use a template reference variable #email in your input field, like: <input #email ...>?

    That would explain the problem. I made a Stackblitz which reproduces the error: https://stackblitz.com/edit/angular-qyt7kb

    In that example the firstName input has the template reference variable #firstName AND the error div is never shown. The lastName error div is shown correctly.

    <form [formGroup]="form">
    
        <label>
          First Name:
          <input #firstName type="text" formControlName="firstName">
        </label>
        <div *ngIf="firstName.errors?.required && firstName.touched">*FIRST Name is Required</div>
    
        <label>
          Last Name:
          <input type="text" formControlName="lastName">
        </label>
        <div *ngIf="lastName.errors?.required && lastName.touched">*LAST name is Required</div>
    
    </form>
    

    So it seems that in the template, the template reference variable has precedence over the getter of the same name in the TS file. In the example you can see that the console.log prints the errors correctly just as you said.