J'ai une exigence étrange et j'espérais de l'aide.
Je dois me concentrer sur la première entrée non valide trouvée d'un formulaire après avoir cliqué sur un bouton (ne pas soumettre). Le formulaire est assez grand, et donc l'écran doit défiler jusqu'à la première entrée non valide.
Cette réponse AngularJS serait ce dont j'aurais besoin, mais je ne savais pas si une directive comme celle-ci serait la voie à suivre dans Angular 2:
Définir le focus sur la première entrée invalide sous forme AngularJs
Quelle serait la manière Angular 2 de procéder? Merci pour toute l'aide!
Cela fonctionne pour moi. Ce n'est pas la solution la plus élégante, mais étant donné les contraintes de Angular nous rencontrons tous pour cela tâche particulière, il fait le travail.
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();
}
Cela fonctionne, vous permettant d'éviter la manipulation du DOM. Il va simplement au premier élément avec .ng-invalid
Sur la page à travers la document.querySelector()
qui retourne le premier élément dans la liste retournée.
Pour l'utiliser :
this.scrollIfFormHasErrors(this.form).then(() => {
// Run any additional functionality if you need to.
});
J'ai également posté ceci sur la page Github d'Angular: https://github.com/angular/angular/issues/13158#issuecomment-432275834
Malheureusement, je ne peux pas tester cela pour le moment, il pourrait donc y avoir quelques bugs, mais devrait être principalement là. Ajoutez-le simplement à votre formulaire.
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');
}
}
}
}
Je ne sais pas si c'est une approche valide ou non, mais cela fonctionne très bien pour moi.
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();
}
}
}
}
En HTML
<form [formGroup]="addUserForm" class="form mt-30" (ngSubmit)="updateUser(addUserForm)" accessible-form [form]="addUserForm"></form>
J'ai mélangé l'approche de la directive de forme accessible angularjs dans ceci. Les améliorations sont les bienvenues !!!
Si vous utilisez AngularMaterial , le MdInputDirective a une méthode focus () qui vous permet de vous concentrer directement sur le champ de saisie.
Dans votre composant, obtenez simplement une référence à toutes les entrées avec l'annotation @ ViewChildren , comme ceci:
@ViewChildren(MdInputDirective) inputs: QueryList<MdInputDirective>;
Ensuite, définir le focus sur la première entrée non valide est aussi simple que cela:
this.inputs.find(input => !input._ngControl.valid).focus()
Solution HTML simple. Si vous n'avez pas besoin de faire défiler, concentrez-vous simplement sur la première entrée valide, j'utilise:
public submitForm() {
if(this.form.valid){
// submit form
} else {
let invalidFields = [].slice.call(document.getElementsByClassName('ng-invalid'));
invalidFields[1].focus();
}
}
C'est pour le formulaire piloté par modèle ici. Nous nous concentrons sur le deuxième élément de invalidFields car cuz est d'abord la forme entière qui est invalide aussi.
@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();
});
}
}
}
J'ai créé une Angular angulaire pour résoudre ce problème. Vous pouvez la vérifier ici ngx-scroll-to-first-invalid .
Pas:
1.Installez le module:
npm i @ismaestro/ngx-scroll-to-first-invalid --save
2.Importez le 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.Utilisez la directive dans un formulaire:
<form [formGroup]="testForm" ngxScrollToFirstInvalid>
<input id="test-input1" type="text" formControlName="someText1">
<button (click)="saveForm()"></button>
</form>
J'espère que cela aide! :)
Je recommande de mettre cela dans un service, pour moi, cela a fonctionné comme ceci:
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();
});
}
}