πŸš€ OharaLumina

How should I use the new static option for ViewChild in Angular 8

How should I use the new static option for ViewChild in Angular 8

πŸ“… | πŸ“‚ Category: Programming

Angular 8 introduced a significant change to how developers interact with component views through the @ViewChild and @ViewChildren decorators. The introduction of the static property provides more control over when a view query is resolved, addressing common issues related to timing and change detection. Understanding this new feature is essential for any Angular developer aiming to build robust and efficient applications. This article will delve into the nuances of the static option, explaining when and why you should use it, and demonstrate practical examples to solidify your understanding.

Understanding the static Property

Prior to Angular 8, the resolution of @ViewChild queries could be unpredictable, often leading to race conditions and unexpected behavior. This was particularly true when dealing with dynamically rendered content or components projected using ng-content. The static property addresses this by offering explicit control over the timing of query resolution. Setting static: true resolves the query after change detection has completed for the current component’s view, guaranteeing access to the queried elements. Conversely, setting static: false (the default in Angular 9 and later) resolves the query after the change detection cycle of the parent component completes, allowing for access to elements within projected content.

Choosing the correct value depends entirely on your use case. If you’re querying elements within your component’s template directly, static: true is often sufficient. However, if you’re interacting with elements projected from a parent component, static: false is necessary to ensure they’re available.

Using static: true for Local Elements

When you need to access a child component or element within your component’s template, the static: true option simplifies the process. This is particularly useful when you need to interact with the element after initialization, for example, calling a method on a child component or manipulating its properties.

Example:

@ViewChild('myChild', { static: true }) myChildComponent: MyChildComponent; ngAfterViewInit() { this.myChildComponent.someMethod(); }In this example, myChildComponent is guaranteed to be available in ngAfterViewInit.

Using static: false for Projected Content

The static: false setting is crucial when dealing with content projected via ng-content. Because projected content is rendered by the parent component, it’s not available until after the parent’s change detection cycle completes.

Example:

@ViewChild('projectedElement', { static: false }) projectedElement: ElementRef; ngAfterViewInit() { console.log(this.projectedElement); // Now available }Best Practices and Common Pitfalls

Choosing the right static value is key to avoiding runtime errors and unexpected behavior. A common mistake is using static: true when interacting with projected content, leading to null or undefined errors. Carefully consider the origin of the element you’re querying.

  • Always analyze whether you are querying locally or from projected content.
  • Use static: true for elements within your component’s template.
  • Use static: false for projected content or when the element’s existence is conditional.

Here’s a helpful table summarizing the key differences:

  1. static: true: Resolves query after component view initialization.
  2. static: false: Resolves query after parent component change detection.

Infographic Placeholder: (Visual representation of static: true vs. static: false resolution timing)

Learn more about Angular component interaction.Advanced Use Cases and Alternatives

While @ViewChild with the static flag covers many scenarios, other approaches might be more suitable for complex situations. For instance, when dealing with multiple dynamic components or elements, @ViewChildren combined with an observable provides more flexibility. This allows you to react to changes in the queried elements over time.

Another alternative is to leverage the ContentChild decorator which is specifically designed for querying projected content. This can simplify your logic and improve readability, especially when dealing with nested component structures.

External Resources

FAQ

Q: What happens if I use static: true with dynamically created elements?

A: If the element doesn’t exist during the initial view initialization, using static: true will result in the query returning undefined. You’ll need to use static: false or a different approach to handle dynamic content.

The static option in Angular’s @ViewChild decorator provides crucial control over query timing. By understanding the distinction between static: true and static: false, you can avoid common pitfalls and build more robust Angular applications. Remember to choose the appropriate setting based on whether you are querying elements within your component’s template or projected from a parent component. Explore the provided resources and experiment with different approaches to truly master this powerful feature and optimize your Angular development workflow. Start building more efficient and predictable Angular applications today by leveraging the full potential of @ViewChild.

Question & Answer :
How should I configure the new Angular 8 view child?

@ViewChild('searchText', {read: ElementRef, static: false}) public searchTextInput: ElementRef; 

vs

@ViewChild('searchText', {read: ElementRef, static: true}) public searchTextInput: ElementRef; 

Which is better? When should I use static:true vs static:false?

In most cases you will want to use {static: false}. Setting it like this will ensure query matches that are dependent on binding resolution (like structural directives *ngIf, etc...) will be found.

Example of when to use static: false:

@Component({ template: ` <div *ngIf="showMe" #viewMe>Am I here?</div> <button (click)="showMe = !showMe"></button> ` }) export class ExampleComponent { @ViewChild('viewMe', { static: false }) viewMe?: ElementRef<HTMLElement>; showMe = false; } 

The static: false is going to be the default fallback behaviour in Angular 9. Read more here and here

The { static: true } option was introduced to support creating embedded views on the fly. When you are creating a view dynamically and want to acces the TemplateRef, you won’t be able to do so in ngAfterViewInit as it will cause a ExpressionHasChangedAfterChecked error. Setting the static flag to true will create your view in ngOnInit.

Nevertheless:

In most other cases, the best practice is to use {static: false}.

Be aware though that the { static: false } option will be made default in Angular 9. Which means that setting the static flag is no longer necessary, unless you want to use the static: true option.

You can use the angular cli ng update command to automatically upgrade your current code base.

For a migration guide and even more information about this, you can check here and here

#What is the difference between static and dynamic queries? The static option for @ViewChild() and @ContentChild() queries determines when the query results become available.

With static queries (static: true), the query resolves once the view has been created, but before change detection runs. The result, though, will never be updated to reflect changes to your view, such as changes to ngIf and ngFor blocks.

With dynamic queries (static: false), the query resolves after either ngAfterViewInit() or ngAfterContentInit() for @ViewChild() and @ContentChild() respectively. The result will be updated for changes to your view, such as changes to ngIf and ngFor blocks.


A nice use-case for using static: true, is if you are using fromEvent to bind to an element defined in the template. Consider the following template:

<div [ngStyle]="thumbStyle$ | async" #thumb></div> 

You can then handle events on this element without the need of using subscriptions or init hooks (if you don’t want to or cannot use angular event binding):

@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 }) // transform to proper positioning )); } 

It is important to use defer. This will make sure the observable is only resolved when it’s subscribed to. This will happen before the ngAfterViewInit gets triggered, when the async pipe subscribes to it. Because we are using static: true, the this.thumb is already populated.