简体   繁体   中英

Angular 8 viewChild returns undefined

I am trying to access the childView instance but it keeps saying the the childView is undefined.

Here is my code for childViews:

@ViewChild(CreateQuestionnaireComponent,{ read: true, static: false })  private childQuestionnaireDialog:CreateQuestionnaireComponent;
@ViewChild(ProjectNavbarComponent,{ read: true, static: false })  private childProjectNavBar:ProjectNavbarComponent;
@ViewChild(QuestionnaireNodeComponent,{ read: true, static: false }) private childQuestionnaireNode:QuestionnaireNodeComponent;
....

onCreateTerminal() {
        this.childQuestionnaireDialog.generateQuestionnaireDropDownList();
        this.childQuestionnaireDialog.resetFields();
        this._hideQuestionnaireDialog = false;
        this._modalTitle = 'New Terminal';
        this._placeHolderText = 'Terminal Questionnaire Title';
        this._terminal = true;
    }

...

It says:this.childQuestionnaireDialog is undefined".

It was working with Angular 7.

As per my new knowledge, the @viewChild takes a flag called static. If we put the flag to true, the parent component tries to get a reference to the childView during its own creation. In other words, we could have an instance of the childView in the onInit() method of the parent Component.Basically a one time access because we won't be able to access in any other methods.

The flag set to false, is basically the new way in ivy renderer.

The problem in my case, neither options are working.

I had a similar problem where ViewChild component is undefined in onInit() method.

This fixed the issue :

// Ensure Change Detection runs before accessing the instance
@ContentChild('foo', { static: false }) foo!: ElementRef;

// If you need to access it in ngOnInit hook
@ViewChild(TemplateRef, { static: true }) foo!: TemplateRef;

You must be trying to access the results of a ViewChild query before the view has completed initializing.

So, you can either mark the query static:

@ViewChild('test', {static: true}) test: ElementRef;

... or move the logic to ngAfterViewInit (preferred).

Refer https://angular.io/guide/static-query-migration

I spent an hour not finding it on SO, so it may also help someone else: My mistake was that I used the wrong selector in the angular html. The component I wanted to reference looks like this:

@Component({
  selector: 'app-universal-modal',
  templateUrl: './universal-modal.component.html',
  styleUrls: ['./universal-modal.component.scss']
})
export class UniversalModalComponent implements OnInit {

Now I used viewchild in its parent:

  @ViewChild('universalModalComponent', {static: true}) universalModalComponent: UniversalModalComponent;

And the html selector should be app-universal-modal :

  <app-universal-modal #universalModalComponent ></app-universal-modal>

But instead I used new-task selector.

Strange thing there was no compile time error, but a runtime one...

In my case i had rendered my child on *ngIf condition. Hence ViewChild was unable to initialize the required element. Have updated the logic to render the component outside *ngIf, there by ViewChild was able to successfully initialize the element.

Hence if possible, when ViewChild is required, alter the logic to render the component outside *ngIf and show appropriate messages. Or Hide the visibility of the elements using CSS, instead of *ngIf.

If you are using #viewchildTemplateRef element inside *ngIf condition in .HTML file then @ViewChild selector/reference variable in .ts file will be undefined. So, use [ngClass] instead.

Example: Use [ngClass]={'hide': data.length = 0} instead of *ngIf=data.length>0

According to the docs , the metadata property read is:

`read - read a different token from the queried elements.`

In other words, it's used if you want to read in ViewContainerRef or the Component name instead of the normal ElementRef (which is the default if you leave read out). So putting true as the value is saying to return type true from the element, which as far as I know is impossible.

A much better explanation is here , but the short answer to your problem is take out the read property or to specify ElementRef or the specific type you want.

Slightly different issue on my side, so adding for posterity.

I was initially implementing a @ViewChild and within the scope of the component everything was alright as normal.

However I was implementing a event subscription to triger a method on the child using this.action() withing the event, which lost its reference via the event. (Bad eventing on my side)

Strange one for me, but one solution (not the best fix but before the event manager refactor) was to pass through the reference to this at subscription creation time.

var self = this; // var because we want it to stick around. 

subscription.event.subscribe(()=>{
 self.callOnThisRef();
}) 

I'm sure this is by design as it highlighted the problems with my event management. But it was strange to debug.

For Angular 7 we won't get the {static: boolean} parameter instead we are getting {read: any} in my case I had a *ngif directive so the reference of the element was not getting by Viewchild(), what I did is I just checked the element is undefined or not in Component.ts


If you need to access it in ngOnInit hook

Here is an example for removing class by Hot listening

@ViewChild('elref', ) elref: ElementRef;

import { Component, ElementRef, , Renderer2, ViewChild } from'@angular/core';

export class Component implements OnInit {
 @ViewChild('elref', ) elref: ElementRef;
    constructor(private renderer: Renderer2) {}
     ngOnInit() { this.hotlistening(); }
      hotlistening() {
        this.renderer.listen('window', 'click', (e: Event) => {
                if (this.elref !== undefined) { //Here i checked for undefined
                    this.elref.nativeElement.classList.remove('open');
                });
        }
    }

@ViewChild('test', {static: false}) 测试:ElementRef;

I had the error, but what was causing was more hidden. Simply My View Child component had an error on template, where a variable was undefined. I only perceived it because I revised the HTML. It was hard to notice the error, once no error message was triggered.

(I know this question is a bit old. but for those who the guys still find the hint like me)

I had faced this problem also and I found that {static: true} option might not work properly always when your element is in the parent that has *ngIf .

The workaround to me is that making that element to sub-component and using it on its own ngOnInit or ngAfterViewInit. In this way, I don't have to check whether it really exists, subscribe changes, or something complicated extra work.

maybe you could consider it too. I think this is the simplest solution if it's possible.

for child to be available in ngOnInit()

@ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;

for child to be available in ngAfterViewInit()

@ViewChild(CreateQuestionnaireComponent,{ read: true, static: false }) private childQuestionnaireDialog:CreateQuestionnaireComponent;

but please make sure your selector of child component <app-questionnaire> </app-questionnaire> in html of parent should not embedded in any condition otherwise it will not follow the above pattern, it will required the condition to be true first then.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM