3

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

Narm
  • 10,677
  • 5
  • 41
  • 54
Kanchan
  • 1,609
  • 3
  • 22
  • 37
  • I know my answer went unnoticed :(, but https://stackoverflow.com/questions/53690973/change-behaviour-of-enter-key-in-a-phone-angular-5/53691038 – Eliseo Apr 09 '19 at 16:25
  • @Eliseo, I have tried the code and doesn't work in my case I'm generating the controls dynamically. – Kanchan Apr 10 '19 at 06:38
  • 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 Apr 10 '19 at 06:59
  • I tried debugging, querycontrols.length always comes 0. Even I tried with AfterViewChecked. – Kanchan Apr 10 '19 at 07:37
  • 1
    see the new https://stackblitz.com/edit/angular-m8jtwp?file=src%2Fapp%2Fapp.component.html. 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 Apr 10 '19 at 08:18
  • @Eliseo, I have used GroupName, FormArrayName and FormControl. so it is not working for me. – Kanchan Apr 10 '19 at 12:33
  • has you see the new stackblitz? There are two examples, one using FormArray, another one dinamic Form, I hope this can help you – Eliseo Apr 10 '19 at 13:25
  • @Eliseo, Your implementation is right. seems something is not working in my case. I will put my code in stackblitz so you can have a look. Thanks for the effort. – Kanchan Apr 11 '19 at 12:06

3 Answers3

5

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.

Federico Galfione
  • 1,069
  • 7
  • 19
  • if you want take a look to my comment at top – Eliseo Apr 09 '19 at 16:32
  • @Eliseo I really like it, but I have a question. The ViewChildren already orders the controls by tabIndex or you have to order them programmatically when the directive is created? – Federico Galfione Apr 09 '19 at 16:44
  • ViewChildren order the elemens by position in the DOOM (Almost I beleive in this). If you want to change, is possible: First you pass to array using toArray, order the array and use reset with the array ordered. – Eliseo Apr 09 '19 at 18:00
1

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 . . .

Crowdpleasr
  • 3,574
  • 4
  • 21
  • 37
  • It's generally not a great idea to set tabindex over 0 otherwise you have to keep up with it to make sure you don't have tabbing jump all around the page. Not that you can't use values greater than 0, it can just lead to unexpected and undesirable behavior. – blau Dec 16 '22 at 16:05
0

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 ;

mahtab
  • 1
  • 2