如何在Angular中自动调整表行<tr>

时间:2019-10-11 19:08:58

标签: angular typescript angular-material mat-table

上下文:

我有一个用给定数据源加载<table mat-table>的组件。 我在<tr mat-row>中添加了一个(keydown)函数。

该功能允许我在按向上/向下箭头键的同时上下移动突出显示的行。

问题:

  1. 为执行(按键)功能,我必须单击表行之一来设置焦点,或者使用“选项卡”浏览多个项目以最终将焦点放在表行上。

  2. (keydown)函数仅更改行的突出显示,而不更改:focus。因此,如果我按下向下箭头,则下一行向下突出显示,但:focus轮廓仍保留在上一行。

我已经尝试了几种解决方案,并且似乎没有单击/ Tab键就无法集中<tr>标签。 <tr>标签不支持自动对焦指令。使用.focus()会出错,声称.focus()不是函数,即使我将其转换为HTMLElement类型也是如此。

示例:

let firstRow = this.dataSource.connect().value[0] as unknown as HTMLTableRowElement;
firstRow.focus();

ERROR TypeError: firstRow.focus is not a function

我还尝试将tabIndex =“ 0”应用于行,但它不会成为页面上的第一个可标签的元素。

该表是可排序的,因此我正在使用dataSource.connect()。value [0]查找第一行,而不是dataSource.data [0]。

这是因为dataSource.data [0]提供了预先排序的数组,这违背了上/下箭头功能的目的。

TS组件:

import { Component, OnInit, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { TableService } from '../table.service';
import { SelectionModel } from '@angular/cdk/collections';
import { TableGrid } from '../TableGrid';

@Component({
  selector: 'app-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.css']
})
export class TableComponent implements OnInit {

  @ViewChild(MatSort, { static: true }) sort: MatSort;
  dataSource: MatTableDataSource<TableGrid> = null;
  displayedColumns = [
    'locked',
    'includeRe',
    'includeMv'];

  selection = new SelectionModel<TableGrid>(false, []);
  selectedRowId = 0;
  selectedRowIndex = 0;

  constructor( private tableService: TableService) { }


  ngOnInit() {
    this.tableService.get();
    this.tableService.tableGrid
      .subscribe(result => {
        this.selectedRowId = 0;
        this.dataSource = new MatTableDataSource(result);
        this.dataSource.sort = this.sort;
        if (this.dataSource.data.length > 0) {
          this.onRowClicked(this.dataSource.data[0]);
        }
      });
  }

  onRowClicked(row) {
    this.selection.clear();
    this.selection.select(row);
    this.selectedRowId = row.id;
    this.selectedRowIndex = this.dataSource.connect().value.indexOf(row);
    this.tableService.setId(row.id, row.name, row.locked, row.isDeleteable, row.includeHistory);

  }

  tableKeydown(event: KeyboardEvent) {
    if (!this.selection.isEmpty()) {
      let newSelection;
      const currentIndex = this.selectedRowIndex;
      if (event.key === 'ArrowDown') {
        newSelection = this.dataSource.connect().value[currentIndex + 1];
      } else if (event.key === 'ArrowUp') {
        newSelection = this.dataSource.connect().value[currentIndex - 1];
      }
      if (newSelection) {
        this.selectedRowId = newSelection.id;
        this.onRowClicked(newSelection);
      }
    }
  }
}

HTML:

<div class="container">
  <div class="table-container">
    <table mat-table [dataSource]="dataSource" matSort aria-label="Elements">
      <ng-container matColumnDef="locked">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Locked</th>
        <td mat-cell *matCellDef="let row">
          <i *ngIf="row.locked === true" class="material-icons">lock</i>
        </td>
      </ng-container>
      <ng-container matColumnDef="includeRe">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>RE</th>
        <td mat-cell *matCellDef="let row">
          <i *ngIf="row.includeRe === true" class="material-icons">house</i>
        </td>
      </ng-container>
      <ng-container matColumnDef="includeMv">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>MV</th>
        <td mat-cell *matCellDef="let row">
          <i *ngIf="row.includeMv === true" class="material-icons">directions_car</i>
        </td>
      </ng-container>
      <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky:true"></tr>
      <tr mat-row tabIndex="0" *matRowDef="let row; columns: displayedColumns;"
          (click)="onRowClicked(row)"
          (keydown)="tableKeydown($event)"
          [ngClass] = "{'highlight' : row.id == selectedRowId}">
      </tr>
    </table>
  </div>
</div>

所需功能:

我想在初始化时将:focus属性设置为表的第一个<tr>,以便(keydown)函数立即可用。

我还想获得(keydown)函数以在<tr>标签上设置:focus。

我要避免的事情

我想避免创建自定义焦点类来重现焦点轮廓效果。我还想避免在表行上禁用焦点概述。我想要的是将:focus伪类放在<tr>上。如果这不可能,我将考虑替代方法。

我可以根据需要添加更多信息,但是我试图使其相对匿名,因为这与工作相关。

我是Angular的新手,但是在React中已经发展了几年。任何意见,将不胜感激。

谢谢。

1 个答案:

答案 0 :(得分:0)

focus()方法可用于很少的HTML元素(Which HTML elements can receive focus?)。在使用(click)=“ myfunc()”事件处理程序向html中添加按钮之后,可以直接在浏览器的开发人员工具中对此进行测试。

在元素检查器中,为第一个tr设置id属性,例如id =“ myrow”。然后在控制台上:

var myrow = document.getElementById('myrow')
var myfunc = function () {console.log('fired'), myrow.focus(); }

现在单击按钮,并且-由于我的测试-该行没有获得焦点。 使用此测试,您应该尝试focus()是否适用于该行的第一个单元格。由于td不在上述元素列表中,因此有必要在td内包含一个可在其中运行focus()的元素,例如输入。 在这种情况下,您可以在输入上设置id属性,然后在代码中调用getElementById(注意:该元素必须已经呈现(请参见此处的讨论:How to check if element exists in the visible DOM?)),然后对其进行调用focus()。

再次注意,似乎有必要在超时时调用focus():

setFocus(el_ref: HTMLElement) {
    setTimeout(() => el_ref.focus(), 10);
}