[angular] Angular2 - Focusing a textbox on component load

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.

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

This question is related to angular angular2-template angular2-forms

The answer is


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.

_x000D_
_x000D_
///This is typescript_x000D_
import {Component, Input, Output, AfterContentInit, ContentChild,_x000D_
  AfterViewChecked, AfterViewInit, ViewChild,ViewChildren} from 'angular2/core';_x000D_
_x000D_
export class AppComponent implements AfterViewInit,AfterViewChecked { _x000D_
   @ViewChildren('input') vc;_x000D_
  _x000D_
   ngAfterViewInit() {            _x000D_
        this.vc.first.nativeElement.focus();_x000D_
    }_x000D_
  _x000D_
 }
_x000D_
<div>_x000D_
    <form role="form" class="form-horizontal ">        _x000D_
        <div [ngClass]="{showElement:IsEditMode, hidden:!IsEditMode}">_x000D_
            <div class="form-group">_x000D_
                <label class="control-label col-md-1 col-sm-1" for="name">Name</label>_x000D_
                <div class="col-md-7 col-sm-7">_x000D_
                    <input #input id="name" type="text" [(ngModel)]="person.Name" class="form-control" />_x000D_
_x000D_
                </div>_x000D_
                <div class="col-md-2 col-sm-2">_x000D_
                    <input type="button" value="Add" (click)="AddPerson()" class="btn btn-primary" />_x000D_
                </div>_x000D_
            </div>_x000D_
        </div>_x000D_
        <div [ngClass]="{showElement:!IsEditMode, hidden:IsEditMode}">_x000D_
            <div class="form-group">_x000D_
                <label class="control-label col-md-1 col-sm-1" for="name">Person</label>_x000D_
                <div class="col-md-7 col-sm-7">_x000D_
                    <select [(ngModel)]="SelectedPerson.Id"  (change)="PersonSelected($event.target.value)" class="form-control">_x000D_
                        <option *ngFor="#item of PeopleList" value="{{item.Id}}">{{item.Name}}</option>_x000D_
                    </select>_x000D_
                </div>_x000D_
            </div>_x000D_
        </div>        _x000D_
    </form>_x000D_
</div>
_x000D_
_x000D_
_x000D_


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', []);
    }
}

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();
    }
    }

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
};

Directive for autoFocus first field

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


<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)


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/


See Angular 2: Focus on newly added input element for how to set the focus.

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


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


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.


Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7

Examples related to angular2-template

Please add a @Pipe/@Directive/@Component annotation. Error How to truncate text in Angular2? What is let-* in Angular 2 templates? angular2: Error: TypeError: Cannot read property '...' of undefined Can't bind to 'ngForOf' since it isn't a known property of 'tr' (final release) Angular2: Cannot read property 'name' of undefined How to prevent Browser cache on Angular 2 site? Angular 2 Scroll to top on Route Change <ng-container> vs <template> Angular 2: Can't bind to 'ngModel' since it isn't a known property of 'input'

Examples related to angular2-forms

How can I manually set an Angular form field as invalid? Angular 2 Cannot find control with unspecified name attribute on formArrays Remove all items from a FormArray in Angular Angular ReactiveForms: Producing an array of checkbox values? How to make readonly all inputs in some div in Angular2? tslint / codelyzer / ng lint error: "for (... in ...) statements must be filtered with an if statement" Min / Max Validator in Angular 2 Final TypeScript-'s Angular Framework Error - "There is no directive with exportAs set to ngForm" Can't bind to 'formGroup' since it isn't a known property of 'form' ngModel cannot be used to register form controls with a parent formGroup directive