August 15, 2019

Angular ViewChild – static property in ng8

Some of you might have stumbled upon an unexpected error message, while updating your project from ng7 to ng8. We all know that. Everything runs smoothly and we use the last 10 minutes before heading off for the update. Not a major issue. Already having packed your stuff and ready to head home, you get this message.

What went wrong? Lets have a look, as knowing this might save you a little time and surprises.

Quick Fix
To solve the problem fast without much reading, you just have to add a static property {static: false} or {static: true} into every @ViewChild/@ContentChild decorator. {static: true} forces the initialization of the component/directive already in ngOnInit, {static: false} changes the timing to ngAfterViewInit for @ViewChild and ngAfterContentInit for @ContentChild.

Whats the Problem?

So our problem is the @ViewChild or the @ContentChild property decorator. In short these decorators make it possible to include any components or directives, which are a direct child to the component. With @ViewChild you include elements you added to the component yourself. Contrary to that @ContentChild includes a reference to content that has been placed in a component with the <ng-content>-tag.

To learn in depth what they do and how to use them you can check it out here. Also click here for a detailed discussion regarding the differences of @ViewChild and @ContentChild.

Lets have a look at an example we used earlier in this blogpost about Content Projection in Angular. The following code shows the old version in Angular 7, where we included three directives with the selectors appCardBody, appCardFooter and appCardHeader.

To show both decorators we also included In the Card Component as @ViewChild in app.component.ts as followed:

So the application is running great and we decide to update from Angular 7 to Angular 8. After the update is finished we see, that the @ViewChild and @ContentChild decorators complain about a shortage of  arguments. Why is that? Whats happenig here?

@ViewChild in app.component.ts:

@ContentChild in card.component.ts:

The solution comes with the timing of the component initialization. Lets dig into this and find out.

Adding the static property.

The reason for this error is fast and simple to detect, if you know what has changed in Angular 8. The missing argument is the newly introduced static-property. It is a boolean, that determines the timing of component initialization.

<=Angular 7:

As you may know (See: On Init Life Cycle Hook) in most cases the @ViewChild/@ContentChild gets initialized after the main component initialization. So it is more safe to put the component initialization code that uses the references in the ngAfterViewInit Lyfecycle Method (for @ContentChild -> ngAfterContentInit).

So without the static argument, the decision when to resolve the query was made by the compiler. Each case was examined an decided individually. The @ViewChild/@ContentChild queries got bundled in two categories. Static and dynamic. Following this categorization, the querie results were made available at different times:

  • Static queries: Result of the query is determined statically and does not depend on runtime values. Query results of this kind are available before Change Detection and are accessible in ngOnInit.
  • Dynamic queries: The results of dynamicly labelled query results depend on runtime values like bindings. So they are not available after change detection. So they are accessible in ngAfterViewInit or ngAfterContentInit.

>=Angular 8

Now it is possible (and necessary) to define the timing of initialization proactively. Here it is, where the static property gets relevant. If you set static false, the component ALWAYS gets initialized after the view initialization in time for the ngAfterViewInit/ngAfterContentInitcallback functions. This is the matching with the standard case of the old configuration, except that it is now forced. If static is set true, the initialization will take place at the view initialization (ngOnInitfor @ViewChild and @ContentChild).

Important: this new flag only applies to @ViewChild and @ContentChild detectors. @ViewChildren and @ContentChildren don’t work with the concept of static and dynamic. In this case the query is always dynamic.

So now lets try it! We work with both options. As you can see in the following code snippets we add the argument {static: false} into the decorators. Additionally we include some console logs, to showcase the behaviour for the different options.

If you now run the code with both options: {static: false} and {static: true} the console will show you results shown in the following matrix. With {static: true} the components are already initialized at ngOnInit. In the case of {static: false} we see that the initialization didn’t happen until the ngAfterViewInit/ngAfterContentInit event.

What is the best Strategy?

You now wonder what you are supposed to do with this new superpower? Here are some ideas:

  1. If you are used to work with ngAfterViewInit/ ngAfterContentInitanyways, you will be happy with { static: false }.
  2. The feature was included to be able to embed views on the go. In case you need to access TemplateRef in a query for dynamic view creation, you can’t do this in ngAfterViewInit. You will get an ExpressionHasChangedAfterChecked error, as change detection has already run. This is a case where you should set {static: true} and create the view in ngOnInit.
  3. If you are a library author, you should get familiar with the inner workings of the static property. The early adoption of the migration will facilitate your users to work with the Ivy Renderer in Angular 9 (aproximatly coming beginning Q4 2019).
  4. In Angular version 9 and later, it will be safe to remove any {static: false} flags and we will do this cleanup for you in a schematic.
  5. We found a nice quote of Destreyf in the Github forum to make the decision simpler:
    “more specifically if the element needs to be available during ngOnInit, then static needs to be true, but if it can wait until after the init it can be false, which means it won’t be available until ngAfterViewInit.”

As often in life it depends on what you want to achieve.

Related Posts

Janik Horst
Developer at thecodecampus </>


Leave a Reply

Add code to your comment in Markdown syntax.
Like this:
`inline example`

```
code block
example
```

Your email address will not be published.