web-dev-qa-db-ja.com

Angular 6 Reactive Forms:最初の無効な入力にフォーカスを設定する方法

私のAngular 6 appでは、Reactive Formsを使用しています。

私の目的は、送信するとき、エラーが発生したときに最初の無効な入力にフォーカスを設定することです。

私のフォームは次のようになります:

<form [formGroup]="addItemfForm " (ngSubmit)="onSubmitForm()">

        <div class="form-inline form-group">
          <label class="col-md-2 justify-content-start">
            Libellé du pef
            <span class="startRequired mr-1"> *</span>
          </label>
          <input type="text"
                 maxlength="100"
                 formControlName="libellePef"
                 class="col-md-6 form-control"
                 placeholder="saisie obligatoire"
                 [ngClass]="{ 'is-invalid': submitted && formFiels.libellePef.errors }"/>
          <div *ngIf="submitted && formFiels.libellePef.errors" class="col invalid-feedback">
            <div class="col text-left" *ngIf="formFiels.libellePef.errors.required">Libellé du pef est requis.</div>
          </div>
        </div>

        <div class="form-inline form-group">
          <label class="col-md-2 justify-content-start">
            Code Basicat
            <span class="startRequired mr-1"> *</span>
          </label>
          <input type="text"
                 maxlength="100"
                 formControlName="codeBasicat"
                 class="col-md-3 form-control"
                 placeholder="saisie obligatoire"
                 [ngClass]="{ 'is-invalid': submitted && formFiels.codeBasicat.errors }"/>
          <div *ngIf="submitted && formFiels.codeBasicat.errors" class="col invalid-feedback">
            <div class="text-left" *ngIf="formFiels.codeBasicat.errors.required">Code Basicat est requis.</div>
          </div>
        </div>

        <div class="form-inline form-group">
          <label class="col-md-2 justify-content-start">
            Nom de l'application
            <span class="startRequired mr-1"> *</span>
          </label>
          <input type="text"
                 maxlength="100"
                 formControlName="nomApplication"
                 class="col-md-6 form-control"
                 placeholder="saisie obligatoire"
                 [ngClass]="{ 'is-invalid': submitted && formFiels.nomApplication.errors }"/>
          <div *ngIf="submitted && formFiels.nomApplication.errors" class="col invalid-feedback">
            <div class="text-left" *ngIf="formFiels.nomApplication.errors.required">Nom de l'application est requis.
            </div>
          </div>
        </div>
</form>

TSファイルの下では、フォーム設定は次のようになります:

this.addItemfForm = this.formBuilder.group({
  libellePef: ['', Validators.required],
  codeBasicat: ['', Validators.required ],
  nomApplication: ['', Validators.required ],
  urlCible: [''],
  modeTransfert: [''],
});

autofocusディレクティブを試しましたが、うまくいきませんでした

提案?

7
firasKoubaa

これを試して:

import { Directive, HostListener, ElementRef} from '@angular/core';

@Directive({
  selector: '[focusFirstInvalidField]'
})
export class FocusFirstInvalidFieldDirective {

  constructor(private el: ElementRef) { }

  @HostListener('submit')
  onFormSubmit() {
    const invalidElements = this.el.nativeElement.querySelectorAll('.ng-invalid');
    if (invalidElements.length > 0) {
      console.log(invalidElements[0]); 

      invalidElements[0].focus();
    }
  }
}

デバッグすることを忘れないでください。要素0が私に起こったあなた自身のフォームではないかどうかを確認してください。

1

私の答えは yurzuianswer からインスピレーションを受けています。 nativeElementを使用して、特定のFormControlFormControlを取得するために、彼の答えのロジックを使用しています。

これはそれを行うロジックです:

_const originFormControlNameNgOnChanges = FormControlName.prototype.ngOnChanges;
FormControlName.prototype.ngOnChanges = function () {
  const result = originFormControlNameNgOnChanges.apply(this, arguments);
  this.control.nativeElement = this.valueAccessor._elementRef.nativeElement;
  return result;
};
_

これで、フォームのエラーフィールドは、フィールドが無効であってもnullになります。したがって、無効な最初のフィールドを正確に取得するには、すべてのフィールドをループ処理し、各フィールドの有効性を確認する必要があります。このロジックはonSubmitForm()メソッドで記述できます。このようなもの:

_onSubmitForm() {
  const fieldsToCheck = [
    'codeBasicat',
    'libellePef',
    'nomApplication'
  ];
  for (let i = 0; i < fieldsToCheck.length; i++) {
    const fieldName = fieldsToCheck[i];
    if (this.addItemfForm.get(fieldName).invalid) {
      ( < any > this.addItemfForm.get(fieldName)).nativeElement.focus();
      break;
    }
  }
}
_

ループから抜け出したかったので、意図的に_Array.forEach_の代わりにforを使用しました。

うまくいけば、これはあなたのためのトリックを行う必要があります。

参照用の WorkingサンプルStackBlitz .

5
SiddAjmera

私はディレクティブを使用してそれを行いました。したがって、私のフォームは次のようになります。

<form [formGroup]="userForm" (submit)="saveData()" appFocus >
...
</form>

ディレクティブ自体のコード:

import { Directive, HostListener, Input, ElementRef } from '@angular/core';
import { NgForm } from '@angular/forms';

@Directive({
  selector: '[appFocus]'
})
export class FocusDirective {

  constructor(private el: ElementRef) { }

  @Input() formGroup: NgForm;

  @HostListener('submit', ['$event'])
  public onSubmit(event): void {
    if ('INVALID' === this.formGroup.status) {
      event.preventDefault();

      const formGroupInvalid = this.el.nativeElement.querySelectorAll('.ng-invalid');
      (<HTMLInputElement>formGroupInvalid[0]).focus();
    }
  }
}

ただし、考慮しなければならないコーナーケースが多数あるため、このソリューションは不完全です。たとえば、最初の要素がラジオボタングループの場合はどうなりますか。フォーカスイベントをディスパッチすると、フィールドが自動的にマークされます。次に、angular ads ng-invalidのすべての要素が入力になるわけではありません。

3
piotr szybicki

フォームのsubmit()にこのコードを記述するだけで、最初の無効な入力にフォーカスを設定できます。

   if(this.form.invalid)
    {  
      // Got focus to the error field
    let invalidFields = [].slice.call(document.getElementsByClassName('ng-invalid'));
    invalidFields[1].focus();  

    }
2
Aswathy

このオプションは私には機能しませんが、次のようにコードを変更することで修正できました。

@Directive({
  selector: '[appErrorFocusin]'
})
export class ErrorFocusinDirective {

  selectorToFocus : String = 'textArea,mat-select,select,input,button';

  constructor(private el: ElementRef,
    @Inject(DOCUMENT) private document: Document) { }

  @Input() formGroup: NgForm;

  @HostListener('submit', ['$event'])
  public onSubmit(event): void {
    if ('INVALID' === this.formGroup.status) {
      event.preventDefault();

      const formGroupInvalid = this.el.nativeElement.querySelectorAll('.ng-    invalid,:not(.mat-badge-hidden).mat-badge');
      let elementToOffset = this.getElementToOffset(formGroupInvalid[0]);
      this.document.documentElement.scrollTop = elementToOffset.offsetTop;
      this.setFocusOnError(elementToOffset);
    }
  }


  getElementToOffset(element : any){
    let defaultElement = element;
    while (!(element.parentElement instanceof HTMLFormElement)){
      if (element.parentElement){
        element = element.parentElement;
      }else{
        return defaultElement;
      }
    }
    return element;
  }

  setFocusOnError(elementToOffset : any){
    let listaElementos =     elementToOffset.querySelectorAll(this.selectorToFocus);
    if (listaElementos.length>0){
      listaElementos[0].focus();
    }
  }

}
0
JLazar0