WitnessTruth
WitnessTruth

Reputation: 589

Angular - How to jump from an input to another?

I would like to bind arrows keyup's events to jump to another input in a MatTable.

I would like to do something like the following situation:

mycomponent.html

<table mat-table [dataSource]="dataSource">
    <ng-container matColumnDef="name">
        <th th mat-header-cell *matHeaderCellDef>Name</th>
        <td mat-cell *matCellDef="let element">
            <input type="text" (keyup.arrowup)="jumpToPreviousNameInput()" (keyup.arrowdown)="jumpToNextNameInput()" (keyup.arrowright)="jumpToAgeInput()"/>
        </td>
    </ng-container>
    
    <ng-container matColumnDef="age">
        <th mat-header-cell *matHeaderCellDef>Age</th>
        <td mat-cell *matCellDef="let element">
            <input type="number" min="0" (keyup.arrowup)="jumpToPreviousAgeInput()" (keyup.arrowdown)="jumpToNextAgeInput()" (keyup.arrowleft)="jumpToNameInput()" />
        </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns; let element" class="table-row" </tr>
</table>

Is this possible with Angular 9?

I have no idea about how can I get the next input element in Angular. In Vanilla JS I would do something like:

let myTable = document.querySelector('table')

let inputs = myTable.querySelectorAll('input')

inputs.forEach((input, index) => {

    input.addEventListener('keydown', e => {
        const key = e.key;

        switch(key) {

            case "ArrowLeft":
                try { inputs[index-1].focus() } catch {}
                break;

            case "ArrowRight":
                try { inputs[index + 1].focus() } catch { }
                break;

            case "ArrowUp":
                try { inputs[index - 2].focus() } catch { }
                break;
            
            case "ArrowDown":
                try { inputs[index + 2].focus() } catch { }
                break;

        }
    })

})

Upvotes: 0

Views: 1169

Answers (1)

abhay tripathi
abhay tripathi

Reputation: 4032

This can be easily done with ListKeyManager from @angular/cdk. @angular/cdk has lots of core basic features. Here i am sharing a bare simple code. You will have to make changes to it accordingly.

import { Component, ViewChild, ViewChildren, ElementRef, QueryList, HostListener, AfterViewInit } from '@angular/core';
import { FocusTrapFactory, FocusMonitor, ListKeyManager} from '@angular/cdk/a11y'

@Component({
  selector: 'app-root',
  template: `
  <button (click)="testA11y()"> Test A11y! </button>
  <div #element role="dialog" hidden=true>
      <label>Sample field</label>
      <input #elementChild>
      <label>Sample field</label>
      <input #elementChild>
      <label>Sample field</label>
      <input #elementChild>
      <label>Sample field</label>
      <input #elementChild>
      <label>Sample field</label>
      <input #elementChild>
  </div>`
})
export class AppComponent implements AfterViewInit {

  keyManager : any

  @ViewChild('element') element : ElementRef; 
  @ViewChildren('elementChild') elementChild : QueryList<any>

  constructor( private focusTrap: FocusTrapFactory, 
            private focusMonitor : FocusMonitor ) {}

  ngAfterViewInit() { 
    this.keyManager = new ListKeyManager(this.elementChild) 
    this.keyManager.withHorizontalOrientation('ltr') // Arrow navigation options 
    this.keyManager.withWrap()  // Arrow navigation options 
  }

  /* Enables keyboard arrows navigation */
  @HostListener('window:keyup', ['$event']) 
  keyFunc(event) {
    if (event.code !== 'Tab') {
      this.keyManager.onKeydown(event)
      this.focusMonitor.focusVia(this.keyManager.activeItem.nativeElement, "keyboard")
    }
    else {  // 'artificially' updates the active element in case the user uses Tab instead of arrows
      this.keyManager.onKeydown(event)
      this.keyManager.setNextItemActive()
    }
  }

  /* Shows the form, puts focus on it and initialize keyboard navigation */
  testA11y() {
    this.element.nativeElement.hidden = false;   
    let focusTrap = this.focusTrap.create(this.element.nativeElement)  // creates a focus trap region
    focusTrap.focusInitialElement()     // Moves the focus in the form (by default the first field)
    this.keyManager.setFirstItemActive()    // Sets the element we focused on as 'active' to the KeyManager
  }

}

There are also some good articles on it https://moduscreate.com/blog/adding-keyboard-navigation-to-angular-lists-using-angular-cdk-listkeymanager/ (NOTE: -not written by me)

Upvotes: 1

Related Questions