@ viewChild 无法工作-无法读取未定义的属性 nativeElement

我试图访问一个本机元素,以便在单击另一个元素时关注它(很像 html 属性“ for”-for 不能用于这种类型的元素。

然而,我得到了一个错误:

TypeError: 无法读取未定义的“ nativeElement”属性

我尝试在 ngAfterViewInit()中记录 nativeElement,以便加载它,但它仍然会抛出错误。

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?.

例如:

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


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

完整代码:

正在使用的 html 模板的相关部分:

<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>

组件:

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);
}
}

完整的 html 模板(可能无关) :

<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>
230720 次浏览

@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

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>

it just simple :import this directory

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

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();
}

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;

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

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


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

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

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>

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();
}

in my case i just checking undefied

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

ngAfterViewInit() {

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

}