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 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.
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.
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.
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.
@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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With