Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@viewChild not working - cannot read property nativeElement of undefined

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> 
like image 986
BeniaminoBaggins Avatar asked Aug 26 '16 05:08

BeniaminoBaggins


People also ask

What is nativeElement in angular?

Angular ElementRef is a wrapper around a native element inside of a View. It's simply a class that wraps native DOM elements in the browser and allows you to work with the DOM by providing the nativeElement object which exposes all the methods and properties of the native elements.

How do I declare ElementRef?

Getting ElementRef in Component ClassCreate a template reference variable for the element in the component/directive. Use the template variable to inject the element into component class using the ViewChild or ViewChildren.

How do you use ViewChildren?

The Viewchild can also be used to query HTML elements. First, assign a Template variable ( #para in the example below) to the HTML element. You can then use the ViewChild to query the element. ViewChild returns a ElementRef , which is nothing but a wrapper around the native HTML element.

What is Javascript nativeElement?

nativeElement. Angular will give you the reference of your component which rendered on DOM. It means Angular gives a plain javascript object and you can play with this object at any browser.


1 Answers

@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

like image 150
Günter Zöchbauer Avatar answered Sep 18 '22 09:09

Günter Zöchbauer