0

I'm trying to simulate tab key press when enter is pressed on input control.For that I use a directive:

 private el: ElementRef;
    @Input() onEnter: string;
    constructor(private _el: ElementRef, public renderer: Renderer) {
        this.el = this._el;
    }
    @HostListener('keydown', ['$event']) onKeyDown(e: any) {
        if ((e.which === 13 || e.keyCode === 13)) {
            e.preventDefault();


    const event = new KeyboardEvent("keypress", {
            "key": "Tab"
          });
          this.el.nativeElement.dispatchEvent(event);
............

code for enter key fires but no tab is sent

3 Answers 3

3

Updated see better aproach in this stackblitz(sorry,I don't remember the post where I talk about it)

If you want to use ENTER to focus an element you can use a directive

@Directive({
  selector: '[next-tab]',
})
export class NextTabDirective {


  @Input('next-tab') nextControl: any;

  @HostListener("keydown.enter", ["$event"])
  onEnter(event: KeyboardEvent) {
    if (this.nextControl) {
      if (this.nextControl.focus) {
        this.nextControl.focus();
        this.nextControl.select();
        event.preventDefault();
        return false;
      }
    }
  }

  constructor(private control: NgControl) {
  }
}

You can use in a form like

  <form (submit)="Submit()">
      <input #input0 [next-tab]="input1"  />
      <input #input1 [next-tab]="input2"  />
      <!--the last not have [next-tab]-->
      <!-an ENTER make a submit -->
      <input #input2   />
       <button type="button" (click)="cancel()">Cancel</button>
       <button type="submit">OK</button>
   </form>

I would like not use this ugly work-around, but we can improve the directive sending as next-tab an array of controls

@Directive({
  selector: '[next-tab]',
})
export class NextTabDirective {

  @Input('next-tab') nextControl: any[]; //<--an array of controls

  @HostListener("keydown.enter", ["$event"])
  onEnter(event: KeyboardEvent) {

    //find the nextControl not disabled. We check if c is defined
    //This allow us to use *ngIf and not put the control
    let nextControl=this.nextControl.find(c=>c && !c.disabled);
    if (nextControl) {
      if (nextControl.focus) {
        nextControl.focus();
        nextControl.select();
        event.preventDefault();
        return false;
      }
    }
  }

  constructor(private control: NgControl) {
  }
}

The form is look like

   <form (submit)="Submit()">
      <!--see that we create an array-->
      <input #input0 [next-tab]="[input1,input2,input3]"  />
      <input #input1 [next-tab]="[input2,input3]"  />
      <!--if only one element, we make an array of one element-->
      <input #input2 [style.display]="existInput2?'inherit':'none'" [next-tab]="[input3]"   />
      <!--if we want make invisible control NOT use *nfIf, therefore, we must hidden and disabled too -->

      <input #input3 />
       <button type="button" (click)="cancel()">Cancel</button>
       <button type="submit">OK</button>
   </form>

Finally I put a stackblitz in https://stackblitz.com/edit/angular-v8fkkf

Sign up to request clarification or add additional context in comments.

10 Comments

Hi.it is good solution but not fully working for me: sometimes I need conditionally to disable some inputs,in this case it doesn't skip to next enabled input as pressing tab key does.thanks
Puff, I improve the directive using an array of controls. I would like get other solution, but I can't get it :(
I try the code with a ReactiveForm. Don't remove items or use ViewChildren. just write betwenn "[" "]" the "reference name" of the inputs (the reference name is the "#variable"). We pass to the directive all the "controls" captable to be focused (they're in the this.nextControl array). nextControl=this.nextControl.find(c=>c && !c.disabled); find the first control not disabled and "visible"
@mrapi I put a stackblitz in stackblitz.com/edit/angular-v8fkkf There're a problem if we use ngIf instead of [style.display]="none". I hope this help you
See the directive. In constructor we have control:NgControl. As the directive not use "control", I hope you can simply remove the constructor function to work
|
1

There are a more "confortable way to do a "next-tab" th idea is that the next-tab directive has two variables "self" and "next-control". We not use @Input, the directive is like

@Directive({
  selector: '[next-tab]',
})
export class NextTabDirective {

  self:any;     
  nextControl:any;  //See that is not a @Input

  @HostListener("keydown.enter", ["$event"])
  onEnter(event: KeyboardEvent) {
    if (this.nextControl) {
      if (this.nextControl.focus) {
        this.nextControl.focus();
        this.nextControl.select();
        event.preventDefault();
        return false;
      }
    }
  }

  constructor(private control: ElementRef) {
    //we store in "self" the native element. This make us easy refered to 
    //html properties of control
    this.self=control.nativeElement;
  }
}

In the .html see that the directive is simply next-tab, not [next-tab] and not [next-tab]="". Take account that input4 is too "decorated" with the directive, and that all the input have a "name" html property

<form [formGroup]="myForm" (submit)="submit(myForm)">
  <p>
    <input type="checkbox" formControlName="ckDisabled"/>Disabled input 2
  </p>
  <p>
    <input type="checkbox" formControlName="ckInvisible"/>Invisible input 3
  </p>
  <p>
    Input 1: <input name="input1" formControlName="input1"
              next-tab/>
  </p>
  <p>
    Input 2: <input name="input2" formControlName="input2" tabindex=""
              [enableControl]="!myForm.controls['ckDisabled'].value" 
              next-tab/>
  </p>
  <p [style.display]="myForm.controls['ckInvisible'].value?'none':'inherit'">
    Input 3: <input name="input3" formControlName="input3"
              [enableControl]="!myForm.controls['ckInvisible'].value"
              next-tab
/>
  </p>
  <p>
    Input 4: <input name="input4"  next-tab formControlName="input4"/>
  </p>
  <p>
  <button>OK</button>
</p>
  </form>

And now the .ts. We use ViewChildren to get all the element that have a next-tab directive.

export class AppComponent implements OnInit,AfterViewInit  {
  //We use a ViewChildren to get all the controls width NextTabDirective
  //We use QueryList<NextTabDirective>. So the elements of "inputs"
  //will have the properties: self and nextControl

  @ViewChildren(NextTabDirective) inputs: QueryList<NextTabDirective>;

  myForm:FormGroup;

  constructor(private fb:FormBuilder){}
  //in ngAfterViewInit we asing to nextControl, the self of the next control
  ngAfterViewInit() {
      const controls=this.inputs.toArray();
      //controls will be [input1,input2,input3,input4]
      for (let i=0;i<controls.length-1;i++)
          controls[i].nextControl=controls[i+1].self;        }
}


  ngOnInit()
  {
   this.myForm=this.fb.group({
    ckDisabled:false,
    ckInvisible:false,
    input1:'',
    input2:'',
    input3:'',
    input4:''

  });
  this.onChanges();
  }
  onChanges()
  {
    this.myForm.valueChanges.subscribe((value)=>{
         if (this.inputs)
         {
           //see how we get the control using self.name
           const input1=this.inputs.find(c=>c.self.name=="input1");
           const input2=this.inputs.find(c=>c.self.name=="input2");
           const input3=this.inputs.find(c=>c.self.name=="input3");
           const input4=this.inputs.find(c=>c.self.name=="input4");

           input1.nextControl=(value.ckDisabled && value.ckInvisible)?input4.self:
                            (value.ckDisabled)?input3.self:input2.self;
           input2.nextControl=(value.ckInvisible)?input4.self:input3.self;
         }

    })

  }
  submit(form:any)
  {
    if (form.valid)
      alert("Fom submitted!!")
  }
}

there are a stackBlitz in https://stackblitz.com/edit/angular-dzdxmh

Comments

1

Very simple, No need custom directive or any dependences:

Give an "#Name" for your inputs and use (keyup.enter) native directive

  <mat-form-field>
    <mat-label>xxxx</mat-label>
    <input matInput [(ngModel)]="xxxx" name="xxxx" #xxxxInput (keyup.enter)="yyyyInput.focus()">
  </mat-form-field>

  <mat-form-field>
    <mat-label>yyyy</mat-label>
    <input matInput [(ngModel)]="yyyy" name="yyyy" #yyyyInput (keyup.enter)="zzzzInput.focus()">
  </mat-form-field>

  <mat-form-field>
    <mat-label>zzzz</mat-label>
    <input matInput [(ngModel)]="zzzz" name="zzzz" #zzzzInput>
  </mat-form-field>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.