web-dev-qa-db-fra.com

Angular 2 Focus sur la première entrée invalide après Click / Event

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!

18
Jared Martinez

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

6
jburtondev

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');
      }
    }
  }
}
5
Baconbeastnz

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 !!!

4
Devang Naghera

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()

3
Guillaume

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.

2
Rabov
  @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
devson

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! :)

2
ismaestro

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();
    });
  }
}
1