Angular 2 Set focus on first field of form on component load
Asked Answered
P

6

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.

Pythagoreanism answered 19/9, 2016 at 22:18 Comment(0)
N
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 {}
Null answered 20/9, 2016 at 7:0 Comment(13)
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.Pythagoreanism
See my updated answer/plunker.. you need to do a recursive search! :)Null
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=previewPythagoreanism
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.Pythagoreanism
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.Pythagoreanism
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..Null
Thank you so much @mxii but it work sometimes and sometimes it doesnt work. Please have a look at plnkr.co/edit/vvdq8IMsGjDAAM2YiO1P?p=previewPythagoreanism
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! ;)Null
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 :)Pythagoreanism
Definitely works but wow what bloat just to set focus of an input!Dashing
@mxii it works when page reload, but doesn't work without reload, M I doing something wrong?Thinkable
Thanks it works for inputs but this does not focuses textarea. Can you please update for textarea also?Carpathoukraine
@faizan Just replace the .localName === 'input' to 'textarea'Null
C
9

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

<input class="form-control" [formControl]="name" autofocus>
Caricaria answered 7/9, 2017 at 15:23 Comment(3)
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.Somewhat
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; }Caricaria
this is only useful when you are hard coding your inputs rather than dynamically generating with ngForGlassine
N
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 {}
Null answered 20/9, 2016 at 7:0 Comment(13)
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.Pythagoreanism
See my updated answer/plunker.. you need to do a recursive search! :)Null
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=previewPythagoreanism
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.Pythagoreanism
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.Pythagoreanism
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..Null
Thank you so much @mxii but it work sometimes and sometimes it doesnt work. Please have a look at plnkr.co/edit/vvdq8IMsGjDAAM2YiO1P?p=previewPythagoreanism
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! ;)Null
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 :)Pythagoreanism
Definitely works but wow what bloat just to set focus of an input!Dashing
@mxii it works when page reload, but doesn't work without reload, M I doing something wrong?Thinkable
Thanks it works for inputs but this does not focuses textarea. Can you please update for textarea also?Carpathoukraine
@faizan Just replace the .localName === 'input' to 'textarea'Null
M
2

HTML autofocus attribute do exactly that

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

Manuel answered 9/11, 2017 at 8:42 Comment(0)
C
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();
    }
  }
}
Colorblind answered 13/7, 2021 at 12:43 Comment(0)
B
0

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>
Borosilicate answered 10/7, 2017 at 8:34 Comment(0)
A
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();
}
}
Antepenult answered 27/7, 2017 at 7:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.