问题描述
我写了一个表格,其中都包含输入字段。对于这个模板,我开发了一个 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;
}
}
这是我的Stackblitz:https://stackblitz.com/edit/angular-wmfjhh-zfkyyx?file=app%2Ftable-basic-example.html
解决方法
拥有可以使用箭头键导航的单元格的表格的一种可能方法
使用 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-0
、row-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);
}
更新
使用你的 Stackblitz 作为上下文很少有 mods 使它工作:
- 设置正确的 id 属性
[id]="'row-' + rowIndex + '-col-' + columnIndex"
- 跟踪当前聚焦的输入:
currentInputInFocus!: HTMLElement;
- rest 与原始演示中的相同
Forked and updated your Stackblitz
第二次更新
HTMLElement.focus() 方法将焦点设置在指定的元素上,如果 可以集中注意力。
一种解决方法是根据此 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>
第三次更新
要继续导航,您可以在 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
}
,
您有一个错误(好吧,至少有一个),您已将 arrow-div
附加到您的 <ng-template>
,这导致 Comment
类型的节点被添加到您的 {{1} } inputs
- 这些节点没有 QueryList
函数,因此您的代码会失败。
这是问题区域:
focus()
除此之外,当您按下键盘箭头时,您的索引会跳入错误的间隙,但是一旦您修复了之前的错误,它就会毫无错误地跳来跳去。