Keywords: Angular | ViewChild | Static Queries
Abstract: This article provides a comprehensive examination of the new static option in Angular 8's @ViewChild decorator. Through comparative analysis of static:true and static:false usage scenarios, combined with practical code examples, it explores the core differences between static and dynamic queries. The paper delves into query behavior under structural directives like ngIf, examines access timing in ngOnInit and ngAfterViewInit lifecycle hooks, and offers migration guidance from Angular 7 to Angular 8.
Introduction
With the release of Angular 8, the @ViewChild decorator introduced a significant new option—the static flag. This change not only affects developers' coding practices but also impacts application performance and stability. This article provides an in-depth analysis of how the static option works and demonstrates through practical examples how to make the right choice in different scenarios.
Core Differences Between Static and Dynamic Queries
In the Angular framework, the static option of @ViewChild queries determines when query results become available and whether they respond to view changes. When configured with {static: true}, the query resolves after view creation but before change detection runs. The results of such static queries are fixed and won't update with subsequent view changes.
In contrast, dynamic queries with {static: false} resolve after the ngAfterViewInit lifecycle hook. These queries can capture subsequent view changes, particularly when using structural directives like *ngIf or *ngFor, where query results update as conditions change.
Typical Use Cases for static:false
In most scenarios, using {static: false} configuration is recommended. This setting is particularly suitable for query matches that depend on binding resolution, especially when templates contain structural directives.
Consider the following example scenario:
@Component({
template: `
<div *ngIf="showMe" #viewMe>This content may show or hide</div>
<button (click)="showMe = !showMe">Toggle Display</button>
`
})
export class ExampleComponent {
@ViewChild('viewMe', { static: false })
viewMe?: ElementRef<HTMLElement>;
showMe = false;
}In this example, due to the presence of the *ngIf directive, the viewMe element might not immediately appear in the DOM. Using static: false ensures that the query only returns valid results when the element actually exists. If static: true were used, during component initialization, with showMe initially false, the query would return undefined, and even if the user later clicks the button to make the element visible, the query results wouldn't update.
Special Use Cases for static:true
Although static: false is the default recommended choice, static: true has irreplaceable value in certain specific scenarios. One typical application is when accessing TemplateRef while dynamically creating embedded views.
Another important use case involves event handling. Consider this implementation:
@Component({})
export class ThumbComponent {
@ViewChild('thumb', { static: true })
thumb?: ElementRef<HTMLElement>;
readonly thumbStyle$ = defer(() => fromEvent(this.thumb, 'pointerdown').pipe(
switchMap((startEvent) => fromEvent(document, 'pointermove', { passive: true })
// Subsequent position transformation logic
));
}In this example, using the defer operator ensures the Observable only resolves when subscribed. Because static: true is used, when the async pipe subscribes to the Observable (which happens before ngAfterViewInit triggers), this.thumb is already properly initialized. If static: false were used, the element reference might not be ready when subscription occurs.
Access Timing in Lifecycle Hooks
The choice of static option directly determines in which lifecycle hook query results can be safely accessed:
- With
{static: true}, query results can be accessed inngOnInit - With
{static: false}, query results can only be accessed inngAfterViewInitand subsequent lifecycle hooks
This timing difference has significant implications for application initialization logic design. When view child elements need to be accessed in ngOnInit, static: true must be used, but with the risk that query results won't update.
Version Compatibility and Migration Strategies
When upgrading from Angular 7 to Angular 8, the introduction of the static option indeed presented some compatibility challenges. In Angular 7, this option didn't exist, and direct upgrades would cause TypeScript compilation errors.
The Angular team recognized this issue and made {static: false} the default behavior in Angular 9, meaning that in most cases, explicitly setting the static option is no longer necessary. For projects migrating from older versions, the Angular CLI's ng update command can automatically handle these changes.
For library developers, during the transition period, some workarounds might be necessary, such as type assertions, but the best approach is to adapt to the new version requirements as soon as possible.
Best Practices Summary
Based on the above analysis, the following best practice guidelines can be derived:
- Prefer
{static: false}in most cases, especially when templates contain structural directives - Use
{static: true}only when query results need to be accessed inngOnInitand you're certain elements won't dynamically show/hide due to conditional changes - For scenarios like event handling that require early access to elements during component initialization,
static: truecombined with thedeferoperator provides an effective solution - Stay updated with Angular version changes, understand default behavior modifications, and adjust code structure promptly
By appropriately choosing the static option, developers can ensure application stability across various scenarios while maintaining code performance. This granular control reflects the careful balance the Angular framework maintains between usability and flexibility.