【问题标题】:Angular 6 Reactive Forms : How to set focus on first invalid inputAngular 6 Reactive Forms:如何将焦点设置在第一个无效输入上
【发布时间】:2019-05-24 06:21:59
【问题描述】:

在我的 Angular 6 应用下,我使用的是 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 指令,但没有成功

建议?

【问题讨论】:

    标签: javascript angular html rxjs angular-reactive-forms


    【解决方案1】:

    在您的提交中使用以下代码。

    for (const key of Object.keys(this.addressForm.controls)) {
          if (this.addressForm.controls[key].invalid) {
            const invalidControl = this.el.nativeElement.querySelector('[formcontrolname="' + key + '"]');
            invalidControl.focus();
            break;
         }
    }
    

    this.addressForm 将是您的 FormGroup。

    这里甚至不需要指令。

    【讨论】:

    • this.el 的类型是什么?
    • 将“private el: ElementRef”添加到您的构造函数(ElementRef form '@angular/core')
    • 它对我有用,但仅适用于输入字段,不适用于下拉字段。
    【解决方案2】:

    只需在表单的 submit() 中编写此代码,我们就可以将焦点设置在第一个无效输入上。

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

    【讨论】:

      【解决方案3】:

      试试这个:

      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 是否不是你自己的表单,因为它发生在我身上,所以看看它作为第一个报告的字段是什么并将位置正确。

      【讨论】:

      • querySelectorAll() 返回一个 NodeListOf 并且 Element 类型没有 focus() 方法
      • @TheCodeFox 对我来说有效,请检查您使用的是哪个版本
      【解决方案4】:

      这个选项对我不起作用,但我设法通过更改代码来修复它,如下所示:

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

      【讨论】:

        【解决方案5】:

        我使用指令来做到这一点。所以我的表格看起来像这样:

        <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 都会作为输入的元素。

        【讨论】:

          【解决方案6】:

          我的答案的灵感来自yurzui 的answer 这里。我正在使用他的答案中的逻辑来获取特定FormControl 的nativeElement,方法是使用它的FormControl。

          这是这样做的逻辑:

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

          现在,表单的错误字段将为空,即使它的字段无效。因此,要获得确切的第一个无效字段,我们必须遍历所有字段并检查每个字段的有效性。我可以在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;
              }
            }
          }
          

          我故意使用for 而不是Array.forEach,因为我想打破循环。

          希望这对你有用。

          这里有一个Working Sample StackBlitz 供您参考。

          【讨论】:

          • 我需要动态获取第一个错误输入的引用,并设置焦点
          • @firasKoubaa,我已经更新了我的答案。请检查是否适合您。我还添加了一个您可能想要查看的工作示例 StackBlitz。希望对您有所帮助。
          • @SiddAjmera 你可以使用Array.some 来突破 :) 不过,我想找到 DOM 顺序中的第一个无效字段...
          猜你喜欢
          • 2019-01-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-10-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-19
          相关资源
          最近更新 更多