0

I'm working on Angular 9 and want to access an input field after clicking on a button. right now it gives me undefined. I have tried @ViewChild and @viewChildern because I'm using ngIf.

Template.html file

  <div class="search-input" #searchDiv *ngIf="serachActive">
    <input
    #searched
    autofocus
      type="text"
      class="serach-term"
      placeholder="Search"
      [(ngModel)]="searchTerms"
      (ngModelChange)="applySearch()"
    />
    <button (click)="toggleSearch(!serachActive)">
      <span class="material-icons"> search </span>
    </button>
  
    <ul class="search-list">
      <li *ngFor="let result of results">
        <a [routerLink]="['/', 'video', 'details', result._id]">{{
          result.title ? result.title : ''
        }}</a>
      </li>
    </ul>
  </div>

Template.ts file

import { Component, OnInit,AfterViewInit,ElementRef,ViewChild,ViewChildren } from '@angular/core';
import { UserService } from '../../../user.service';
import { VideoService } from '../../../services/video.service';
import { Subject } from 'rxjs';
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { Router } from '@angular/router';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css'],
})
export class HeaderComponent implements OnInit,AfterViewInit{


  serachActive: boolean = false;
  @ViewChildren('searched') searchElement: ElementRef;
  @ViewChildren("searched") input: ElementRef;
  user;
  subject = new Subject<string>();
  results = [];
  searchTerms;
  loggedIn: Boolean = false;
  constructor(
    private userService: UserService,
    private videoService: VideoService,
    private router: Router
  ) {
    this.user = this.userService.getUser();
    this.loggedIn = this.userService.isAuthenticated();
  }
 



  ngOnInit() {
    console.log('on init', this.input);  //undefined
    this.subject
      .pipe(debounceTime(400), distinctUntilChanged())
      .subscribe((value) => {
        this.router.navigate(['search'], { queryParams: { term: value } });
      });
  }
  ngAfterViewInit() {
    console.log('on after', this.input);  //undefined
    
}

  toggleSearch(toggledata) {
    this.serachActive = toggledata;
    this.results = [];
    this.searchTerms = '';
    console.log(this.input)   //undefined
    console.log(this.searchElement.nativeElement) //undefined
   
  }
 
  applySearch() {
    const searchText = this.searchTerms;
    this.subject.next(searchText);
     this.searchElement.nativeElement.focus();  //undefined
 
  }

  menuButtonClick(button){
    if(button === "history"){
      this.router.navigate(['history'])
    }
  }
}
georgeawg
  • 48,608
  • 13
  • 72
  • 95
akshay Tiwari
  • 137
  • 3
  • 11
  • There is a reference here: https://stackoverflow.com/questions/34947154/angular-2-viewchild-annotation-returns-undefined?rq=1 - You may wish to try `@ViewChild()` and with `{static: false}` (since `*ngIf` is used) – Neelavar Dec 08 '20 at 12:11
  • @Neelavar I have tried but still getting `undefined`. – akshay Tiwari Dec 08 '20 at 12:17

1 Answers1

2

Use ViewChild since you're only searching for 1 element ID.

If adding { static: true } or { static: false } in your ViewChild options doesn't work as what is stipulated on Angular Static Query Migration Documentation

Use ChangeDetectorRef instead:

@Component({...})
export class AppComponent {

  @ViewChild('searchInput') input: ElementRef;

  isShow: boolean = false;

  constructor(private cdr: ChangeDetectorRef) {}

  toggle(): void {
    this.isShow = !this.isShow;
    this.cdr.detectChanges();            // Detects changes which this.isShow is responsible on showing / hiding 
                                         // the element you're referencing to in ViewChild

    if (this.isShow)                     // If element is shown, console the referenced element 
      console.log(this.input);
  }

}

Have created a Stackblitz Demo for your reference

KShewengger
  • 7,853
  • 3
  • 24
  • 36
  • 1
    @akshayTiwari, the problem is always the same, Angular makes all the instruction of your code and "refresh" the app., so if you makes `this.isShow=true;console.log(this.input)` this input not exist. if you write `this.isShow=true;this.cdr.detetChanges();console.log(this.input)` this input exist because "cdr.detectchanges" makes the the app refresh. Another option is use `this.isShow=true;setTimeout(()=>{console.log(this.input)}` because the instruction under setTimeout is executed after Angular refresh the app. – Eliseo Dec 08 '20 at 12:51
  • thanks, @Eliseo for this explanation actually I was trying to understand how `ChangeDetectorRef` solved my problem. but this explanation helped me a lot. – akshay Tiwari Dec 08 '20 at 13:10