crunch
crunch

Reputation: 375

Angular formGroup, scroll to first Invalid input in a scrolling div

I have a significantly large form and the way the application is laid out, the form is in a scrolling div, so I can't use (because it doesn't seem to work) something like:

private scrollToFirstInvalidControl() {
        const firstInvalidControl: HTMLElement = this.el.nativeElement.querySelector(
            "form .ng-invalid"
        );

        firstInvalidControl.focus();
    }

This is a one-off thing, I also don't want it as a directive, it's just overkill, I just want to focus on the first invalid object in the formGroup after I run this.form.markAllAsTouched(); and walk away.. smooth scrolling would be a nice bonus, but not a huge deal.

Upvotes: 2

Views: 6268

Answers (3)

Sasa Jevremovic
Sasa Jevremovic

Reputation: 61

I have created a directive for this, it's easier and reusable:

@Directive({
  selector: '[scrollToFirsInvalidFormField]',
  standalone: true,
})
export class ScrollToFirstInvalidFormFieldDirective {
  private readonly elementRef = inject(ElementRef);

  @HostListener('ngSubmit')
  onSubmit(): void {
    this.scrollToFirstInvalidField();
  }

  private scrollToFirstInvalidField(): void {
    const invalidFields =
      this.elementRef.nativeElement.querySelectorAll('.ng-invalid');
    const firstInvalidField = invalidFields[0];

    if (!firstInvalidField) {
      return;
    }

    firstInvalidField.scrollIntoView({
      behavior: 'smooth',
      block: 'center',
    });

    firstInvalidField.classList.add('ng-dirty');
  }
}

On the form you need to add following:

<form [formGroup]="formService.form" (ngSubmit)="submit()">
   <button type="submit" />
</form>

Upvotes: 0

Arsen Khachaturyan
Arsen Khachaturyan

Reputation: 8330

You can also use the ElementRef and scrollIntoView for navigating close to invalid element:

private scrollToFirstInvalidControl(): void {
    const formElement = elementRef.nativeElement.querySelector('.form'); // get the form by class name
    
    if (formElement) {
        const firstErrorElement = formElement.querySelector('.ng-invalid'); // get the first error with ng invlalid input class name
        
        if (firstErrorElement) {
            firstErrorElement.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' }); // you can also use block: 'start' for closer navigation
        }
    }
}  

Upvotes: 0

Misha Mashina
Misha Mashina

Reputation: 2113

You can try like this (change formId to actual id of your form:

private scrollToFirstInvalidControl() {
    let form = document.getElementById('formId'); // <-- your formID
    let firstInvalidControl = form.getElementsByClassName('ng-invalid')[0];
    firstInvalidControl.scrollIntoView();
    (firstInvalidControl as HTMLElement).focus();
}

Upvotes: 6

Related Questions