Skip to content Skip to sidebar Skip to footer

Angular 6 Reactive Forms : How To Set Focus On First Invalid Input

Under my Angular 6 app , i'm using Reactive Forms . My purpose is when submitting , i want to set focus on first invalid input when error. My form looks like this :

Solution 1:

Use below code in your submit.

for (const key of Object.keys(this.addressForm.controls)) {
      if (this.addressForm.controls[key].invalid) {
        const invalidControl = this.el.nativeElement.querySelector('[formcontrolname="' + key + '"]');
        invalidControl.focus();
        break;
     }
}

this.addressForm will be your FormGroup.

We don't even need directive here.

Solution 2:

My Answer is inspired from yurzui's answer here. I'm using the logic from his answer to get the nativeElement of a particular FormControl by using it's FormControl.

This is the logic that does that:

const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function () {
  const result = originFormControlNameNgOnChanges.apply(this, arguments);
  this.control.nativeElement = this.valueAccessor._elementRef.nativeElement;
  return result;
};

Now, the form's errors field would be null even though it's fields are invalid. So to get the exact first field that's invalid, we'll have to loop through all the fields and check for validity for each of them. I can write this logic in the onSubmitForm() method. Something like this:

onSubmitForm() {
  const fieldsToCheck = [
    'codeBasicat',
    'libellePef',
    'nomApplication'
  ];
  for (let i = 0; i < fieldsToCheck.length; i++) {
    const fieldName = fieldsToCheck[i];
    if (this.addItemfForm.get(fieldName).invalid) {
      ( < any > this.addItemfForm.get(fieldName)).nativeElement.focus();
      break;
    }
  }
}

I've deliberately used for instead of Array.forEach as I wanted to break from the loop.

Hopefully this should do the trick for you.

Here's a Working Sample StackBlitz for your ref.

Solution 3:

I did that using directives. So My form would look like this:

<form [formGroup]="userForm" (submit)="saveData()"appFocus >
...
</form>

and the code for the directive itself:

import { Directive, HostListener, Input, ElementRef } from '@angular/core';
import { NgForm } from '@angular/forms';

@Directive({
  selector: '[appFocus]'
})
export classFocusDirective{

  constructor(private el: ElementRef) { }

  @Input() formGroup: NgForm;

  @HostListener('submit', ['$event'])public onSubmit(event): void {
    if ('INVALID' === this.formGroup.status) {
      event.preventDefault();

      const formGroupInvalid = this.el.nativeElement.querySelectorAll('.ng-invalid');
      (<HTMLInputElement>formGroupInvalid[0]).focus();
    }
  }
}

However this solution is incomplete as there is a lot of corner cases that have to be considered. For example what if the first element is radio button group. Dispatching focus event will automatically mark the filed. Second not every element to which angular ads ng-invalid will be an input.

Solution 4:

We can set focus on first invalid input simply by just write this code in the submit() of the form.

if(this.form.invalid)
    {  
      // Got focus to the error fieldlet invalidFields = [].slice.call(document.getElementsByClassName('ng-invalid'));
    invalidFields[1].focus();  

    }

Solution 5:

Try this:

import { Directive, HostListener, ElementRef} from'@angular/core';

@Directive({
  selector: '[focusFirstInvalidField]'
})
exportclassFocusFirstInvalidFieldDirective {

  constructor(private el: ElementRef) { }

  @HostListener('submit')
  onFormSubmit() {
    const invalidElements = this.el.nativeElement.querySelectorAll('.ng-invalid');
    if (invalidElements.length > 0) {
      console.log(invalidElements[0]); 

      invalidElements[0].focus();
    }
  }
}

Remember to debug, see if element 0 is not your own form as it happened to me, so see right what field it is reporting as the first and put the position right.

Post a Comment for "Angular 6 Reactive Forms : How To Set Focus On First Invalid Input"