Angular 2 @ViewChild 返回未定义

Posted

技术标签:

【中文标题】Angular 2 @ViewChild 返回未定义【英文标题】:Angular 2 @ViewChild returns undefined 【发布时间】:2017-01-08 11:06:33 【问题描述】:

我查看了几个相关的帖子和​​文档,但似乎仍然无法从 @ViewChild 获得预期的行为。

最终,我正在尝试设置 div 的滚动位置。此元素不是组件,而是我的 html 中的普通 div。

为此,我尝试使用@ViewChild 来获取我需要的DOM 元素,并设置它的滚动值。 (顺便说一句,如果您知道在没有 @ViewChild(或 jQuery)的情况下完成此任务的更好方法,我们将非常感谢您的回答!)

目前,@ViewChild 只返回 undefined。通过一些虚拟检查: - 我正在 AfterViewInit 中访问我的元素 - 我在这个元素上没有任何其他指令,如 *ngIf 或 *ngFor。

这是控制器:

import  Component, AfterViewInit, ViewChild, ElementRef  from '@angular/core';

@Component(
    selector: 'portfolio-page',
    templateUrl: './portfolio-page.component.html',
    styleUrls: ['./portfolio-page.component.scss']
)

export class PortfolioPageComponent implements AfterViewInit
    @ViewChild('gallery-container') galleryContainer: ElementRef;

    ngAfterViewInit()
        console.log('My element: ' + this.galleryContainer);
    

还有模板:

<div id='gallery-container' class='gallery-image-container'>
    <div class='gallery-padding'></div>
    <img class='gallery-image' src=' coverPhotoVm ' />
    <img class='gallery-image' src=' imagepath ' *ngFor='let imagepath of imagesVm' />
</div>

我的输出很简单:我的元素:未定义。

如您所见,我目前正在尝试通过 ID 访问元素,但也尝试过类名。谁能提供更多关于 ViewChild 选择器查询的详细信息?

我还看到了将哈希“#”用作@ViewChild 使用的选择器标识符的示例——但这会导致我使用#gallery-container 时出现模板解析错误。

我想不出还有什么可能是错误的。感谢所有帮助,谢谢!

此处提供完整代码:https://github.com/aconfee/KimbyArting/tree/master/client/KimbyArting/components/portfolio-page

【问题讨论】:

你试过使用属性绑定吗?我不知道你想要哪个滚动属性,但你可以试试:&lt;div [someScrollProperty]="someComponentProperty" ...&gt;。然后只需在您的组件逻辑中更新this.someComponentProperty。另请参阅***.com/a/36224837/215945。 感谢您添加此内容!实际上,我一直在研究这个作为解决我最初问题的一种方式——设置滚动值。我已经制作了一个自定义指令并将其传递给所需的滚动值。我还希望该指令监视父组件何时使用 ngOnChanges 更改此值。 ngOnChanges 是在组件和指令之间绑定属性的最佳方式吗?我注意到您发送的示例的做事方式有所不同..您能否详细说明或发送有关他们正在做什么的文档?我没有意识到组件有一个指令数组。那里存在什么默认绑定? ngOnChanges 是一个好方法。我发送的示例只是向您展示一个实际的绑定示例(在这种情况下使用[inScrollHeight])。不要担心该帖子中的内容投影和“检查后已更改”错误。组件与其在directives 数组中指定的指令之间没有默认绑定。您必须在 HTML 模板中声明所需的任何绑定。 明白了,谢谢。我仔细阅读了它,事情变得更有意义了。 Angular 2 @ViewChild annotation returns undefined的可能重复 【参考方案1】:

尝试在模板中使用 ref:

<div id='gallery-container' #galleryContainer class='gallery-image-container'>
    <div class='gallery-padding'></div>
    <img class='gallery-image' src=' coverPhotoVm ' />
    <img class='gallery-image' src=' imagepath ' *ngFor='let imagepath of imagesVm' />
</div>

并使用 ref 名称作为参数:

@ViewChild('galleryContainer') galleryContainer: ElementRef;

编辑

忘了提到任何这样声明的视图子视图只有在视图初始化后才可用。第一次发生这种情况是在ngAfterViewInit 中(导入并实现AfterViewInit 接口)。

引用名称不能包含破折号,否则这将不起作用

【讨论】:

谢谢!我之前尝试过#gallery-container,导致解析错误。我没有意识到有一个约定。我见过其他人的工作示例没有这种 ref 标识符。这是@ViewChild 唯一的期望吗? 得到您的编辑,谢谢!虽然,在我上面的代码中,你会看到我已经实现了它。我之前也尝试过使用 ref 语法......只是没有意识到它必须是驼峰式的。 必须是驼峰式的吗?那么帕斯卡大小写(#GalleryContainer)无效? 我认为只有 - 引起问题,而不是其他外壳 FWIW,接口(现在)命名为AfterViewInit,没有On 前缀。【参考方案2】:

有时,如果在您访问组件时该组件尚未初始化,您会收到一条错误消息,指出子组件未定义。

但是,即使您在 AfterViewInit 中访问子组件,有时@ViewChild 仍然返回 null。问题可能是由 *ngIf 或其他指令引起的。

解决方案是使用@ViewChildren 代替@ViewChild 并订阅组件准备就绪时执行的更改订阅。

例如,如果在父组件ParentComponent中要访问子组件MyComponent。

import  Component, ViewChildren, AfterViewInit, QueryList  from '@angular/core';
import  MyComponent  from './mycomponent.component';

export class ParentComponent implements AfterViewInit

  //other code emitted for clarity

  @ViewChildren(MyComponent) childrenComponent: QueryList<MyComponent>;

  public ngAfterViewInit(): void
  
    this.childrenComponent.changes.subscribe((comps: QueryList<MyComponent>) =>
    
      // Now you can access the child component
    );
  

【讨论】:

这是关于 *ngIf 的非常重要的一点。如果您正在异步加载数据并隐藏部分 UI,那么它不仅不可见,而且由于它不存在而无法绑定! 谢谢@Simon_Weaver。在这种情况下使用 *ngIf 给我带来了一些问题,我通过应用此解决方案最终解决了这些问题。【参考方案3】:

订阅更改

@ViewChildren(MyComponent) childrenComponent: QueryList<MyComponent>

确认工作,结合setTimeout()notifyOnChanges() 并仔细检查null

任何其他方法都会产生不可靠的结果并且难以测试。

【讨论】:

【参考方案4】:

我遇到了类似的问题。与你不同的是,我的@ViewChild 返回了一个有效的ElementRef,但是当我尝试访问它的nativeElement 时,它是undefined

我通过将视图id 设置为#content 来解决它,而不是#content = "ngModel"

<textarea type = "text"
    id = "content"
    name = "content"
    #content
    [(ngModel)] = "article.content"
    required></textarea>

【讨论】:

【参考方案5】:

这是 ViewChild 使用 DOM 元素的 .ts 文件代码

import  Component, AfterViewInit, ViewChild, ElementRef  from '@angular/core';

@Component(

    selector: 'portfolio-page',
    templateUrl: './portfolio-page.component.html',
    styleUrls: ['./portfolio-page.component.scss']
)

export class PortfolioPageComponent implements AfterViewInit

    @ViewChild('galleryContainer') galleryContainer: ElementRef;

    ngAfterViewInit()
        console.log('My element: ' + this.galleryContainer);
    

这是你的 .html 文件代码

    <div #galleryContainer class='gallery-image-container'>
    <div class='gallery-padding'></div>
    <img class='gallery-image' src=' coverPhotoVm ' />
    <img class='gallery-image' src=' imagepath ' *ngFor='let imagepath of imagesVm' />
</div>

希望对你有帮助!

【讨论】:

在为元素创建 id 时不要使用破折号或减号【参考方案6】:

另一种可能的情况是,当您在 angular 或 ionic 框架中使用 typescript 并从具有类似 javascript 签名的函数中调用 ViewChild 元素时。请改用类似函数签名的打字稿(例如,使用粗箭头 =>)。 例如

accessViewChild()
    console.log(this.galleryContainer.nativeElement.innerHTML);

会显示打印未定义

accessViewChild = ()=>
    console.log(this.galleryContainer.nativeElement.innerHTML);

将打印内部 html。

【讨论】:

胖箭头不是 TypeScript 特有的。两者都可以在 TypeScript 中使用。

以上是关于Angular 2 @ViewChild 返回未定义的主要内容,如果未能解决你的问题,请参考以下文章

Angular @ViewChild() 错误:预期 2 个参数,但得到 1 个

Angular 2+:获取@ViewChild() 的子元素

如何在 Angular 中使用多个 ViewChild 元素 (2/4)

Angular Viewchild undefined

[Angular 2] @ViewChild to access Child component's method

Angular 在项目中学习@ViewChild和ElementRef的使用