Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2: Autofocus does not work on view 'refresh' (angular)

Tags:

angular

I'm building a QuizTool/LMS similar in nature to Duolingo (SPA-ish).

tldr: Hit the plnkr, 'Check Answers', 'Do Another', and notice the first input element no longer has the input focus -- even tho the 'autofocus' property seems to be set correctly. Can I fix this?

https://embed.plnkr.co/DU8opUuquP5MXlcfdHIe/

long version: When I first render a quiz, I am able to set the input focus (using a bound autofocus property) into the first input area on the screen (a TEXTAREA where the user will answer the first question) -- so the user can just start typing away. Excellent.

However, once the user has submitted that first set of questions (the first quiz), the user can choose to 'Do Another' quiz -- I/Angular am able to redraw a new quiz/UI (with, for now, what happen to be the same exact questions), only the 'autofocus' attribute does not seem to work -- i.e. the first input/textarea does not get the focus.

It does appear that the bound autofocus attribute is set, however.

Which means either: 1) i'm reading the attribute incorrectly somehow 2) it's a bug in Chrome (Version 56.0.2924.87 (64-bit) on Mac OSX) 3) something is getting/stealing the focus after this field's autofocus is correctly set 4) etc.

I've set up 'firstQuestion' and 'lastQuestion' local vars in the ngFor loop to help prove that the autofocus property is being set deterministically (just change the [autofocus]="firstQuestion" to [autofocus]="lastQuestion").

I'm open to fixing this however I need to. The whole 'no controller' thing is pretty new to me, so it's very possible I'm doing something stupid with this entire setup.

I have tried using Angular forms/ngForm but it doesn't seem to make a difference.

I'd also be curious to know where I could find a basic tutorial on Angular2 transitions/'controller' stuff -- i.e. how do I redraw a screen, or switch to a new view, etc. -- without what might be considered a 'traditional' controller. I think with VB you said something like:

oldForm.hide(); newForm.show();

If none of this works, I want to set the focus manually/jqLite/pre-HTML5/whatever -- haven't had much success there yet either -- thus this question.

Thank you.


Not sure how useful this is, but it seems my app -- which was set up with the ng cli tool -- is different significantly than what plunkr is producing.

i was able to discover that:

  1. even if the 'document.activeElement' was set to the correct textarea (according to querying the DOM), it might still not be activate for the user to start inputting text (I set a red bg to make it very visual), and
  2. the outer/hosting index.html page, shown below, was getting the focus after my component finished loading. i added a hack javascript function to manually push the focus back to my desired element, but it was really just for testing.

    <script>
         setInterval(function(){ 
            if(document.activeElement!=null){
                if(document.activeElement.id!='textarea-0'){ // find first input element on the page
                    document.getElementById('textarea-0').focus(); // set the focus
                }
            } 
        },
        5000);                
    </script>
    

Update:

Fixed. Thanks again to @mickdev.

This is a small twist on the first solution. I'd describe it as "using a template reference variable with the ngAfterViewChecked() lifecycle method on the component you are rendering".


Add this stuff to the component you are rendering:

import { ViewChild } from '@angular/core';

@ViewChild('focusThis') focusThis;

// to keep track of which element in our loop we are in
id:number=0 

//wait for viewchild to update then focus
ngAfterViewChecked(){
    // only focus the 0th (i.e. first) element of our loop
    if(this.id == 0){
        this.focusThis.nativeElement.focus();
        this.id++
    }
}

Then add the template reference variable, '#focusThis':

<textarea   #focusThis
            [(ngModel)]="question.useranswer" 
            [disabled]="quiz.wasEvaluated"...
like image 266
Peter Smith Avatar asked Mar 09 '17 12:03

Peter Smith


People also ask

Is it possible to use autofocus in angular?

This will work only on page navigation (if you are lucky). Here's my take on Angular autofocus directive. The following directive accepts boolean value as an input and focuses the element based on the evaluated boolean expression so the element could be focused dynamically.

How to set default focus on input box in angular?

Make It Easy: Angular auto focus for input box - Angular. 1 1. Auto focus on textbox using directive. Especially while dealing with forms you may need to set default focus on some fields. We are going to create ... 2 2. Set focus on text box on click. 3 3. Auto focus on textbox without using a directive.

How to do a page refresh on angular?

We will look at the best method to do a page refresh on Angular. First, we will have to head to our editor, open the terminal most preferably Visual Studio Code and create the app-routing.module.ts using the ng generate function.

How to use autofocus with ngafterviewinit?

We target each element with the autofocus attribute, and explicitly invoke the focus () method in its ngAfterViewInit hook. We can also take it one step further, and expose an input that will focus the element based on its value.


2 Answers

Here is a (ugly) workaround but it works :) First, import ViewChild

import {Component, NgModule, ViewChild} from '@angular/core'

@ViewChild('test') test;

doAnother() {
    this.quiz = new Quiz();

    this.test.nativeElement.focus(); 
}

Then update the view :

<div><textarea [autofocus]="firstQuestion" #test></textarea></div>

Here is a working plunker: https://plnkr.co/edit/S6wRn3tUTxZcOQdojzr6?p=preview

Update: here is a more elegant way to achieve this ElementRef

//component
import {Component, NgModule, ElementRef} from '@angular/core'

this.element.nativeElement.querySelector('#textarea-1').focus()

//template
<div><textarea id="textarea-{{ question.id }}" [autofocus]="firstQuestion"></textarea></div>

Now, you can target any textarea in your DOM. Here is a working plunker : https://plnkr.co/edit/izH61uVHBreEwdVQNoSf?p=preview

like image 190
mickdev Avatar answered Sep 29 '22 00:09

mickdev


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

Just add {{ myInput.focus() }} right after input inside template

like image 44
Sergey Gurin Avatar answered Sep 28 '22 23:09

Sergey Gurin