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

Posted

技术标签:

【中文标题】我应该如何在 Angular 8 中为 @ViewChild 使用新的静态选项?【英文标题】:How should I use the new static option for @ViewChild in Angular 8? 【发布时间】:2019-10-15 00:04:38 【问题描述】:

我应该如何配置新的 Angular 8 子视图?

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

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

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

【问题讨论】:

【参考方案1】:

在大多数情况下,您会希望使用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 和 here

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

尽管如此:

在大多数其他情况下,最佳做法是使用static: false

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

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

有关迁移指南以及更多相关信息,您可以查看here 和here

#静态查询和动态查询有什么区别? @ViewChild() 和 @ContentChild() 查询的静态选项决定查询结果何时可用。

对于静态查询 (static: true),查询会在视图创建后但在更改检测运行之前解析。但是,结果将永远不会更新以反映对视图的更改,例如对 ngIf 和 ngFor 块的更改。

对于动态查询(静态:false),查询分别在 @ViewChild() 和 @ContentChild() 的 ngAfterViewInit() 或 ngAfterContentInit() 之后解析。结果将根据视图的更改进行更新,例如对 ngIf 和 ngFor 块的更改。


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

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

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

@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 很重要。这将确保 observable 仅在订阅时才被解析。这将在ngAfterViewInit 被触发之前发生,此时async 管道订阅它。因为我们使用的是static: true,所以this.thumb 已经被填充了。

【讨论】:

请更新 Angular 文档的链接(发布后更改)angular.io/api/core/ViewChild#description 我无法访问 childView 的实例。它一直说未定义。 您能否提供一个关于删除 Angular 9 中的静态选项的信息的链接? @AlexMarinov 我已经更新了我的答案,以便更清楚地了解 Angular 9 中会发生什么。有关此问题的链接在迁移指南中 @MinhNghĩa 如果您将整个组件嵌套在组件模板之外,您可以使用 static: true ,但如果没有直接需要访问ngOnInit 内的 ViewChild,您应该只使用 static: false 【参考方案2】:

从角度docs

静态 - 是否在更改检测运行之前解析查询结果(即仅返回静态结果)。如果未提供此选项,编译器将退回到其默认行为,即使用查询结果来确定查询解析的时间。如果任何查询结果在嵌套视图中(例如 *ngIf),则查询将在更改检测运行后解析。否则,它将在更改检测运行之前解决。

如果孩子不依赖任何条件,最好使用static:true。如果元素的可见性发生变化,那么static:false 可能会给出更好的结果。

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

编辑

正如@Massimiliano Sartoretto 所提到的,github commit 可能会给你更多的见解。

【讨论】:

我将添加此功能背后的官方动机github.com/angular/angular/pull/28810【参考方案3】:

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

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

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

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

【讨论】:

【参考方案4】:

因此,根据经验,您可以选择以下方法:

要访问ngOnInit中的ViewChild,需要设置 static: true

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

【讨论】:

注意:在 Angular 9 中,静态标志默认为 false,因此“可以安全地删除任何 static: false 标志”。文档:angular.io/guide/static-query-migration【参考方案5】:

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

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

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

static - 是否在更改检测运行之前解析查询结果(即仅返回静态结果)。如果未提供此选项,编译器将退回到其默认行为,即使用查询结果来确定查询解析的时间。如果任何查询结果在嵌套视图中(例如 *ngIf),则查询将在更改检测运行后解析。否则,它将在更改检测运行之前解决。

【讨论】:

【参考方案6】:

在 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() 中,你会得到 undefined。

【讨论】:

【参考方案7】:

查看孩子

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

...对于特定组件引用之外的内容。

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

对特定组件或元素的引用。

AfterViewInIt()中使用它。

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

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

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

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

【讨论】:

以上是关于我应该如何在 Angular 8 中为 @ViewChild 使用新的静态选项?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 中为 *ngFor 设置动画?

为在开放层中为 angular 8 创建的图像添加边框

如何在 Storybook 中为 Angular 启用样式源映射

我如何在打字稿中为 Angular 5 设置 zindex

如何在 Angular 中为不同的环境设置配置或属性文件?

如何在 Angular 4 中为表格添加分页?