Alessandro Celeghin
Alessandro Celeghin

Reputation: 4189

Angular, how to set focus in a specific FormControl of FormArray

lHow ca I set focus to a specific field in a formArray?

this is the formArray:

    this.productForm = this.formBuilder.array([
     this.formBuilder.group({
     code: [null],
     description: [null],
     unitPrice: [null],
     qty: [null],
     discount: [null],
     vat: [null],
     total: [null],
     amount: [null],
     taxAmount: [null]
  })
]);

I can have a lot of this formGroup and I need to focus a specific description field. Is there a smart way in angular?

Upvotes: 6

Views: 20490

Answers (3)

Guy Park
Guy Park

Reputation: 1017

Had this same issue. I resolved it by rethinking the implementation.

My situation was that I wanted to focus on the row (first input) when the use tabbed (blurred) out of the last row. I had implemented a directive to handle the blur and focus on my Row.

import { Directive, Input, Output, ElementRef, EventEmitter, HostListener, HostBinding } from '@angular/core';

@Directive({
  selector: '[focusedRow]'
})
export class RowDirective {
  static tabindex = 1;
  static currentFocus = null;

  private timer: any = null;

  @HostBinding('tabindex') public tabIndex: number = 0;

  @Input('formGroupName') public rowNumber: number;

  @Output('focus') public onFocus: EventEmitter<number> = new EventEmitter<number>();
  @Output('blur')  public onBlur: EventEmitter<number> = new EventEmitter<number>();

  constructor(private elm: ElementRef) {
    this.tabIndex = RowDirective.tabindex++;
  }

  @HostListener('focusin', ['$event'])
  public focusHandler(event: Event) {
    // When we're not the current focus.
    if (RowDirective.currentFocus !== this) {
      this.onFocus.emit(event);

      RowDirective.currentFocus = this;

    } else {
      // Stop our blur from happening since it's the same row
      if (this.timer) {
        window.clearTimeout(this.timer);
        this.timer = null;
      }
    }
  }
  @HostListener('focusout', ['$event'])
  public blurHandler(event: Event) {
    this.timer = window.setTimeout(() => {
      // If our row has changed, then we have blurred.
      this.onBlur.emit(event);

      // Clear if this is still listed as the Focused Row.
      if (RowDirective.currentFocus === this) {
        RowDirective.currentFocus = null;
      }
    }, 200);
  }
}

What I did was, instead of trying to battle the Angular Form Group, I simply made it that when a new Row was created from my Reactive Form and the row was added, because the form row element had the directive applied, the better solution was to simply focus the row (first input) when the directive initialised.

This was done using the OnInit interface. So the above code was adjusted to include the interface (changes below);

import { Directive, Input, Output, OnInit, ElementRef, EventEmitter, HostListener, HostBinding } from '@angular/core';

@Directive({
  selector: '[focusedRow]'
})
export class RowDirective implements OnInit {
  ....

  ngOnInit() {
    // Focus on the first Form Element
    this.elm.nativeElement.querySelector('input').focus();
  }

  ....
}

So create the above Directive, and then add that directive to your Form Array element...

 <tr *ngFor="let entry of myForms.controls; let index=index" [formGroupName]="index" focusedRow (focus)="rowFocus(index)" (blur)="rowBlur(index)">

This way, anytime a row is created, it focuses the first form element.

The beauty of this solution is that it also works when the user clicked my "Add new Row" button, and you get some cool Focus and Blur handlers on your [table] row. (the secret to that by the way is the use of the tabindex attribute)

Hope that helps your situation.

Upvotes: 0

tech-y
tech-y

Reputation: 1867

You might want to use angular2-focus package in your application for consistent use from below location. You will be able to reuse the code anywhere in your application.

https://www.npmjs.com/package/angular2-focus

Using this package, you will be able to focus any input element as below.

<input type="text" focus="true">

Include module as below

import {NgModule, Component} from '@angular/core';
import {FocusModule} from 'angular2-focus';
@NgModule({
  imports: [FocusModule.forRoot()],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

Upvotes: 0

rrd
rrd

Reputation: 5957

If you know the specific item you want to focus it would be fairly straightforward first of all by adding a template reference:

<input
  #fieldName
  type="text"
  id="fieldName"
  name="fieldName"
  formControlName="yourFieldName" />

Then in your component (probably in ngAfterViewInit() method):

@ViewChild('fieldName') fieldName: ElementRef;

Then to focus on your element:

this.fieldName.nativeElement.focus();

NB this will be vulnerable to XSS attacks though

Upvotes: 3

Related Questions