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 rath
For Angular Material , The below worked for me
@ViewChildren(MatInput) inputs: QueryList <MatInput>;
this.inputs.find(input => !input.ngControl.valid).focus();
@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();
});
}
}
}
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()
Plain HTML solution. If you don't need to be scrolling , just focus on 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();
}
}
This is for template driven form here. We focus on second element of invalidFields cuz first is the whole form which is invalid too.
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!!!
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