Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 @ViewChild returns undefined

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

like image 524
Adam Avatar asked Aug 31 '16 18:08

Adam


People also ask

Why is my ViewChild undefined?

The problem can be caused by the *ngIf or other directive. The solution is to use the @ViewChildren instead of @ViewChild and subscribe the changes subscription that is executed when the component is ready. For example, if in the parent component ParentComponent you want to access the child component MyComponent .

What does ViewChild return?

ViewChild returns the first matching element and ViewChildren returns all the matching elements as a QueryList of items. We can use these references to manipulate element properties in the component.

What does @ViewChild does in Angular?

The @ViewChild decorator allows us to inject into a component class references to elements used inside its template, that's what we should use it for. Using @ViewChild we can easily inject components, directives or plain DOM elements.

What is the difference between @ViewChild and @input?

While Angular inputs/outputs should be used when sharing data to and from child components, ViewChild should be used when trying to utilize properties and methods of the child component directly in the parent component.


3 Answers

Try using a ref in your template instead:

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

And use the ref name as the argument:

@ViewChild('galleryContainer') galleryContainer: ElementRef;

EDIT

Forgot to mention that any view child thus declared is only available after the view is initialized. The first time this happens is in ngAfterViewInit (import and implement the AfterViewInit interface).

The ref name must not contain dashes or this will not work

like image 80
Aviad P. Avatar answered Oct 16 '22 10:10

Aviad P.


Sometimes, if the component isn’t yet initialized when you access it, you get an error that says that the child component is undefined.

However, even if you access to the child component in the AfterViewInit, sometimes the @ViewChild was still returning null. The problem can be caused by the *ngIf or other directive.

The solution is to use the @ViewChildren instead of @ViewChild and subscribe the changes subscription that is executed when the component is ready.

For example, if in the parent component ParentComponent you want to access the child component 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
    });
  }
}
like image 29
Marco Barbero Avatar answered Oct 16 '22 08:10

Marco Barbero


Subscribing to changes on

@ViewChildren(MyComponent) childrenComponent: QueryList<MyComponent>

confirmed working, combined with setTimeout() and notifyOnChanges() and careful null checking.

Any other approach produces unreliable results and is hard to test.

like image 2
Martin Stetina Avatar answered Oct 16 '22 10:10

Martin Stetina