6

After upgrading to Angular 9, basically all my @ViewChild references are not initialized anymore.

No matter what,

<app-menu-editor #menuEditor>
</app-menu-editor>

<div #cardBody>
  <!-- ... -->
</div>
@ViewChild('menuEditor', {read: MenuEditorComponent}) menuEditor: MenuEditorComponent;

@ViewChild('cardBody', {read: ElementRef}) cardBody: ElementRef;

I keep getting exceptions telling me that e.g. menuEditor is undefined.

Any idea why this is not working anymore?

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378

4 Answers4

15

Try

@ViewChild('menuEditor', {static: true, read: MenuEditorComponent}) menuEditor: MenuEditorComponent;

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

Like PierreDuc said, maybe you're referring to them in the ngOnInit.

AliF50
  • 16,947
  • 1
  • 21
  • 37
  • 2
    *Why* do they keep changing this thing? Everytime I upgrade I have to do this differently or change something to get rid of warnings.. Thanks, however. This was indeed the problem. – Stefan Falk Feb 14 '20 at 17:01
  • It was an experimental change in v8, more towards letting everyone know what it meant and it was clearly mentioned that v9 won't have these, so if you need it onInit, you will have to `{static: true,` – nircraft Feb 14 '20 at 17:34
  • 2
    @displayname if you want to upgrade your angular version, you should use the cli. This will handle all these things automatically, or give you a warning saying that you should check these places. – Poul Kruijt Feb 14 '20 at 19:13
  • 2 days before I update angular with the latest version and already using @ViewChild() but var value is undefined. E.g. @ViewChild(childComponent,{static: true})_childCmpt: childComponent; And when I print _childCmpt on button click then getting undefined. I am using packages - "@angular/animations": "~11.2.6", "@angular/cdk": "^11.2.7", "@angular/common": "~11.2.6", "@angular/compiler": "~11.2.6", "@angular/core": "^11.2.8", "@angular/forms": "~11.2.6", – suryadev Apr 03 '21 at 14:24
  • Also, I agree with Stefan Falk, When i update angular version then some feature not working. Need to modify something everytime. – suryadev Apr 03 '21 at 14:29
8

I had another scenario with angular 9 that took me two days to figure out.

My problem was that none of the solutions worked; in fact, neither ViewChild nor ViewChildren worked in ANGULAR 9.

The issue, after A LOT of investigation and NO WARNS, was that my @ViewChild was on an ABSTRACT CLASS that WASN'T marked as a @Component.

So, if you're doing something like that:

export abstract class AbstractBaseComponent {
   @ViewChild('test') public testRef: TestComponent;
}

@Component({
   'selector': 'base-component'
})
export class BaseComponent extends AbstractBaseComponent {
    ngAfterViewInit() {
        console.log(this.testRef);
    }
}

THIS WON'T WORK. you must add @Component on the abstract class as well:

@Component({})
export abstract class AbstractBaseComponent {
   @ViewChild('test') public testRef: TestComponent;
}

Further reading about that can be (indirectly) found in the breaking changes in angular 9:

Although it's not mentioned that abstract classes follows the same behavior, it is actually intended to be as it is, since abstract classes are technically not transpiled.

briosheje
  • 7,356
  • 2
  • 32
  • 54
5

I am guessing that you are getting undefined either within the ngOnInit hook or inside of the class constructor. If my guess is correct then here's your problem:

A view child becomes available within ngOnInit if you use the static: true option inside the @ViewChild decorator. If static: true option is not set, then your view child only becomes available in ngAfterViewInit. The static options is set to false by default.

This SO answer may also be of help.

Sam Herrmann
  • 6,293
  • 4
  • 31
  • 50
0

For Angular 8 and above, you can go for the following:

 @ViewChild('txtName', {static: false}) txtName: ElementRef;
    
    { static: true } needs to be set when you want to access the ViewChild in ngOnInit.
        
    { static: false } can only be accessed in ngAfterViewInit. This is also what you want to go for when you have a structural directive (i.e. *ngIf) on your element in your template.
Bhadresh Patel
  • 1,671
  • 17
  • 18