How to set focus to input element in modal dialog

10,887 views
Skip to first unread message

Björn Peemöller

unread,
May 2, 2017, 10:29:40 AM5/2/17
to Angular and AngularJS discussion
Hi all,

I am trying to implement a modal dialog for user switching which can be enabled or disabled using buttons. If the dialog is enabled, the input element for the user name should be focussed.


The problem I have is that I need to register an AfterViewChecked callback in which I update the FocusDirective using an EventEmitter. Previously, I called `this.focusUsername.emit` directly in `setVisible`. However, this had no effect. I suspect that this is caused by the contemporary (de)activation of the dialog, such that the set focus is removed if the dialog is internally refreshed and rendered by angular. Nevertheless, the callback gets invoked every time I click in the UI, which is far from optimal.

Does anyone have a suggestion on how to improve my solution?

Many thanks in advance,
Björn

Sander Elias

unread,
May 2, 2017, 11:23:22 PM5/2/17
to Angular and AngularJS discussion
Hi Björn,

Your focus directive is trying to make the input element in control of where the app's focus will end up. Experience has thought me that this will never work fluently. Use the HTML autofocus for initial focus. If you use something to hide/show parts of an app, that part should be responsible for setting the focus. the @Viewchild decorator is your friend here. It might be handy to use a directive to ease the selection of which input might receive focus, but the input itself should not be responsible for setting it.

Regards
Sander

Björn Peemöller

unread,
May 3, 2017, 11:45:56 AM5/3/17
to Angular and AngularJS discussion
Hi Sander,

thank you for your reply! This is what I initially, I called

@ViewChild('usernameField') input : ElementRef;
...
public show(): void {
this.setVisible(true);
this.input.nativeElement.focus();
}


where the field is provided by the template:

<input #usernameField
type="text"
class="form-control"
id="username"
name="username"
...>

  <button (click)="show()">Switch User</button>

However, I receive the error

TypeError: Cannot read property 'nativeElement' of undefined

I guess this is caused by the following constellation: The ViewChild is injected at component creation, but since the input element is intially hidden (using *ngIf), it is not created and thus not injected. If the component is later changed to be visible, then the input is created but the ViewChild reference is not injected anymore. Furthermore, if I hide the component, the input is also removed from the DOM, invalidating the reference.

Regards,
Björn

Björn Peemöller

unread,
May 3, 2017, 11:56:41 AM5/3/17
to Angular and AngularJS discussion
Hi,

I improved the solution by suspending the focus setting until the view has been refreshed:

ngAfterViewChecked() {
if (this.setFocus && this.input) {
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
this.setFocus = false;
}
}
...
public show(): void {
this.setVisible(true);
this.setFocus = true;
}

This works as expected and focusing is now done by the component and not the view. 
However, I'm still not sure whether this is the bset solution since registering the lifecycle hook seems quite costly to me.

Regards,
Björn

Sander Elias

unread,
May 4, 2017, 12:50:41 AM5/4/17
to Angular and AngularJS discussion
Hi Björn,

Looks better. However, I have to break to you, renderer is deprecated. there is now renderer2, and it doesn't have an invokeElementMethod. 
replace it with an if (you need to make it safe) something like this:

ngAfterViewChecked() {
 
if (this.setFocus && this.input $$ this.input.nativeElement) {
   
this.nativeElement.focus(); // can do this because I check if there is a nativeElement above
   
this.setFocus = false;
 
}
}

Regards
Sander

Björn Peemöller

unread,
May 4, 2017, 3:28:49 AM5/4/17
to Angular and AngularJS discussion
Hi Sander,

thank you for your reply. I changed my code accordingly and it now works just as expected.

Regards,
Björn
Reply all
Reply to author
Forward
0 new messages