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
【问题讨论】:
你试过使用属性绑定吗?我不知道你想要哪个滚动属性,但你可以试试:<div [someScrollProperty]="someComponentProperty" ...>
。然后只需在您的组件逻辑中更新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)