循环遍历表单,重点关注必填但无效字段

问题描述

StackBlitz here. 请注意,addShortcut() 函数用于创建如下所述的热键。

我想在表单中动态移动,只关注无效字段,但允许用户根据需要遍历这些无效字段。

例如,如果我有一个表单,其中 addressphoneemail 输入是唯一的必填字段,则用户可以按下按钮并在这些特定字段之间循环.

现在,假设他们正在 3 个字段之间移动,并填写 address 字段。这不再无效,所以现在表单应该只循环/循环遍历 phoneemail 输入字段。

我正在使用自定义热键功能,以便在用户按下 alt + a 时设置焦点。用户可以再次按下此按钮,焦点将转到下一个必填字段。

当应用运行时,ngOnInit() 构建表单,并触发一个函数来查找无效字段:

findInvalidFields() {
  this.invalidFields = [];
  const controls = this.myForm.controls;

  for (const name in controls) {
    if (controls[name].invalid) {
      this.invalidFields.push(name);
    }
  }
}

this.invalidFields 现在是仅包含无效字段的数组:

[
  "customerAddress","customerPhone","customerEmail"
]

按下热键,我运行一个函数来设置焦点,然后移动数组使其转到下一个无效项。:

... {
  this.setFocus(this.invalidFields[0]);
  this.invalidFields.push(this.invalidFields.shift());
}

现在的问题是,即使我填写了一个表单,焦点也会在整个原始列表中来回移动——这是意料之中的,因为无效字段仅在 OnInit 中被发现......当然,我可以运行findInvalidFields() 每次按下热键时,我的热键功能如下所示:

... {
  this.findInvalidFields();
  this.setFocus(this.invalidFields[0]);
  this.invalidFields.push(this.invalidFields.shift());
}

这适用于获取正确的无效字段,但不再“循环”无效字段,因为表单每次运行函数时都会返回特定的控件顺序,因此会卡在输入上,直到它不再无效。

有没有办法动态“循环”输入字段而不会“卡住”无效字段?

Stackblitz.

解决方法

这是我的快速而肮脏的解决方案。它可能会被清理很多,但它似乎有效。

首先我们需要监听相关的按键并触发我们的函数来更新焦点,我使用了向右箭头键来简化它,

  @HostListener('window:keyup',['$event'])
  keyEvent(event: KeyboardEvent) {
    if (event.key === 'ArrowRight') {
      this.focusNext();
    }
  }

那么focusNext()函数就是这个样子,

  private focusNext(): void {
    let focusNextControl: boolean = false;
    let focusSet: boolean = false;
    let firstKey: string;

    let currentControlInvalid: boolean;
    Object.keys(this.myForm.controls).forEach((key,index) => {
      currentControlInvalid = this.myForm.controls[key].invalid;
      //Find the first invalid control,we will need it later in the case that the active focus is on the last control on the form
      if(!firstKey && currentControlInvalid){
        firstKey = key;
      }
      //If the current control is invalid 
      //AND the previous control is the currently focused one 
      //AND we haven't already set the focus.. Then set the focus on this control.
      if(currentControlInvalid && focusNextControl && !focusSet){
        this.elRef.nativeElement.querySelector('[formcontrolname = "' + key + '"]').focus();
        focusSet = true;
      }
      else if(this.myForm.controls[key] == this.formFocus){
        focusNextControl = true;
      }
      else if(!focusSet && focusNextControl && index == Object.keys(this.myForm.controls).length - 1){
        //If we are on the last control and we need to set the 'next' control 
        //AND we haven't already focused on a control
        //Then we need to set the focus to the first invalid control we found
        this.elRef.nativeElement.querySelector('[formcontrolname = "' + firstKey + '"]').focus();
      }
    });
  }

其中 this.formFocus 是我们用来跟踪主动聚焦表单控件的字段,

  private formFocus: FormControl;
  public onControlFocus(control: string): void {
    this.formFocus = this.myForm.controls[control] as FormControl;
  }

然后在您的模板中,您需要将每个输入的 (focus) 事件绑定到 onControlFocus() 并传入输入的 formcontrolname

    <input
        formControlName="firstName" 
        type="text" class="form-control" id="firstName"
        (focus)="onControlFocus('firstName')"
    >

这个解决方案的大部分内容都在 focusNext() 函数中,我存储第一个无效控件的方式有点混乱。如果你想清理它,你可以尝试将表单控件存储在一个循环链接列表中,这样你就可以直接迭代回第一个无效元素。这样你就可以避免一些跟踪变量。