Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular6 AutoFocus using *ngIf

Tags:

angular

<div class="dropdownContainer" placeholder="test" (click)="ShowDropDown()"  />

<div #tref *ngIf="showDropDown == 1" class="dropdownList" (focusout)="HideDropDown()" style="border:1px solid black;" >this is my test</div>

After clicking the dropDownContainer i would like the dropdownList to appear and have focus put on it.

I Have tried using

  @ViewChild("tref", {read: ElementRef}) tref: ElementRef; 

method, but it returns undefined because that element doesnt exist in the DOM until the above div is clicked. How can I autofocus on a dynamic NON INPUT DOM object?

EDIT Updated my code per suggestions, this still will not autofocus on the div.

  @ViewChild("tref") tref: ElementRef;
      ShowDropDown() {
      this.showDropDown = 1;
      this.tref.nativeElement.focus();
      console.log(this.tref);
  }
HideDropDown(){
  console.log('test out')
  this.showDropDown = 0;
}



<input   #tref class="dropdownContainer" placeholder="george" (click)="ShowDropDown()"  />
<div tabindex="-1" (focusout)="HideDropDown()" [hidden]="showDropDown == 0" class="dropdownList"  style="border:1px solid black;" >this is my test</div>

ANSWER TO THE PROBLEM Two fold answer.

1) DIVS cannot have focus unless they have tabindex. Stack answer

2)I need to include setTimeout(() => this.tref.nativeElement.focus(), 1); because an element that is hidden is not automatically ready to receive focus.

3)*ngIf and hidden both worked, once i put in the above fixes

Cleaned up code

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.less']
})
export class AppComponent {

  constructor() {
  }

  showDropDown = 0;



  @ViewChild("tref") tref: ElementRef;
  ShowDropDown() {
this.showDropDown = 1;
setTimeout(() => this.tref.nativeElement.focus(), 1);
  }

HideDropDown(){
  this.showDropDown = 0;
}

test(){ console.log('works');}

}

<div tabindex="-2"    class="dropdownContainer" placeholder="george" (click)="ShowDropDown()"   ></div>
<div tabindex="-1" #tref [hidden]="showDropDown == 0" class="dropdownList"  style="border:1px solid black;" (click)="test()" (focusout)="HideDropDown()">this is my test</div>
like image 568
gh9 Avatar asked Nov 15 '18 21:11

gh9


2 Answers

You can focus the dropdown element as soon as it becomes visible, with the help of ViewChildren and the QueryList.changes event. This technique works no matter how long it takes for the element to appear in the view.

In the template, give a tabindex attribute to the dropdown div:

<div class="dropdownContainer" (click)="showDropDown = true">
  Click here to show the dropdown
</div>

<div #dropDownDiv *ngIf="showDropDown" tabindex="1" class="dropdownList" (focusout)="showDropDown = false">
  This is the dropdown element
</div>

In code, retrieve the dropdown element with ViewChildren, and set the QueryList.changes event handler in ngAfterViewInit. When you are notified that the element has become visible, you can set the focus on it:

showDropDown = false;

@ViewChildren("dropDownDiv") private dropDownDivList: QueryList<ElementRef>;

ngAfterViewInit() {
  this.dropDownDivList.changes.subscribe((list: QueryList<ElementRef>) => {
    if (list.length > 0) {
      list.first.nativeElement.focus();
    }
  });
}

See this stackblitz for a demo.

like image 94
ConnorsFan Avatar answered Sep 22 '22 21:09

ConnorsFan


Change from *ngIf="showDropDown" to [hidden]="! showDropDown" and you should be able to use @ViewChild inside the component and prevent the "undefiend" issue.

If that still didn't work, you could always pass the element to the component through the click event by changing the click to this (click)="ShowDropDown(tref)". Note that in order for this to work, you'd still need to change *ngIf to [hidden].

like image 29
Uğur Dinç Avatar answered Sep 23 '22 21:09

Uğur Dinç