Angular 2 Focus on the first invalid input after Click / Event

I have a strange demand, and I was hoping for some help.

I need to focus on the first found invalid input of the form after clicking the button (do not submit). The form is quite large, so the screen should scroll to the first invalid input.

This AngularJS answer would be what I needed, but didn’t know if such a directive would be as it would be in Angular 2:

Set focus to first invalid entry in AngularJs form

What will be the way Angular 2? Thanks for the help!

+18
source share
9 answers

This works for me. Not the most elegant solution, but given the limitations in Angular that we all experience for this particular task, it does its job.

scrollTo(el: Element): void { if(el) { el.scrollIntoView({ behavior: 'smooth' }); } } scrollToError(): void { const firstElementWithError = document.querySelector('.ng-invalid'); this.scrollTo(firstElementWithError); } async scrollIfFormHasErrors(form: FormGroup): Promise <any> { await form.invalid; this.scrollToError(); } 

This works, allowing you to avoid manipulating the DOM. It just jumps to the first element with .ng-invalid on the page through document.querySelector() which returns the first element in the returned list.

To use this :

 this.scrollIfFormHasErrors(this.form).then(() => { // Run any additional functionality if you need to. }); 

I also posted this on the Angular Github page: https://github.com/angular/angular/issues/13158#issuecomment-432275834

+6
source

Unfortunately, I can’t check this at the moment, so there may be a few errors, but should be mostly there. Just add it to your form.

 import {Directive, Input, HostListener} from '@angular/core'; import {NgForm} from '@angular/forms'; @Directive({ selector: '[scrollToFirstInvalid]' }) export class ScrollToFirstInvalidDirective { @Input('scrollToFirstInvalid') form: NgForm; constructor() { } @HostListener('submit', ['$event']) onSubmit(event) { if(!this.form.valid) { let target; for (var i in this.form.controls) { if(!this.form.controls[i].valid) { target = this.form.controls[i]; break; } } if(target) { $('html,body').animate({scrollTop: $(target.nativeElement).offset().top}, 'slow'); } } } } 
+5
source

I don't know if this is a valid approach or not, but it works fine for me.

 import { Directive, Input, HostListener, ElementRef } from '@angular/core'; import { NgForm } from '@angular/forms'; import * as $ from 'jquery'; @Directive({ selector: '[accessible-form]' }) export class AccessibleForm { @Input('form') form: NgForm; constructor(private el: ElementRef) { } @HostListener('submit', ['$event']) onSubmit(event) { event.preventDefault(); if (!this.form.valid) { let target; target = this.el.nativeElement.querySelector('.ng-invalid') if (target) { $('html,body').animate({ scrollTop: $(target).offset().top }, 'slow'); target.focus(); } } } } 

In HTML

 <form [formGroup]="addUserForm" class="form mt-30" (ngSubmit)="updateUser(addUserForm)" accessible-form [form]="addUserForm"></form> 

I mixed the directive approach of the angularjs view in this. Improvements are welcome !!!

+4
source

If you use AngularMaterial , MdInputDirective has a focus () method that allows you to directly focus on the input field.

In your component, just get a link to all inputs with @ViewChildren annotation, for example:

@ViewChildren(MdInputDirective) inputs: QueryList<MdInputDirective>;

Then setting the focus to the first invalid input is as simple as this:

this.inputs.find(input => !input._ngControl.valid).focus()

+3
source

I created an angular directive to solve this problem. You can check it out here ngx-scroll-to-first-invalid .

Steps:

1.Install the module:

 npm i @ismaestro/ngx-scroll-to-first-invalid --save 

2.Use the NgxScrollToFirstInvalidModule :

 import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {NgxScrollToFirstInvalidModule} from '@ismaestro/ngx-scroll-to-first-invalid'; @NgModule({ imports: [ BrowserModule, NgxScrollToFirstInvalidModule ], bootstrap: [AppComponent] }) export class AppModule { } 

3. Use the directive inside the form:

 <form [formGroup]="testForm" ngxScrollToFirstInvalid> <input id="test-input1" type="text" formControlName="someText1"> <button (click)="saveForm()"></button> </form> 

Hope it helps! :)

+3
source
  @HostListener('submit', ['$event']) onSubmit(event) { event.preventDefault(); if (!this.checkoutForm.valid) { let target; target = $('input[type=text].ng-invalid').first(); if (target) { $('html,body').animate({ scrollTop: $(target).offset().top }, 'slow', ()=> { target.focus(); }); } } } 
+2
source

A simple HTML solution. If you don't need to scroll, just focus on the first valid input, I use:

 public submitForm() { if(this.form.valid){ // submit form } else { let invalidFields = [].slice.call(document.getElementsByClassName('ng-invalid')); invalidFields[1].focus(); } } 

Here for the form with the template. We focus on the second invalidFields element, because the first is also an invalid form.

+2
source

I recommend putting this in the service, for me it works as follows:

 if (this.form.valid) { //submit } else { let control; Object.keys(this.form.controls).reverse().forEach( (field) => { if (this.form.get(field).invalid) { control = this.form.get(field); control.markAsDirty(); } }); if(control) { let el = $('.ng-invalid:not(form):first'); $('html,body').animate({scrollTop: (el.offset().top - 20)}, 'slow', () => { el.focus(); }); } } 
+1
source

For angular material, below works for me

 @ViewChildren(MatInput) inputs: QueryList <MatInput>; this.inputs.find(input => !input.ngControl.valid).focus(); 
+1
source

Source: https://habr.com/ru/post/1013203/


All Articles