0

I have an input field. The field should not be empty. If it is, I want Bootstrap's is-invalid class to be assigned to it. The problem I am facing is that if I just click on the field, do not type anything and then leave the field, the field doesn't turn red (has-error doesn't get assigned to the field). However if I type something and delete it completely and then leave the field, the field turns red.

The HTML is

<input id="firstname" type="text" class="form-control" formControlName="firstName" [ngClass]="validateField('firstName')" (blur)="setFieldStatus('firstName')" required>

.ts is

  validateField(field:string){
    console.log("validating field: " +field)
    return this.helper.displayFieldCss(this.signupForm,field);
  }

 /*mark a control field as touched on losing focus from that field.*/
  setFieldStatus(field:string){
    console.log("inside setFieldStatus for field "+field)
    const control = this.signupForm.get(field);         
      if (control instanceof FormControl) {             
        control.markAsTouched({ onlySelf: true }); 
      }
  };

helper.ts

 displayFieldCss(form: FormGroup, field: string) {
    console.log("in displayFieldCss for field "+field);
    if(!form.get(field).pristine) { //check for validity only if the user has interacted with the field. Without this check, the default field will have red border.
      let fieldValid: boolean = form.get(field).valid;
      return {
        'is-invalid': !fieldValid, // if fieldvalid returns false i.e. field is not valid then we want has-error css. So set it to true (opp. of fieldValid)
        'is-valid': fieldValid //if fieldvalid returns true i.e. field is valid then we want has-success css. So set it to true (value of fieldValid)
        //'has-feedback': this.isFieldValid(field)
      }
    } else {//if field is pristine (not touched by user at all) then dont display error or success
      return {
        'is-invalid': false, // if field is pristine (not touched by user at all) then dont display error or success
        'is-valid': false//if field is pristine (not touched by user at all) then dont display error or success
      }
    }
  }

My logic is, in template, ngClass gets is-valid or is-invalid depending on whether user has interacted with the field or not. I do not want the field to be red when the form loads up (pristine check handles that)

when the user leaves the field, I mark the field touched. Now, I can see the issue that [ngClass] gets assigned when the field is created but it doesn't get updated when (blur) is called. I need to update [ngClass] when (blur) is triggered but don't know how to. blur is handled in component's class while [ngClass] is in HTML.

Manu Chadha
  • 15,555
  • 19
  • 91
  • 184

2 Answers2

1

Use the Renderer2 to set the class attribute on your component programmatically on the blur event:

template (html)

<my-element (blur)="onBlur()"></my-element>

component class (ts)

@ViewChild('my-element', {read: ElementRef}) el: ElementRef;
constructor(private renderer: Renderer2) {}

onBlur() {
  const classVal = some condition ? 'is-valid' : 'is-invalid';
  this.renderer.setAttribute(this.el.nativeElement, 'class', classVal);
}

You could add the this.renderer.setAttribute... line to the function that currently runs on blur. There are also other methods you could use like addClass and removeClass -- I've linked the API above.

vince
  • 7,808
  • 3
  • 34
  • 41
  • Didn't work. `if(fieldValid) { this.renderer.setAttribute(this.el.nativeElement, 'class', 'is-valid'); this.renderer.removeAttribute(this.el.nativeElement, 'class', 'is-invalid');` I inspected the element. the classes were not added. However, once I type something in the field then the classes get added (the old code) – Manu Chadha Feb 14 '18 at 18:53
  • also, I had to use `el.nativeElement`. using only `el` caused runtime exception – Manu Chadha Feb 14 '18 at 18:55
  • Yes, sorry forgot to include the .nativeElement, editing my answer to include that now. Can you create a minimal reproduction in a Stackblitz? This approach should work, so there must be something else affecting it. I'd be happy to take a look. – vince Feb 14 '18 at 19:07
  • 1
    I was able to solve the problem. I should have been checking `touched` instead of `pristine` status. https://stackoverflow.com/questions/25025102/angular-difference-between-pristine-dirty-and-touched-untouched. Changed the condition in `displayFieldCss` and happy days! No need for `blur` check as well. – Manu Chadha Feb 14 '18 at 19:10
-1

I was able to solve the problem. I should have been checking touched instead of pristine status. stackoverflow.com/questions/25025102/…. Changed the condition in displayFieldCss and happy days! No need for blur check as well.

Manu Chadha
  • 15,555
  • 19
  • 91
  • 184