Как автофокусировать строку таблицы <tr>в Angular - PullRequest
0 голосов
/ 11 октября 2019

Контекст:

У меня есть компонент, который загружает <table mat-table> с данным источником данных. Я добавил (keydown) функцию к <tr mat-row>.

Эта функция позволяет мне смещать выделение строк вверх и вниз при нажатии клавиш со стрелками вверх / вниз.

Проблемы:

  1. Чтобы выполнить функцию (keydown), мне нужно либо щелкнуть одну из строк таблицы, чтобы установить фокус, либо использовать вкладку для нескольких элементов, чтобы в конечном итоге сосредоточиться на строках таблицы.

  2. Функция (keydown) изменяет только подсветку строк, но не фокус. Поэтому, если я нажму стрелку вниз, следующий ряд будет подсвечен, но контур: focus останется в предыдущем ряду.

Я пробовал несколько решений, и, похоже, не могуСфокусируйте теги <tr>, не нажимая / TabДиректива автофокуса не поддерживается для тегов <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> таблицы в init, так что функция (keydown) может быть сразу использована.

Я также хотел бы получить функцию (keydown) для установки: focus на <tr> tags.

Чего я пытаюсь избежать

Я бы хотел избежать создания собственного класса фокусировки для воспроизведения эффекта контура фокуса. Я также хотел бы избежать отключения фокуса фокуса на строках таблицы. Я хочу поставить псевдокласс: focus на <tr>. Если это невозможно, я рассмотрю альтернативы.

Я могу добавить больше информации, если это необходимо, но я стараюсь сохранять ее относительно анонимной, поскольку это связано с работой.

Яотносительно новый для Angular, но в течение нескольких лет разрабатывался в React. Любой совет будет принят во внимание.

Спасибо.

1 Ответ

0 голосов
/ 12 октября 2019

Метод focus () работает с очень немногими элементами HTML ( Какие элементы HTML могут получать фокус? ). Вы можете проверить это непосредственно в инструментах разработчика браузера после добавления кнопки в html с помощью обработчика событий (click) = "myfunc ()".

В инспекторе элементов установите атрибут id для первого trНапример, id = "myrow". Затем на консоли:

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

Теперь нажмите кнопку, и - из-за моих тестов - строка не получает фокус. Используя этот тест, вы должны проверить, работает ли focus () в первой ячейке строки. Поскольку td отсутствует в списке элементов, упомянутых выше, необходимо, чтобы внутри td был элемент, над которым работает focus (), например вход. В этом случае вы можете установить атрибут id на входе, чем вызывать getElementById в вашем коде (внимание: элемент уже должен быть визуализирован (см. Обсуждение здесь: Как проверить, существует ли элемент в видимом DOM? )) и затем вызовите focus () для него.

Внимание, опять же, в angular кажется необходимым вызвать focus () с таймаутом:

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

.

...