13

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
  • 10,048
  • 12
  • 46
  • 85

6 Answers6

8

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

<input class="form-control" [formControl]="name" autofocus>
Rajiv
  • 447
  • 4
  • 12
  • 4
    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. – gentiane Jan 23 '18 at 00:16
  • you can create a ref to your input in the view and then you can access it in your component class @ViewChild('myname') input; ngAfterViewInit() { this.input.focus; } – Rajiv Dec 07 '18 at 18:17
  • 1
    this is only useful when you are hard coding your inputs rather than dynamically generating with ngFor – pcnate Oct 14 '19 at 19:53
5

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 {}
slaesh
  • 16,659
  • 6
  • 50
  • 52
  • 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. – Naveed Ahmed Sep 20 '16 at 15:55
  • See my updated answer/plunker.. you need to do a recursive search! :) – slaesh Sep 20 '16 at 16:09
  • 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 https://plnkr.co/edit/YF7M4ph7891x03hjJr6A?p=preview – Naveed Ahmed Sep 20 '16 at 16:54
  • 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 Sep 20 '16 at 17:02
  • 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. – Naveed Ahmed Sep 20 '16 at 17:08
  • 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.. – slaesh Sep 20 '16 at 17:11
  • 1
    Thank you so much @mxii but it work sometimes and sometimes it doesnt work. Please have a look at https://plnkr.co/edit/vvdq8IMsGjDAAM2YiO1P?p=preview – Naveed Ahmed Sep 20 '16 at 18:02
  • Cause in that example you are using `type="hidden"` instead of the attribute `hidden` !! See this plunker: https://plnkr.co/edit/azdVcdRSgER13qAcM97a?p=preview I just pointed you to the idea, everything else is now your job! ;) – slaesh Sep 20 '16 at 18:11
  • 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 :) – Naveed Ahmed Sep 20 '16 at 18:19
  • 7
    Definitely works but wow what bloat just to set focus of an input! – ScottN Jan 11 '17 at 22:43
  • 1
    @mxii it works when page reload, but doesn't work without reload, M I doing something wrong? – Nikhil Radadiya Oct 10 '17 at 08:18
  • Thanks it works for inputs but this does not focuses textarea. Can you please update for textarea also? – Faizan Saiyed Aug 14 '20 at 06:41
  • @faizan Just replace the ```.localName === 'input'``` to ```'textarea'``` – slaesh Aug 14 '20 at 20:13
2

HTML autofocus attribute do exactly that

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

Yinon
  • 697
  • 7
  • 13
1

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>
Peng Wang
  • 227
  • 3
  • 13
1
import { AfterViewInit, Directive, ElementRef } from '@angular/core';

@Directive({
  selector: 'form[appFocusFirstInput]'
})
export class FocusFirstInputDirective implements AfterViewInit {

  constructor(
    private element: ElementRef
  ) {
  }

  ngAfterViewInit(): void {
    const input = this.element.nativeElement.querySelector('input');
    if (input) {
      input.focus();
    }
  }
}
d4nni5
  • 11
  • 2
0

Following approaches you can use => 1. You can do this by autoFoucs directive OR 2. Provide reference to your control like

 <input hidden #firstName formcontrolname="firstName" type="text" class="form-control input-sm ng-pristine ng-valid ng-touched" placeholder="First Name" id="firstName">

Then in ts file declare like this

export class App implements OnInit{
 @ViewChild('firstName') firstNameTextbox;
  constructor() {    
  }
ngOnInit() {
this.firstNameTextbox.nativeElement.focus();
}
}
sachin
  • 222
  • 2
  • 3