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

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

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

vs

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

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

172018 次浏览

在大多数情况下,您将使用{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中的默认回退行为。阅读更多在这里在这里

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

然而:

在大多数其他情况下,最佳实践是使用{static: false}

不过要注意的是,{ static: false }选项在Angular 9中会被设置为默认值。这意味着不再需要设置静态标志,除非你想使用static: true选项。

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

有关迁移指南和更多信息,可以查看在这里在这里

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

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

对于动态查询(静态:false),查询分别在@ViewChild()和@ContentChild()的ngAfterViewInit()或ngAfterContentInit()之后解析。当你的视图发生变化时,结果会被更新,比如ngIf和ngFor块的变化。


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

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

然后你可以处理这个元素上的事件,而不需要使用订阅或init钩子(如果你不想或不能使用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已经被填充。

从angular 文档

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

如果子对象不依赖于任何条件,那么使用static:true可能是一个更好的主意。如果元素的可见性发生变化,则static:false可能会给出更好的结果。

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

编辑

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

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

静态查询在ngOnInit之前填充,而动态查询(Static: false)在ngOnInit之后填充。换句话说,如果在你设置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之前解析非嵌套引用的查询。恕我直言,这种特殊情况值得提及,因为空异常可能是您遇到这种特殊性的第一种方式,就像对我来说一样。

所以根据经验,你可以选择以下方法:

  • 当你想访问ngOnInit中的ViewChild时,需要设置{ static: true }

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

查看子@angular 5+令牌的两个参数('本地引用名',static: false|true)

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

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

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

在ng8中,你可以手动设置何时访问父组件中的子组件。 当你将static设置为true时,这意味着父组件只能在onInit钩子中获得该组件的定义: 如:< / p >
 // 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。

视图的孩子

... 可以使用它作为模板元素参考。

...用于特定组件的外部引用。

With decorator style syntax.. @ViewChild( selector) reference : ElementRef || QueryList. < / p >

特定组件或元素引用的。

AfterViewInIt()中使用。

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

但这对于具体使用ngAfterViewInit()

最后{static : false}应该放在@ViewChild( Useme , { static : false})…模板变量参考。

模板文件中的变量如下所示。 #Useme . < / p >

Static属性通知angular子节点的可用性

例如:如果static被设置为真正的,我们从一开始就通知angular我们的子元素在页面上是可用的(这意味着它不依赖于*ngIf、页面绑定、API调用等),所以angular会在最早的生命周期钩子(ngOnInit)中寻找它,并且永远不会再寻找它

如果我们将static设置为,我们就在告诉angular,我们的子函数依赖于一些条件指令,所以angular会在每个变化检测周期后寻找我们的子函数,如果它可用,我们就可以在ngAfterViewInit()生命周期钩子中访问它