Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 804
Patrik Laszlo Avatar asked Oct 10 '22 11:10

Patrik Laszlo


People also ask

What is the use of static in ViewChild?

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. Instead now when we run our code, our ViewChild is available earlier.

When you want to access the ViewChild in ngOnInit you need to use @ViewChild Option State True or false?

Use { static: true } when you want to access the ViewChild in ngOnInit . Use { static: false } will be accessible only in ngAfterViewInit .

What is the difference between ViewChild and ViewChildren?

The ViewChild or ViewChildren decorators are used to Query and get the reference of the DOM element in the Component. ViewChild returns the first matching element and ViewChildren returns all the matching elements as a QueryList of items. We can use these references to manipulate element properties in the component.

What is ViewChild and ContentChild?

ViewChild is used to select an element from component's template while ContentChild is used to select projected content.


1 Answers

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.

like image 359
Poul Kruijt Avatar answered Oct 13 '22 01:10

Poul Kruijt