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>
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
This answer is inspired by post Angular 2: Focus on newly added input element
Steps to set the focus on Html element in Angular2
Import ViewChildren in your Component
import { Input, Output, AfterContentInit, ContentChild,AfterViewInit, ViewChild, ViewChildren } from 'angular2/core';
Declare local template variable name for the html for which you want to set the focus
- Implement the function ngAfterViewInit() or other appropriate life cycle hooks
Following is the piece of code which I used for setting the focus
ngAfterViewInit() {vc.first.nativeElement.focus()}
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>
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/
<input id="name" type="text" #myInput />
{{ myInput.focus() }}
this is the best and simplest way, because code "myInput.focus()" runs after input created
See Angular 2: Focus on newly added input element for how to set the focus.
For "on load" use the ngAfterViewInit()
lifecycle callback.
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', []);
}
}
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.