Angular 2 @ViewChild returns undefined

时间:2016-08-31 18:34:52

标签: dom angular scroll viewchild

I've looked at several related posts and documentation, but still can't seem to get expected behavior from @ViewChild.

Ultimately, I'm trying to set the scroll position of a div. This element isn't a component, but a normal div in my HTML.

To accomplish this, I'm trying to use @ViewChild to get the DOM element I need, and set its scroll value. (As an aside, if you know a better way to accomplish this without @ViewChild (or jQuery), answers will be very much appreciated!)

At the moment, @ViewChild only returns undefined. Going through some dummy checks: - I am accessing my element in AfterViewInit - I do not have any other directives like *ngIf or *ngFor on this element.

Here's the controller:

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

And the template:

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

My output is simple: My element: undefined.

As you can see, I'm currently trying to access the element by ID, but have tried class name as well. Could anyone provide more detail about what the ViewChild selector query is expecting?

I've also seen examples where a hash '#' is used as the selector idendifier that @ViewChild uses -- -- but this causes a template parse error for me with #gallery-container.

I can't think of anything else that could possible be wrong here. All help is appreciated, thanks!

Full code available here: https://github.com/aconfee/KimbyArting/tree/master/client/KimbyArting/components/portfolio-page

7 个答案:

答案 0 :(得分:48)

请尝试在模板中使用参考:

<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接口)。

引用名称不得包含破折号,否则无法使用

答案 1 :(得分:14)

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

但是,即使您访问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
    });
  }
}

答案 2 :(得分:1)

订阅

上的更改
@ViewChildren(MyComponent) childrenComponent: QueryList<MyComponent>

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

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

答案 3 :(得分:0)

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

我通过将视图id设置为#content而不是#content = "ngModel"来解决了这个问题。

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

答案 4 :(得分:0)

我今天遇到了同样的问题。奇怪地使用setTimeout对我有用。

尝试一下:

ngAfterViewInit(){
    setTimeout(_ => console.log('My element: ' + this.galleryContainer), 0);
}

答案 5 :(得分:0)

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

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

将显示未定义的打印内容

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

将打印内部html。

答案 6 :(得分:0)

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

希望有帮助!