@viewChild not working - cannot read property nativeElement of undefined

AngularViewchild

Angular Problem Overview


I'm trying to access a native element in order to focus on it when another element is clicked (much like the html attribute "for" - for cannot be used on elements of this type.

However I get the error:

> TypeError: Cannot read property 'nativeElement' of undefined

I try to console.log the nativeElement in ngAfterViewInit() so that it is loaded but it still throws the error.

I also access nativeElement in the click event handler, so that I can focus the element when another element is clicked - is this possibly what is mucking it up, because it compiles before the view has loaded?.

eg:

ngAfterViewInit() {
    console.log(this.keywordsInput.nativeElement); // throws an error
}

focusKeywordsInput(){
    this.keywordsInput.nativeElement.focus();
}

full Code:

relevant part of the html template being used:

<div id="keywords-button" class="form-group" (click)="focusKeywordsInput()">
	<input formControlName="keywords" id="keywords-input" placeholder="KEYWORDS (optional)"/>
	<div class="form-control-icon" id="keywords-icon"></div>
</div>

component.ts:

import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
import {  REACTIVE_FORM_DIRECTIVES, 
          FormGroup, 
          FormBuilder, 
          Validators,
          ControlValueAccessor
        } from '@angular/forms';
import { NumberPickerComponent } from './number-picker.component';
import { DistanceUnitsComponent } from './distance-units.component';
import { MapDemoComponent } from '../shared/map-demo.component';
import { AreaComponent } from './area-picker.component';
import { GoComponent } from './go.component';
import { HighlightDirective } from '../highlight.directive';

@Component({
   selector: 'find-form',
   templateUrl: 'app/find-page/find-form.component.html',
   styleUrls: ['app/find-page/find-form.component.css'],
   directives: [REACTIVE_FORM_DIRECTIVES, 
                NumberPickerComponent, 
                DistanceUnitsComponent, 
                MapDemoComponent, 
                AreaComponent, 
                GoComponent]
})
export class FindFormComponent implements OnInit, AfterViewInit {
   findForm: FormGroup;
   submitted: boolean; // keep track on whether form is submitted
   events: any[] = []; // use later to display form changes
   @ViewChild('keywords-input') keywordsInput;
//comment
   constructor(private formBuilder: FormBuilder, el: ElementRef) {}

   ngOnInit() {
      this.findForm = this.formBuilder.group({
         firstname: ['', [ Validators.required, Validators.minLength(5) ] ],
         lastname: ['', Validators.required],
         keywords: [],
         area: ['', Validators.required],
         address: this.formBuilder.group({
            street: [],
            zip: [],
            city: []
         })
      });

      this.findForm.valueChanges.subscribe(data => console.log('form changes', data));
   }

     ngAfterViewInit() {
    console.log(this.keywordsInput.nativeElement); // throws an error
  }

   focusKeywordsInput(){
      this.keywordsInput.nativeElement.focus();
   }

   save(isValid: boolean) {
      this.submitted = true;
      // check if model is valid
      // if valid, call API to save customer
      console.log(isValid);
   }
}

full html template (probably irrelevant):

<form class="text-uppercase" [formGroup]="findForm" (ngSubmit)="save(findForm.value, findForm.valid)">
	<div class="row is-heading">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group">
			<h2 class="search-filter-heading heading m-x-auto">find vegan</h2>
		</div>
	</div>
	<div class="row has-error-text">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group btn-group" style="height:64px;">
			<div style="position: relative; display: inline-block; width: 100%;">
				<multiselect #multiselect></multiselect>
			</div>
		</div>
	</div>
	<div class="row error-text"  [style.display]="multiselect.selectedCategories.length < 1 && submitted ? 'block' : 'none'">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 form-group input-group btn-group">
			<small>Please select at least 1 category.</small>
		</div>
	</div>
	<div class="row is-heading">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group">
			<h2 class="search-filter-heading heading m-x-auto">within</h2>
		</div>
	</div>
	<div class="row">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group btn-group" style="height:64px;">
			<div style="position: relative; display: inline-block;">
				<number-picker #numberPicker></number-picker>
			</div>
			<distance-units></distance-units>
		</div>
	</div>
	<div class="row is-heading">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group">
			<h2 class="search-filter-heading heading m-x-auto">of</h2>
		</div>
	</div>
	<div class="row has-error-text">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group btn-group" style="height:64px;">
			<div style="position: relative; display: inline-block; width: 100%;">
				<my-area></my-area>
			</div>
		</div>
	</div>
	<div class="row error-text"  [style.display]="multiselect.selectedCategories.length < 1 && submitted ? 'block' : 'none'">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 form-group input-group btn-group">
			<small [hidden]="findForm.controls.firstname.valid || (findForm.controls.firstname.pristine && !submitted)">Please enter an area.</small>
		</div>
	</div>
	<div class="row is-heading">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group">
			<h2 class="search-filter-heading heading m-x-auto">keywords</h2>
		</div>
	</div>
	<div class="row form-group">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group btn-group" style="height:64px;">
			<div style="position: relative; display: inline-block; width: 100%;">
				<div id="keywords-button" class="form-group" (click)="focusKeywordsInput()">
					<input formControlName="keywords" id="keywords-input" placeholder="KEYWORDS (optional)"/>
					<div class="form-control-icon" id="keywords-icon"></div>
				</div>
			</div>
		</div>
	</div>
	<div class="row">
		<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-lg-4 offset-lg-4 input-group btn-group" style="height:64px;">
			<div style="position: relative; display: inline-block; width: 100%;">
				<go></go>
			</div>
		</div>
	</div>
</form>

Angular Solutions


Solution 1 - Angular

@ViewChild('keywords-input') keywordsInput; doesn't match id="keywords-input"

id="keywords-input"

should be instead a template variable:

#keywordsInput

Note that camel case should be used, since - is not allowed in template reference names.

@ViewChild() supports names of template variables as string:

@ViewChild('keywordsInput') keywordsInput;

or component or directive types:

@ViewChild(MyKeywordsInputComponent) keywordsInput;

See also https://stackoverflow.com/a/35209681/217408

Hint:
keywordsInput is not set before ngAfterViewInit() is called

Solution 2 - Angular

You'll also get this error if your target element is inside a hidden element. If this is your HTML:

<div *ngIf="false">
    <span #sp>Hello World</span>
</div>

Your @ViewChild('sp') sp will be undefined.

Solution

In such a case, then don't use *ngIf.

Instead use a class to show/hide your element being hidden.

<div [class.show]="shouldShow">...</div>

Solution 3 - Angular

The accepted answer is correct in all means and I stumbled upon this thread after I couldn't get the Google Map render in one of my app components.

Now, if you are on a recent angular version i.e. 7+ of angular then you will have to deal with the following ViewChild declaration i.e.

@ViewChild(selector: string | Function | Type<any>, opts: {
read?: any;
static: boolean;
})

Now, the interesting part is the static value, which by definition says

  • static - True to resolve query results before change detection runs

Now for rendering a map, I used the following ,

@ViewChild('map', { static: true }) mapElement: any;
  map: google.maps.Map;

Solution 4 - Angular

This error occurs when you're trying to target an element that is wrapped in a condition.

So, here if I use ngIf in place of [hidden], it will give me TypeError: Cannot read property 'nativeElement' of undefined

So use [hidden], class.show or class.hide in place of *ngIf.

<button (click)="displayMap()" class="btn btn-primary">Display Map</button>

   <div [hidden]="!display">
      <div #mapContainer id="map">Content to render when condition is true.</div>
   </div>

Solution 5 - Angular

I had a similar problem, but in my case I was trying to read the nativeElement inside the ngOnInit method:

@ViewChild('userNameInput') userNameInput: ElementRef<HTMLInputElement>;
...
ngOnInit(): void {
    this.userNameInput.nativeElement.focus();
}

I've changed to ngAfterViewInit and everything worked fine:

@ViewChild('userNameInput') userNameInput: ElementRef<HTMLInputElement>;
...
ngAfterViewInit(): void {
    this.userNameInput.nativeElement.focus();
}

Solution 6 - Angular

Sometimes, this error occurs when you're trying to target an element that is wrapped in a condition, for example: <div *ngIf="canShow"> <p #target>Targeted Element</p></div>

In this code, if canShow is false on render, Angular won't be able to get that element as it won't be rendered, hence the error that comes up.

One of the solutions is to use a display: hidden on the element instead of the *ngIf so the element gets rendered but is hidden until your condition is fulfilled.

Read More over at Github

Solution 7 - Angular

in my case i just checking undefied

` @ViewChild('myinput') myInputField: ElementRef;

ngAfterViewInit() {

if (this.myInputField !== undefined) {
  this.myInputField.nativeElement.focus();
}

}

Solution 8 - Angular

What happens is when these elements are called before the DOM is loaded these kind of errors come up. Always use:

 window.onload = function(){
     this.keywordsInput.nativeElement.focus();
 }

Solution 9 - Angular

Initializing the Canvas like below works for TypeScript/Angular solutions.

const canvas = <HTMLCanvasElement> document.getElementById("htmlElemId"); 

const context = canvas.getContext("2d"); 

Solution 10 - Angular

it just simple :import this directory

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBeniaminoBagginsView Question on Stackoverflow
Solution 1 - AngularGünter ZöchbauerView Answer on Stackoverflow
Solution 2 - Angularuser2023861View Answer on Stackoverflow
Solution 3 - AngularPrateekView Answer on Stackoverflow
Solution 4 - AngularTapas VashiView Answer on Stackoverflow
Solution 5 - AngularFelipe WindmollerView Answer on Stackoverflow
Solution 6 - AngularBundayy OlayinkaView Answer on Stackoverflow
Solution 7 - AngularNikseView Answer on Stackoverflow
Solution 8 - AngularPriyanka AroraView Answer on Stackoverflow
Solution 9 - Angularluke neeleyView Answer on Stackoverflow
Solution 10 - AngularMunaf WadiwalaView Answer on Stackoverflow