2

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
  • 9,441
  • 5
  • 39
  • 52
Kanchan
  • 1,559
  • 2
  • 20
  • 36
  • 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

4

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.

  • 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
  • 2,740
  • 2
  • 14
  • 32
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