A very common question I field from developers new to Angular is what does the “static” property do when using ViewChild. For example, if we have code like so :
@ViewChild(ChildComponent, {static : false})
childComponent : ChildComponent;
What exactly does setting static to false do?
I think the documentation is actually rather light and I’ve seen lots of posts around the subject that explain the end behaviour of setting static to true or false, but not what it’s actually doing. A bit like describing the symptoms rather than what’s actually going on.
So let’s break it down!
What Does Static Actually Do
Static stands for whether the ViewChild is “static” content e.g. Always available on the page, no matter page bindings, API calls, ngIfs etc. When set to true, we are telling Angular that the ViewChild will be available at anytime, so simply query for the ChildComponent at the earliest lifecycle hook available and then never query again.
However, if we set static to false, we are saying that the ViewChild will be available at a later time, but it’s dependant on a condition (Such as an API call, or a simple component property binding), and therefore we must check for the ViewChild every time ChangeDetection runs. Understandably, this can create a higher performance load because we must always be checking if our ChildComponent is available any time the component changes.
Access In Component Lifecycle Hooks
I want to talk a little bit about how the setting of the static property affects how early we can access the ViewChild. This is one of those symptoms I talked about earlier. Setting static to true or false does affect which lifecycle hook you can access the ViewChild, but it’s more a side effect of what static is actually doing rather than the intention of the property.
When static is set to false, it is only available during the ngAfterViewInit lifecycle hook at the very earliest because this is the very first lifecycle hook that runs after ChangeDetection for the component.
In code form, if we have a component like so :
export class AppComponent implements OnInit, AfterViewInit {
@ViewChild(ChildComponent, {static : false}) childComponent : ChildComponent; //Notice the static false
ngOnInit(): void {
console.log(this.childComponent);
}
ngAfterViewInit(): void {
console.log(this.childComponent);
}
}
Notice that static is set to false. And therefore, our ViewChild is not available in ngOnInit. If we run the code, we get this :
undefined
ChildComponent
Changing the code to instead use static true
export class AppComponent implements OnInit, AfterViewInit {
@ViewChild(ChildComponent, {static : true}) childComponent : ChildComponent; //Notice the static false
ngOnInit(): void {
console.log(this.childComponent);
}
ngAfterViewInit(): void {
console.log(this.childComponent);
}
}
Instead now when we run our code, our ViewChild is available earlier.
ChildComponent
ChildComponent
So Should We Always Use Static True Then?
So the question then becomes, if we can get access to our ViewChild earlier, why not just always make it available as soon as possible? There’s actually one big reason, but I’ll give two examples to show how always setting static to true on ViewChild can cause a few headaches.
In this first example, I’m going to add a boolean to our code on whether we should show our ChildComponent or not. The code behind looks like so :
export class AppComponent implements OnInit, AfterViewInit {
shouldShow = true; //This is our new boolean.
@ViewChild(ChildComponent, {static : true}) childComponent : ChildComponent;
ngOnInit(): void {
console.log(this.childComponent);
}
ngAfterViewInit(): void {
console.log(this.childComponent);
}
}
With HTML utilizing a simple ngIf :
<app-child *ngIf="shouldShow"></app-child>
When static is set to true, our output is :
undefined
undefined
So not only is ViewChild not available during ngOnInit, it’s actually never available. And the reason is that when we set static to true, Angular only tries to find our ViewChild *once*, and only once. If our ViewChild is not available in the DOM when Angular checks, it is never queries for again. Even in this case where shouldShow is a constant value, ChangeDetection does not run until later in the component lifecycle – after Angular tries to fetch a ViewChild initially.
However if we change static to false, then instead we get this :
undefined
ChildComponent
Because ngAfterViewInit runs after the first ChangeDetection cycle, our component is available, and because setting static to false means after every ChangeDetection, we re-query, we are able to find our ChildComponent.
How about a second example? Let’s take this code :
export class AppComponent implements OnInit, AfterViewInit {
shouldShow = false; //This is our new boolean.
@ViewChild(ChildComponent, {static : false}) childComponent : ChildComponent;
ngOnInit(): void {
console.log(this.childComponent);
timer(2500).subscribe(x =>
{
this.shouldShow = true;
});
timer(3000).subscribe(x =>
{
console.log(this.childComponent);
})
}
ngAfterViewInit(): void {
console.log(this.childComponent);
}
}
I want to use this example because it shows it’s not about Lifecycle Hooks at all. It’s just that some people boil it down to something like :
- Static = true. Access in ngOnInit
- Static = false. Access in ngAfterViewInit
And the reality is it’s actually got nothing to do with that. It’s about ChangeDetection. In our above example, both the logs from ngOnInit *and* ngAfterViewInit will be false, however when our timer goes off, and we set shouldShow to true, and then 500ms later we check the ChildComponent, it will be available.
Which One To Use?
The rules for which one to use is actually quite simple.
- If your ChildComponent is hidden or dynamically loaded in some way via component binding (Either with property binding or an API call), then you *must* set static to false.
- If your ChildComponent is always available on the page, and is never hidden. Then you can use *either* static false or static true. But setting static to true is more performant, and gives you earlier access to the component (if required).
💬 Leave a comment