Angular2 - Focusing a textbox on component load

AngularAngular2 TemplateAngular2 Forms

Angular Problem Overview


I am developing a component in Angular2 (Beta 8). The component has a textbox and a dropdown. I would like to set the focus in textbox as soon as component is loaded or on change event of dropdown. How would I achieve this in angular2. Following is Html for the component.

<div>
    <form role="form" class="form-horizontal ">        
        <div [ngClass]="{showElement:IsEditMode, hidden:!IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Name</label>
                <div class="col-md-7 col-sm-7">
                    <input id="name" type="text" [(ngModel)]="person.Name" class="form-control" />

                </div>
                <div class="col-md-2 col-sm-2">
                    <input type="button" value="Add" (click)="AddPerson()" class="btn btn-primary" />
                </div>
            </div>
        </div>
        <div [ngClass]="{showElement:!IsEditMode, hidden:IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Person</label>
                <div class="col-md-7 col-sm-7">
                    <select [(ngModel)]="SelectedPerson.Id"  (change)="PersonSelected($event.target.value)" class="form-control">
                        <option *ngFor="#item of PeopleList" value="{{item.Id}}">{{item.Name}}</option>
                    </select>
                </div>
            </div>
        </div>        
    </form>
</div>

Angular Solutions


Solution 1 - Angular

Using simple autofocus HTML5 attribute works for 'on load' scenario

 <input autofocus placeholder="enter text" [(ngModel)]="test">

or

<button autofocus (click)="submit()">Submit</button>

http://www.w3schools.com/TAgs/att_input_autofocus.asp

Solution 2 - Angular

This answer is inspired by post Angular 2: Focus on newly added input element

Steps to set the focus on Html element in Angular2

  1. Import ViewChildren in your Component

    import { Input, Output, AfterContentInit, ContentChild,AfterViewInit, ViewChild, ViewChildren } from 'angular2/core';
    
  2. Declare local template variable name for the html for which you want to set the focus

  3. Implement the function ngAfterViewInit() or other appropriate life cycle hooks

  4. Following is the piece of code which I used for setting the focus

    ngAfterViewInit() {vc.first.nativeElement.focus()}
    
  5. Add #input attribute to the DOM element you want to access.

///This is typescript
import {Component, Input, Output, AfterContentInit, ContentChild,
  AfterViewChecked, AfterViewInit, ViewChild,ViewChildren} from 'angular2/core';

export class AppComponent implements AfterViewInit,AfterViewChecked { 
   @ViewChildren('input') vc;
  
   ngAfterViewInit() {            
        this.vc.first.nativeElement.focus();
    }
  
 }

<div>
    <form role="form" class="form-horizontal ">        
        <div [ngClass]="{showElement:IsEditMode, hidden:!IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Name</label>
                <div class="col-md-7 col-sm-7">
                    <input #input id="name" type="text" [(ngModel)]="person.Name" class="form-control" />

                </div>
                <div class="col-md-2 col-sm-2">
                    <input type="button" value="Add" (click)="AddPerson()" class="btn btn-primary" />
                </div>
            </div>
        </div>
        <div [ngClass]="{showElement:!IsEditMode, hidden:IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Person</label>
                <div class="col-md-7 col-sm-7">
                    <select [(ngModel)]="SelectedPerson.Id"  (change)="PersonSelected($event.target.value)" class="form-control">
                        <option *ngFor="#item of PeopleList" value="{{item.Id}}">{{item.Name}}</option>
                    </select>
                </div>
            </div>
        </div>        
    </form>
</div>

Solution 3 - Angular

<input id="name" type="text" #myInput />
{{ myInput.focus() }}

this is the best and simplest way, because code "myInput.focus()" runs after input created

WARNING: this solution is acceptable only if you have single element in the form (user will be not able to select other elements)

Solution 4 - Angular

The original question asked for a way to set focus initially, OR to set focus later, in response to an event. It seems like the correct way to approach this is to make an attribute directive which can be set for any input element, and then to use a custom event to safely trigger the focus method on this input element. To to so, first create the directive:

import { Directive, Input, EventEmitter, ElementRef, Renderer, Inject } from '@angular/core';

@Directive({
    selector: '[focus]'
})
export class FocusDirective {
    @Input('focus') focusEvent: EventEmitter<boolean>;

    constructor(@Inject(ElementRef) private element: ElementRef, private renderer: Renderer) {
    }

    ngOnInit() {
        this.focusEvent.subscribe(event => {
            this.renderer.invokeElementMethod(this.element.nativeElement, 'focus', []);
        });
    }
}

Note that it uses renderer.invokeElementMethod on the nativeElement, which is web worker safe. Notice also that focusEvent is declared as an input.

Then add the following declarations to the Angular 2 component which has the template where you wish to use the new directive to set focus to an input element:

public focusSettingEventEmitter = new EventEmitter<boolean>();

ngAfterViewInit() { // ngOnInit is NOT the right lifecycle event for this.
    this.focusSettingEventEmitter.emit(true);
}
setFocus(): void {
  this.focusSettingEventEmitter.emit(true);
}

Don't forget to import EventEmitter above the component like this:

import { Component, EventEmitter } from '@angular/core';

and in the template for this component, set the new [focus] attribute like this:

<input id="name" type="text" name="name" 
    [(ngModel)]="person.Name" class="form-control"
    [focus]="focusSettingEventEmitter">

Finally, in your module, import and declare the new directive like this:

import { FocusDirective } from './focus.directive';

@NgModule({
    imports: [ BrowserModule, FormsModule ],
    declarations: [AppComponent, AnotherComponent, FocusDirective ],
    bootstrap: [ AppComponent ]
})

To recap: the ngAfterViewInit function will cause the new EventEmitter to emit, and since we assigned this emitter to the [focus] attribute in the input element in our template, and we declared this EventEmitter as an input to the new directive and invoked the focus method in the arrow function that we passed to the subscription to this event, the input element will receive focus after the component is initialized, and whenever setFocus is called.

I had the same need in my own app, and this worked as advertised. Thank you very much to the following: http://blog.thecodecampus.de/angular-2-set-focus-element/

Solution 5 - Angular

See https://stackoverflow.com/questions/34522306/angular-2-focus-on-newly-added-input-element for how to set the focus.

For "on load" use the ngAfterViewInit() lifecycle callback.

Solution 6 - Angular

I had a slightly different problem. I worked with inputs in a modal and it drove me mad. No of the proposed solutions worked for me.

Until i found this issue: https://github.com/valor-software/ngx-bootstrap/issues/1597

This good guy gave me the hint that ngx-bootstrap modal has a focus configuration. If this configuration is not set to false, the modal will be focused after the animation and there is NO WAY to focus anything else.

Update:

To set this configuration, add the following attribute to the modal div:

[config]="{focus: false}"

Update 2:

To force the focus on the input field i wrote a directive and set the focus in every AfterViewChecked cycle as long as the input field has the class ng-untouched.

 ngAfterViewChecked() {
    // This dirty hack is needed to force focus on an input element of a modal.
    if (this.el.nativeElement.classList.contains('ng-untouched')) {
        this.renderer.invokeElementMethod(this.el.nativeElement, 'focus', []);
    }
}

Solution 7 - Angular

Also, it can be done dynamically like so...

<input [id]="input.id" [type]="input.type" [autofocus]="input.autofocus" />

> Where input is

const input = {
  id: "my-input",
  type: "text",
  autofocus: true
};

Solution 8 - Angular

Directive for autoFocus first field

import {
  Directive,
  ElementRef,
  AfterViewInit
} from "@angular/core";

@Directive({
  selector: "[appFocusFirstEmptyInput]"
})
export class FocusFirstEmptyInputDirective implements AfterViewInit {
  constructor(private el: ElementRef) {}
  ngAfterViewInit(): void {
    const invalidControl = this.el.nativeElement.querySelector(".ng-untouched");
    if (invalidControl) {
      invalidControl.focus();
    }
  }
}

Solution 9 - Angular

I didn't have much luck with many of these solutions on all browsers. This is the solution that worked for me.

For router changes:

router.events.subscribe((val) => {
    setTimeout(() => {
        if (this.searchElement) {
            this.searchElement.nativeElement.focus();
        }
    }, 1);
})

Then ngAfterViewInit() for the onload scenario.

Solution 10 - Angular

you can use $ (jquery) :

<div>
    <form role="form" class="form-horizontal ">        
        <div [ngClass]="{showElement:IsEditMode, hidden:!IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Name</label>
                <div class="col-md-7 col-sm-7">
                    <input id="txtname`enter code here`" type="text" [(ngModel)]="person.Name" class="form-control" />

                </div>
                <div class="col-md-2 col-sm-2">
                    <input type="button" value="Add" (click)="AddPerson()" class="btn btn-primary" />
                </div>
            </div>
        </div>
        <div [ngClass]="{showElement:!IsEditMode, hidden:IsEditMode}">
            <div class="form-group">
                <label class="control-label col-md-1 col-sm-1" for="name">Person</label>
                <div class="col-md-7 col-sm-7">
                    <select [(ngModel)]="SelectedPerson.Id"  (change)="PersonSelected($event.target.value)" class="form-control">
                        <option *ngFor="#item of PeopleList" value="{{item.Id}}">{{item.Name}}</option>
                    </select>
                </div>
            </div>
        </div>        
    </form>
</div>

then in ts :

    declare var $: any;
    
    @Component({
      selector: 'app-my-comp',
      templateUrl: './my-comp.component.html',
      styleUrls: ['./my-comp.component.css']
    })
    export class MyComponent  {
    
    @ViewChild('loadedComponent', { read: ElementRef, static: true }) loadedComponent: ElementRef<HTMLElement>;
    
    setFocus() {
    const elem = this.loadedComponent.nativeElement.querySelector('#txtname');
          $(elem).focus();
    }
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionparagView Question on Stackoverflow
Solution 1 - AngularnikView Answer on Stackoverflow
Solution 2 - AngularparagView Answer on Stackoverflow
Solution 3 - AngularSergey GurinView Answer on Stackoverflow
Solution 4 - AngularKent WeigelView Answer on Stackoverflow
Solution 5 - AngularGünter ZöchbauerView Answer on Stackoverflow
Solution 6 - AngularFireGnomeView Answer on Stackoverflow
Solution 7 - AngularSoEzPzView Answer on Stackoverflow
Solution 8 - AngularAkRoyView Answer on Stackoverflow
Solution 9 - AngularrtaftView Answer on Stackoverflow
Solution 10 - AngularHamidRezaView Answer on Stackoverflow