Angular 2 Focus On First Invalid Input After Click/Event
Solution 1:
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
Solution 2:
Unfortunately I can't test this at the moment, so might be a few bugs, 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');
}
}
}
}
Solution 3:
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()
Solution 4:
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!!!
Solution 5:
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! :)
Post a Comment for "Angular 2 Focus On First Invalid Input After Click/Event"