如何在 keyCode 导航的输入中设置自动焦点?

How can I set an automatic focus in the input for a keyCode navigation?

我写了一个table,里面都包含输入字段。对于这个模板,我开发了一个 keyCode 导航。可以进行上、下、左、右导航。我必须在我的代码中添加什么才能使光标直接聚焦在导航时?

我的代码:

// Snippet from HTML
...
<tbody formArrayName="rows">
  <tr *ngFor="let rowControl of rows.controls; let rowIndex = index">
   <td [formGroupName]="rowIndex" appArrowKeyNav *ngFor="let column of displayedColumns; let columnIndex = index;">
   <div>
        <span>
               <label>
                   <input [formControl]="rowControl.get(column.attribute)">
                </label>
         </span>
     </div>
   </td>
 </tr>
</tbody>
// TS
public move(object: any) {
    const inputToArray = this.inputs.toArray();
    const cols = this.displayedColumns.length;
    let index = inputToArray.findIndex((x) => x.element === object.element);
    // console.log('Index found:', index);
    switch (object.action) {
      case 'UP':
        index -= cols;
        break;
      case 'DOWN':
        index += cols;
        break;
      case 'LEFT':
        index -= 1;
        break;
      case 'RIGHT':
        index += 1;
        break;
    }
    if (index >= 0 && index < this.inputs.length) {
      console.log('Navigating to index:', index);
      inputToArray[index].element.nativeElement.focus();
    }
  }
// Directive
  @HostListener('keydown', ['$event']) onKeyUp(event: KeyboardEvent) {
    switch (event.key) {
      case 'ArrowUp':
        this.keyboardService.sendMessage({ element: this.element, action: 'UP' });
        break;
      case 'ArrowLeft':
        this.keyboardService.sendMessage({ element: this.element, action: 'LEFT' });
        break;
      case 'ArrowDown':
        this.keyboardService.sendMessage({ element: this.element, action: 'DOWN' });
        break;
      case 'ArrowRight':
        this.keyboardService.sendMessage({ element: this.element, action: 'RIGHT' });
        break;
      case 'Enter':
        this.keyboardService.sendMessage({ element: this.element, action: 'ENTER' });
        break;
    }
}

这是 myStackblitz:https://stackblitz.com/edit/angular-wmfjhh-zfkyyx?file=app%2Ftable-basic-example.html

一种可能的方法是让 table 的单元格可以使用箭头键进行导航

使用 id 属性使用 *ngFor 索引存储行和列信息

    <tr *ngFor="let rowControl of rows.controls; let i = index">

      <ng-container [formGroupName]="i">

        <td>
          <input [id]="'row-' + i + '-col-0'" formControlName="name" (focus)="onFocus($event)">
        </td>
        <td>
          <input [id]="'row-' + i + '-col-1'" formControlName="age" (focus)="onFocus($event)">
        </td>
        <td>
          <input [id]="'row-' + i + '-col-2'" formControlName="color" (focus)="onFocus($event)">
        </td>

      </ng-container>

    </tr>

在这种情况下,第一行的 ID 将是 row-0-col-0row-0-col-1 等。 第二行 row-1-col-0

还有一个 onFocus 事件处理程序,它会将当前单元格设置为焦点

一旦keyUp被触发

 @HostListener('keydown', ['$event'])
  onKeyUp(event: KeyboardEvent) {

    console.log(event.key);

    if (this.currentInputInFocus) {
      const id = this.currentInputInFocus.id;

      if (id && id.includes('row-')) {
        const arr = id.split('-');
        let row: number = Number(arr[1]);
        let col: number = Number(arr[3]);

        switch (event.key) {
          case 'ArrowUp':
            --row;
            break;

          case 'ArrowLeft':
            --col;
            break;

          case 'ArrowDown':
            ++row;
            break;

          case 'ArrowRight':
            ++col;
            break;

          case 'Enter':
            // do nothing
            break;
        }
        this.setFocus(row, col);
      }
    }
  }

获取当前聚焦的元素id,例如。 'row-0-col-0' 并根据按键更改行或列值,然后尝试聚焦新元素

  private setFocus(row: number, col: number) {
    const newElementToFocusOn = document.getElementById(`row-${row}-col-${col}`);
    if (newElementToFocusOn) {
      this.currentInputInFocus = newElementToFocusOn;
      this.currentInputInFocus.focus();
    }
  }

ngAfterViewInit 中最初聚焦的单元格可以设置为:

  ngAfterViewInit() {
    const row = 1;
    const col = 0;
    this.setFocus(row, col);
  }

Working demo

更新

要将您的 Stackblitz 用作上下文,只需很少的 mod 即可使其工作:

  • 设置正确的 id 属性 [id]="'row-' + rowIndex + '-col-' + columnIndex"
  • 跟踪当前聚焦的输入:currentInputInFocus!: HTMLElement;
  • 其余与原demo相同

Forked and updated your Stackblitz

第二次更新

The HTMLElement.focus() method sets focus on the specified element, if it can be focused.

Docs

一种解决方法是根据此 answer

添加 tabindex="0" 属性

返回演示。正如@Aviad P. 提到的 ng-template 上的指令不起作用。相反:

  <ng-template #otherColumns>
    <div tabindex="0" [id]="'row-' + rowIndex + '-col-' + columnIndex" arrow-div>
      Here is a Number
    </div>
  </ng-template>

Demo

第三次更新

要继续导航,您可以在 case 'RIGTH':

上试试这个
++col; // try to move to next column

if (col >= this.columns.length) { // check if moved past last column
  col = 0; // go to column at zero index  (1st column)
  ++row; // go to next row
}

Demo

你有一个错误(好吧,至少有一个),你已经将你的 arrow-div 附加到你的 <ng-template> 这导致类型 Comment 的节点被添加到你的 inputs QueryList - 这些节点没有 focus() 函数,因此您的代码失败。

这是问题区域:

<ng-template arrow-div #otherColumns>
  Here is a Number
</ng-template>

除此之外,当您按下键盘箭头时,您的索引会在错误的间隙中跳跃,但是一旦您修复了之前的错误,它就会毫无错误地跳跃。