akshay Tiwari
akshay Tiwari

Reputation: 135

@viewChild and @ViewChildren gives undefined

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 @ViewChildren because I'm using ngIf.

Template.html file

  <div class="search-input" #searchDiv *ngIf="searchActive">
    <input
    #searched
    autofocus
      type="text"
      class="search-term"
      placeholder="Search"
      [(ngModel)]="searchTerms"
      (ngModelChange)="applySearch()"
    />
    <button (click)="toggleSearch(!searchActive)">
      <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{


  searchActive: 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.searchActive = 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'])
    }
  }
}

Upvotes: 0

Views: 6534

Answers (1)

KShewengger
KShewengger

Reputation: 8269

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

Upvotes: 2

Related Questions