2
votes

I have an Angular 2+ form group and each form field has tabIndex.

How do I change focus to the next form field on each Enter key press (similar to pressing tab)?

JavaScript Reference - Enter key press behaves like a Tab in Javascript

3
I know my answer went unnoticed :(, but stackoverflow.com/questions/53690973/… - Eliseo
@Eliseo, I have tried the code and doesn't work in my case I'm generating the controls dynamically. - Kanchan
if you put #nextTab I don't know because can not work. Check -writing console.log(querycontrols.length) in the function createKeydownEnter to check if the directive is taking account your constrols - Eliseo
I tried debugging, querycontrols.length always comes 0. Even I tried with AfterViewChecked. - Kanchan
see the new stackblitz.com/edit/…. See how I reffered to the controls in a FormArray, like [formControl]="lineas.at(i).get('prop1')" I try use FormArrayName, GroupName and so and don't work, but using formControl it's OK. If not work, tell more data about how generate the controls dinamically - Eliseo

3 Answers

4
votes

I would do this with a simple directive and a much simpler service.

tab.directive.ts

import { Directive, Input, ElementRef, HostListener, OnInit } from '@angular/core';
import { TabService } from './tab.service';
type IKNOWISNUMBER = any;
type IKNOWISSTRING = any;

@Directive({
  selector: '[tabIndex]'
})
export class TabDirective implements OnInit {

  private _index: number;
  get index(): IKNOWISNUMBER{
    return this._index;
  }
  @Input('tabIndex')
  set index(i: IKNOWISSTRING){
    this._index = parseInt(i);
  }

  @HostListener('keydown', ['$event'])
  onInput(e: any) {
    if (e.which === 13) {
      this.tabService.selectedInput.next(this.index + 1)
      e.preventDefault();
    }
  }
  constructor(private el: ElementRef, private tabService: TabService) { 
  }

  ngOnInit(){
    this.tabService.selectedInput.subscribe((i) => {
      if (i === this.index){
        this.el.nativeElement.focus();
      }
    });
  }
}

tab.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
 
@Injectable()
export class TabService {
  selectedInput: BehaviorSubject<number> = new BehaviorSubject<number>(1);
}

I've created a little stackblitz to show how it works.

P.S. Remember to provide the tab.service inside every component with a form, cause you need a specific instance for each form.

1
votes

Here's a very simple approach, with just a few lines of code (which I also posted here:Change behavior of Enter key . . . ):

First, in your Template when you dynamically create your Input elements: 1. populate the tabIndex attribute with a unique number, 2. populate a super-simple custom "Tag" Directive with the same unique number as the tabIndex, and 3. set up a Keydown "Enter" event listener:

Template:

<ng-container *ngFor="let row in data">
   <input tabindex ="{{row[tabCol]}}" [appTag]="{{row[tabCol]}}" (keydown.enter)="onEnter($event)" . . . />
</ng-container>

In your component, your super-simple event-listener onEnter():

@ViewChildren(TagDirective) ipt!: QueryList<ElementRef>;

  onEnter(e: Event) {
    this.ipt["_results"][(<HTMLInputElement>e.target).tabIndex%(+this.ipt["_results"].length-1)+1].el.nativeElement.focus();
  }

Note: The modulus (%) operation is just to make sure that if you're at the last Input, you'll get cycled back to the first input.

Super-simple, bare-minimum "Tag" Directive

import { Directive, ElementRef, Input } from '@angular/core';

@Directive({
  selector: '[appTag]'
})
export class TagDirective {
  @Input('appTag') id: number;

  constructor(public el: ElementRef) { }

}

There's probably even a way to get rid of the "Tag" `Directive altogether and make it even more simple, but I haven't had time to figure out how to do that yet . . .

0
votes

export class InputNumberComponent implements OnInit {
    @Input() model: number;
    @Input() tabIndex: number ;
    @Output()
    changedValue = new EventEmitter<number>();
    constructor() { }
    
    valueChanged(value): void {
        this.changedValue.emit(value);
    }
}
  
  <input tabindex="{{tabIndex}}"  [(ngModel)]="model"  (change)="valueChanged(model)"/>
  

if use angular and make input component only use html "tabindex" in html

and define @input to .ts file get tabindex @Input() tabIndex: number ;