Angular 2 Set focus on first field of form on component load

21,461

Solution 1

You should use an directive to achieve this behavior.

This will show you the way how to do it: https://plnkr.co/edit/ttxCP7vCLkLtNb3Xiaah?p=preview

import {Component, NgModule, Directive, ElementRef, Renderer} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Directive({
  selector: 'form[anyNameHere]'
})
export class SelectFirstInputDirective {

  constructor(private _eRef: ElementRef, private _renderer : Renderer) { }

  private _getInputElement(nativeElement: any): any {
    if (!nativeElement || !nativeElement.children) return undefined;
    if (!nativeElement.children.length && nativeElement.localName === 'input' && !nativeElement.hidden) return nativeElement;

    let input;

    [].slice.call(nativeElement.children).every(c => {
      input = this._getInputElement(c);
      if (input) return false; // break
      return true; // continue!
    });

    return input;
  }

  ngAfterViewInit() {
    let formChildren = [].slice.call(this._eRef.nativeElement.children);

    formChildren.every(child => {
      let input = this._getInputElement(child);

      if (input) {
        this._renderer.invokeElementMethod(input, 'focus', []);
        return false; // break!
      }

      return true; // continue!
    });
  }
}

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Hello {{name}}</h2>
      <form anyNameHere>
        <div class="form-group">
            <input hidden formcontrolname="firstName" type="text" class="form-control input-sm ng-pristine ng-valid ng-touched" placeholder="First Name" id="firstName">
            <label class="col-sm-3 control-label" for="firstName">Name</label>
            <div class="col-sm-9 form-inline">
              <div class="form-group">
                <div class="col-sm-12">
                  <input formcontrolname="firstName" type="text" class="form-control input-sm ng-pristine ng-valid ng-touched" placeholder="First Name" id="firstName">
                </div>
              </div>

              <div class="form-group">
                <div class="col-sm-12">
                  <input formcontrolname="lastName" type="text" class="form-control input-sm ng-untouched ng-pristine ng-valid" placeholder="Last Name" id="lastName">
                </div>
              </div>
            </div>
        </div>
      </form>
    </div>
  `,
})
export class App {
  constructor() {
    this.name = 'Angular2'
  }
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, SelectFirstInputDirective ],
  bootstrap: [ App ]
})
export class AppModule {}

Solution 2

you can achieve this by simply adding the "autofocus" attribute to your input element like.

<input class="form-control" [formControl]="name" autofocus>

Solution 3

HTML autofocus attribute do exactly that

<input type="text" name="fname" autofocus>

Solution 4

With Angular 4,Renderer has been deprecated, so the directive way is gone. But anyhow you can always use a "quick and dirty" way: add the reference to the element you want to set focus on and just use <reference-name>.focus()

<form [formGroup]="form" (ngSubmit)="onSubmit(form, $event); needFocus.focus()">
  <input placeholder="" formControlName="name" #needFocus>
</form>
Share:
21,461
Naveed Ahmed
Author by

Naveed Ahmed

Updated on July 14, 2021

Comments

  • Naveed Ahmed
    Naveed Ahmed almost 3 years

    In my app I want to automatically set focus on first field of form on component load. Can anyone please guide how to achieve this without any repetition as I want this on every form (Reactive Forms) in my app.

  • Naveed Ahmed
    Naveed Ahmed over 7 years
    Thank you so much @mxii. Apparently the above directive should set the focus on first input field in the form and it does it in the plunker. But when I tested it in my app it didn't work, probably because in my case the input field is under a div tag. Is there any way to query and select the input elements only and then set focus to first input. Currently you are using this._eRef.nativeElement.children which selects all children of the form.
  • slaesh
    slaesh over 7 years
    See my updated answer/plunker.. you need to do a recursive search! :)
  • Naveed Ahmed
    Naveed Ahmed over 7 years
    Still no success, I logged, value of input after this line in directive and let input = this._getInputElement(child); and its undefined. Please have a look at plnkr.co/edit/YF7M4ph7891x03hjJr6A?p=preview
  • Naveed Ahmed
    Naveed Ahmed over 7 years
    Please also guide how to skip the hidden inputs? Since if the first input is hidden it would set focus to that, which isn't visible on screen.
  • Naveed Ahmed
    Naveed Ahmed over 7 years
    One more thing, Angular team sdiscourage the use of ElementRef this is what I found in comments "Use this API as the last resort when direct access to DOM is needed. Use templating and data-binding provided by Angular instead. Alternatively you take a look at {@link Renderer} which provides API that can safely be used even when direct access to native elements is not supported." Relying on direct DOM access creates tight coupling between your application and rendering layers which will make it impossible to separate the two and deploy your application into a web worker.
  • slaesh
    slaesh over 7 years
    My fault, updated! And its now checking the hidden property.. you are right, but we are using the Render here !! It should be the last resort to MANIPULATE with ElementRef, not to check those childs and so on..
  • Naveed Ahmed
    Naveed Ahmed over 7 years
    Thank you so much @mxii but it work sometimes and sometimes it doesnt work. Please have a look at plnkr.co/edit/vvdq8IMsGjDAAM2YiO1P?p=preview
  • slaesh
    slaesh over 7 years
    Cause in that example you are using type="hidden" instead of the attribute hidden !! See this plunker: plnkr.co/edit/azdVcdRSgER13qAcM97a?p=preview I just pointed you to the idea, everything else is now your job! ;)
  • Naveed Ahmed
    Naveed Ahmed over 7 years
    Thank you so much mxii that was really helpful. Actually I was not sure what other properties does nativeElement support. localName, hidden and type seems to cover all cases. I added support for select and text area as well :)
  • William Gaul
    William Gaul over 7 years
    Definitely works but wow what bloat just to set focus of an input!
  • Nikhil Radadiya
    Nikhil Radadiya over 6 years
    @mxii it works when page reload, but doesn't work without reload, M I doing something wrong?
  • gentiane
    gentiane over 6 years
    The input will get the focus only after the page is loaded. But if you navigate in your application and return of the page, then the input will not longer receive focus. autofocus is not well adapted for single page application.
  • Rajiv
    Rajiv over 5 years
    you can create a ref to your input in the view <input #myname> and then you can access it in your component class @ViewChild('myname') input; ngAfterViewInit() { this.input.focus; }
  • pcnate
    pcnate over 4 years
    this is only useful when you are hard coding your inputs rather than dynamically generating with ngFor
  • Faizan Saiyed
    Faizan Saiyed over 3 years
    Thanks it works for inputs but this does not focuses textarea. Can you please update for textarea also?
  • slaesh
    slaesh over 3 years
    @faizan Just replace the .localName === 'input' to 'textarea'