Reputation: 1831
I have an input field that I want to disable when a user presses the 'enter' key. It will then try and fetch a user details. When it has fetched the details or there has been an error, I want to enable the input field again and set the focus on that field.
I am using this directive to set the focus on the input field:
import { Directive, OnChanges, ElementRef, Input } from '@angular/core';
@Directive({
selector: '[myFocus]'
})
export class FocusDirective implements OnChanges {
@Input('myFocus') isFocused: boolean;
constructor(private hostElement: ElementRef) { }
ngOnChanges() {
if(this.isFocused) {
this.hostElement.nativeElement.focus();
}
}
}
It is implemented in the html like so:
<input type="text" class="form-control" [(ngModel)]="text"
[myFocus]="isFocused" [disabled]="disableInput"
(keydown)="podcastsKeyDown($event)">
The podcastKeyDown() function looks like this:
podcastsKeyDown(event) {
if(event.keyCode == 13) {
this.disableInput = true;
this.getUser(this.text).subscribe(account => {
this.disableInput = false;
this.isFocused = true;
});
}
}
When the user account has successfully returned i set the input field to be enabled, which works correctly. However the focus is not returned to this input. I have tried placing a timeout around re-setting the focus just in case there is some timing issue, however this did not work either. The code looked like this:
setTimeout(() => {
this.isPodcastNamesFocused = true;
}, 100);
Any ideas what is happening here and how to fix it?
Thanks in Advance.
EDIT 1: To be precise - it actually works the first time when using the timeout, but for subsequent user entries it does not...
Upvotes: 1
Views: 1876
Reputation: 2128
ngOnChanges
will work only if there is a pure change means if you have declared this.isFocused
as true
and you are trying to set it as true
again ngOnChanges
won't trigger
Try something like this
@Input('myFocus')
set isFocused(value: boolean) {
if (value) {
this.hostElement.nativeElement.focus();
}
};
or change your directive constructor like this constructor(@Host() @Self() private hostElement: ElementRef) { }
Just got an idea - hope it works
Try to pass the reference
variable as an @Input()
and read it
<input type="text" class="form-control" [(ngModel)]="text" #inputType [element]="inputType"
[myFocus]="isFocused" [disabled]="disableInput"
(keydown)="podcastsKeyDown($event)">
@Input('element') elementType : ElementRef;
Try to focus the elementType
now
Hope it works - Happy coding !!
Upvotes: 0
Reputation: 1831
It seems this was fixed by using the setTimeout() function AND remembering to set the focus to false while the user was being fetched. This must have been causing the focus directive to get confused.
podcastsKeyDown(event) {
if(event.keyCode == 13) {
//This is the important line that was missing
this.isFocused = false;
this.disableInput = true;
this.getUser(this.text).subscribe(account => {
this.disableInput = false;
setTimeout(() => {
this.isFocused = true;
});
});
}
}
Upvotes: 0
Reputation: 2873
I suggest the following solution. I tested this code locally. And it works every time for me. Most of the code here is for demonstration.
Directive:
import {Directive, ElementRef, Input} from '@angular/core';
@Directive({
selector: '[myFocus]'
})
export class MyFocusDirective {
// as for me I prefer using setters for properties that have non-trivial logic when being set. They are simple, predictable and robust.
@Input('myFocus')
set isFocused(val: boolean) {
this._isFocused = val;
if (this._isFocused) {
this.hostElement.nativeElement.focus();
}
}
get isFocused(): boolean {
return this._isFocused;
}
_isFocused: boolean;
constructor(private hostElement: ElementRef) { }
}
Component:
import { Component } from '@angular/core';
import {Observable, of, timer} from 'rxjs';
import {map, take} from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
text = 'app';
isFocused: boolean = true;
isDisabled: boolean = false;
userData: {name: string, age: number} = null;
podcastsKeyDown(event) {
if (event.keyCode === 13) {
this.isFocused = false;
this.isDisabled = true;
// take(1) not to have subscriptions hanging around
this.getUser(this.text).pipe(take(1)).subscribe(account => {
this.isDisabled = false;
this.userData = account;
// timeout first makes DOM to render setting isDisabled to false
// when field is enabled we can make it focused
setTimeout(() => {
this.isFocused = true;
});
});
}
}
// some mock data
private getUser(name: string): Observable<{name: string, age: number}> {
return timer(2000).pipe(map(() => {
return {name, age: Math.round(Math.random() * 30 + 10)};
}));
}
}
<input type="text" class="form-control" [(ngModel)]="text"
[myFocus]="isFocused" [disabled]="isDisabled"
(keydown)="podcastsKeyDown($event)">
<p>{{account | json}}</p>
Upvotes: 1