我应该如何在 Angular 8 中为 @ViewChild 使用新的静态选项?

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

我应该如何配置新的 Angular 8 view child?

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

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

哪个更好?我什么时候应该使用 static:truestatic:false

在大多数情况下,您会希望使用 {static: false}。像这样设置将确保找到依赖于绑定解析(如结构指令*ngIf, etc...)的查询匹配。

何时使用 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;
}

static: false 将成为 Angular 9 中的默认回退行为。阅读更多 here and here

引入了 { static: true } 选项以支持动态创建嵌入式视图。当您动态创建视图并想要访问 TemplateRef 时,您将无法在 ngAfterViewInit 中这样做,因为它会导致 ExpressionHasChangedAfterChecked 错误。将静态标志设置为 true 将在 ngOnInit 中创建您的视图。

尽管如此:

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

请注意 { static: false } 选项将在 Angular 9 中成为默认选项。这意味着不再需要设置静态标志,除非您想使用 static: true选项。

您可以使用 angular cli ng update 命令自动升级您当前的代码库。

有关迁移指南和更多信息,您可以查看 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.


使用 static: true 的一个很好的 use-case 是,如果您使用 fromEvent 绑定到模板中定义的元素。考虑以下模板:

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

然后您可以处理此元素上的事件,而无需使用订阅或初始化挂钩(如果您不想或不能使用 angular 事件绑定):

@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
  ));
}

使用defer很重要。这将确保仅在订阅时才解析可观察对象。这将在 ngAfterViewInit 被触发之前发生,当 async 管道订阅它时。因为我们正在使用 static: true,所以 this.thumb 已经填充。

来自angulardocs

static - whether or not to resolve query results before change detection runs (i.e. return static results only). If this option is not provided, the compiler will fall back to its default behavior, which is to use query results to determine the timing of query resolution. If any query results are inside a nested view (e.g. *ngIf), the query will be resolved after change detection runs. Otherwise, it will be resolved before change detection runs.

如果 child 不依赖于任何条件,使用 static:true 可能是更好的主意。如果元素的可见性发生变化,那么 static:false 可能会提供更好的结果。

PS:由于这是一项新功能,我们可能需要 运行 性能基准。

编辑

如@Massimiliano Sartoretto 所述,github commit 可能会给您更多见解。

来到这里是因为升级到 Angular 8 后,在 ngOnInit 中 ViewChild 为 null。

在 ngOnInit 之前填充静态查询,而在 ngOnInit 之后填充动态查询 (static: false)。换句话说,如果设置 static: false 后 ngOnInit 中的 viewchild 现在为 null,则应考虑更改为 static: true 或将代码移至 ngAfterViewInit。

https://github.com/angular/angular/blob/master/packages/core/src/view/view.ts#L332-L336

其他答案是正确的,并解释了为什么会这样:查询依赖于结构指令,例如ngIf 中的 ViewChild 引用,应该 运行 在此指令的条件已解决之后,即在更改检测之后。但是,可以安全地使用 static: true 并因此在 ngOnInit 之前解决未嵌套引用的查询。恕我直言,这个特殊情况值得一提,因为空异常可能是您遇到这种特殊性的第一种方式,就像我一样。

根据经验,您可以执行以下操作:

  • { static: true }需要访问ngOnInit中的ViewChild

  • { static: false } 只能在 ngAfterViewInit 中访问。当您在模板中的元素上有结构指令(即 *ngIf)时,这也是您想要的。

查看 child @angular 5+ token 两个参数 ('local reference name',static: false|true)

@ViewChild('nameInput', { static: false }) nameInputRef: ElementRef;

要知道真假之间的区别检查这个

static - whether or not to resolve query results before change detection runs (i.e. return static results only). If this option is not provided, the compiler will fall back to its default behavior, which is to use query results to determine the timing of query resolution. If any query results are inside a nested view (e.g. *ngIf), the query will be resolved after change detection runs. Otherwise, it will be resolved before change detection runs.

在ng8中,可以手动设置何时在父组件中访问子组件。 当你设置 static 为 true 时,意味着父组件只获取组件在 onInit 钩子中的定义: 例如:

 // You got a childComponent which has a ngIf/for tag
ngOnInit(){
  console.log(this.childComponent);
}

ngAfterViewInit(){
  console.log(this.childComponent);
}

如果 static 为 false ,那么您只能在 ngAfterViewInit() 中获得定义,在 ngOnInit() 中,您将获得未定义。

查看child

... 可用于模板元素引用

...在特定组件参考之外。

使用装饰器风格语法.. @ViewChild( selector) reference : ElementRef || QueryList.

特定组件或元素参考。

AfterViewInIt()中使用它。

我们可以在 Oninit() 中使用它。

不过这个是为了具体使用ngAfterViewInit()

最后 {static : false} 应放在 @ViewChild( Useme , { static : false})... 中以供模板变量引用。

模板文件中的变量看起来像。 #Useme.