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

时间:2016-08-26 05:33:38

标签: angular viewchild

我试图访问原生元素,以便在点击另一个元素时专注于它(非常类似于html属性"对于" - 因为不能用于此类型的元素。

然而我收到错误:

  

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

我尝试在ngAfterViewInit()中调试native.lement中的nativeElement,以便加载但仍然会抛出错误。

我还在click事件处理程序中访问nativeElement,这样我可以在单击另一个元素时聚焦元素 - 这可能是什么问题,因为它在视图加载之前编译?

例如:

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>

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

完整的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>

9 个答案:

答案 0 :(得分:33)

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

不匹配
id="keywords-input"

应该是模板变量:

#keywordsInput

请注意,应使用驼峰大小写,因为模板引用名称中不允许-

@ViewChild()支持模板变量的名称为string:

@ViewChild('keywordsInput') keywordsInput;

或组件或指令类型:

@ViewChild(MyKeywordsInputComponent) keywordsInput;

另见https://stackoverflow.com/a/35209681/217408

<强>提示:
在调用keywordsInput之前未设置ngAfterViewInit()

答案 1 :(得分:12)

如果您的目标元素在隐藏元素内,您还将收到此错误。如果这是您的HTML:

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

您的@ViewChild('sp') sp是未定义的。

解决方案

在这种情况下,请不要使用*ngIf

使用类来显示/隐藏隐藏的元素。

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

答案 2 :(得分:10)

接受的答案在所有方面都是正确的,当我无法在我的一个应用程序组件中获得Google Map渲染后,我偶然发现了这个线程。

现在,如果您使用的是最新的角度版本(即7+),则必须处理以下 ViewChild 声明,即

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

现在,有趣的部分是静态值,根据定义,它是

  • static-为True,可在运行更改检测之前解析查询结果

现在用于渲染地图,我使用以下代码,

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

答案 3 :(得分:4)

当您尝试定位包装在条件中的元素时,会发生此错误。

因此,在这里,如果我使用ngIf代替[hidden],它将给我TypeError:无法读取未定义的属性'nativeElement'

因此,请使用[hidden],class.show或class.hide代替* ngIf。

<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="-10 -10 100 100">
  <g fill="rgba(0,0,72)">
    <circle class="circle" cx="191" cy="90" r="2" />
    <circle cx="5" cy="5" r="2" />
    <circle cx="10" cy="10" r="2" />
    <circle cx="5" cy="20" r="2" />
    <circle cx="10" cy="20" r="2" />
    <circle cx="20" cy="20" r="2" />
    <circle cx="30" cy="20" r="2" />
    <circle cx="40" cy="20" r="2" />
    <circle cx="50" cy="20" r="2" />
    <circle cx="60" cy="20" r="2" />
    <circle cx="30" cy="30" r="2" />
    <circle xmlns="http://www.w3.org/2000/svg" cx="35" cy="10" r="3" fill="rgba(204,153,24)" />
    <circle cx="40" cy="40" r="2" />
    <circle cx="50" cy="50" r="2" />
    <circle cx="60" cy="60" r="2" />
    <circle cx="60" cy="5" r="2" />
    <circle cx="60" cy="10" r="2" />
    <circle cx="60" cy="20" r="2" />
    <circle cx="60" cy="30" r="2" />
    <circle cx="60" cy="40" r="2" />
    <circle cx="60" cy="50" r="2" />
  </g>
</svg>

答案 4 :(得分:1)

这很简单:导入此目录

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

答案 5 :(得分:1)

我遇到了类似的问题,但就我而言,我试图读取 nativeElement 方法中的 ngOnInit

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

我已更改为 ngAfterViewInit 并且一切正常:

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

答案 6 :(得分:0)

发生的事情是,在加载DOM之前调用这些元素时,会出现此类错误。始终使用:

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

答案 7 :(得分:0)

有时,当您尝试定位包装在条件中的元素时,会发生此错误,例如: <div *ngIf="canShow"> <p #target>Targeted Element</p></div>

在此代码中,如果canShow在渲染时为false,则Angular将无法获取该元素,因为它不会被渲染,因此会出现错误。

解决方案之一是在元素上使用display: hidden而不是*ngIf,以便呈现元素但隐藏元素,直到满足条件为止。

了解更多over at Github

答案 8 :(得分:-1)

像下面那样初始化Canvas可以用于TypeScript / Angular解决方案。

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

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