Angular 2 Focus on first invalid input after Click

2020-05-23 02:44发布

I have an odd requirement and was hoping for some help.

I need to focus on the first found invalid input of a form after clicking a button (not submit). The form is rather large, and so the screen needs to scroll to the first invalid input.

This AngularJS answer would be what I would need, but didn't know if a directive like this would be the way to go in Angular 2:

Set focus on first invalid input in AngularJs form

What would be the Angular 2 way to do this? Thanks for all the help!

9条回答
贪生不怕死
2楼-- · 2020-05-23 03:01

I recommend putting this in a service, for me it worked like this:

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();
    });
  }
}
查看更多
家丑人穷心不美
3楼-- · 2020-05-23 03:02

This works for me. Not the most elegant solution, but given the constraints in Angular we are all experiencing for this particular task, it does the 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 evade manipulating the DOM. It simply goes to the first element with .ng-invalid on the page through the document.querySelector() which returns the first element in the returned list.

To use it:

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

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

查看更多
混吃等死
4楼-- · 2020-05-23 03:02

I don't know if this is valid approach or not but this is working great 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 have mixed the approach of angularjs accessible form directive in this. Improvements are welcomed!!!

查看更多
Fickle 薄情
5楼-- · 2020-05-23 03:05

I've created an Angular directive to solve this problem. You can check it here ngx-scroll-to-first-invalid.

Steps:

1.Install the module:

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

2.Import 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 a form:

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

Hope it helps! :)

查看更多
Root(大扎)
6楼-- · 2020-05-23 03:07

If you are using AngularMaterial, the MdInputDirective has a focus() method which allow you to directly focus on the input field.

In your component, just get a reference to all the inputs with the @ViewChildren annotation, like this:

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

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

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

查看更多
7楼-- · 2020-05-23 03:10
  @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();
            });
        }
      }
   }
查看更多
登录 后发表回答