Javascript 使用*ngFor和ContentChildren时,获取了ExpressionChangedTerithasBeenCheckedError
我有选项卡和选项卡组件: tabs.component.ts:Javascript 使用*ngFor和ContentChildren时,获取了ExpressionChangedTerithasBeenCheckedError,javascript,angular,exception,Javascript,Angular,Exception,我有选项卡和选项卡组件: tabs.component.ts: @Component({ selector: 'cl-tabs', template: `<ng-content></ng-content>`, styleUrls: ['tabs.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class TabsComponent im
@Component({
selector: 'cl-tabs',
template: `<ng-content></ng-content>`,
styleUrls: ['tabs.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TabsComponent implements AfterContentInit {
@ContentChildren(TabComponent) tabs: QueryList<TabComponent>;
ngAfterContentInit(): void {
if (0 < this.tabs.length) {
this.activate(this.tabs.first);
}
}
activate(activatedTab: TabComponent) {
this.tabs.forEach(tab => tab.active = false);
activatedTab.active = true;
}
}
@Component({
selector: 'cl-tab',
template: `<ng-content></ng-content>`,
styleUrls: ['tab.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TabComponent {
@Input() title: string;
@Input() @HostBinding('class.is-active') active: boolean = false;
}
@组件({
选择器:“cl选项卡”,
模板:``,
样式URL:['tabs.component.scss'],
changeDetection:ChangeDetectionStrategy.OnPush,
})
导出类TabsComponent实现AfterContentInit{
@ContentChildren(TabComponent)选项卡:QueryList;
ngAfterContentInit():void{
if(0<此制表符长度){
this.activate(this.tabs.first);
}
}
激活(激活选项卡:选项卡组件){
this.tabs.forEach(tab=>tab.active=false);
activatedTab.active=true;
}
}
tab.component.ts:
@Component({
selector: 'cl-tabs',
template: `<ng-content></ng-content>`,
styleUrls: ['tabs.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TabsComponent implements AfterContentInit {
@ContentChildren(TabComponent) tabs: QueryList<TabComponent>;
ngAfterContentInit(): void {
if (0 < this.tabs.length) {
this.activate(this.tabs.first);
}
}
activate(activatedTab: TabComponent) {
this.tabs.forEach(tab => tab.active = false);
activatedTab.active = true;
}
}
@Component({
selector: 'cl-tab',
template: `<ng-content></ng-content>`,
styleUrls: ['tab.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TabComponent {
@Input() title: string;
@Input() @HostBinding('class.is-active') active: boolean = false;
}
@组件({
选择器:“cl选项卡”,
模板:``,
样式URL:['tab.component.scss'],
changeDetection:ChangeDetectionStrategy.OnPush,
})
导出类选项卡组件{
@输入()标题:字符串;
@Input()@HostBinding('class.is active')active:boolean=false;
}
app.component.html:
<cl-tabs>
<cl-tab *ngFor="let item of items" [title]="item.name">
<any-component [item]="item"></any-component>
</cl-tab>
</tabs>
问题是,在angulars changedetection检查值之后,您正在更改值。问题是:
ngAfterContentInit(): void {
if (0 < this.tabs.length) {
this.activate(this.tabs.first);
}
}
ngAfterContentInit():void{
if(0<此制表符长度){
this.activate(this.tabs.first);
}
}
setTimeout
将是最好的解决方案,因为它创建了一个宏任务。另一种解决方案是再次触发changedetection,但这将为整个应用程序调用它。要理解答案,您必须先阅读并理解以下两篇文章:
让我们看一下App
组件的以下模板:
<cl-tabs>
<cl-tab *ngFor="let item of items" [title]="item.name"></cl-tab>
</cl-tabs>
可以对AppComponent视图的以下结构进行成像:
AppView.nodes: [
clTabsComponentView
ngForEmbeddedViews: [
ngForEmbeddedView<{name: "1", value: 1}>
ngForEmbeddedView<{name: "2", value: 2}>
ngForEmbeddedView<{name: "3", value: 3}>
AppView.nodes:[
clTabsComponentView
NGD视图:[
NgBeddedView
NgBeddedView
NgBeddedView
当AppView触发更改检测时,操作顺序如下:
1) 检查clTabsComponentView
2) 检查NGForembeddedView中的所有视图。
在这里,每个视图都会记住值active=false
3) 调用ngAfterContentInit
生命周期挂钩。
这里更新active=true
。因此,在验证阶段,Angular将检测差异并报告错误
为什么在前两种情况下错误会消失?
现在确定您静态地是什么意思。如果您的意思是不使用*ngFor
,那么就不会有错误,因为每个选项卡ComponentView都将是子视图,而不是嵌入式视图。子视图将在ngAfterContentChecked
生命周期挂钩之后处理
如果删除@HostBinding
,则Angular不需要在组件中进行检查,并且活动的
值不会被记住-因此不会进行验证和检查
正如在许多情况下建议的那样,cdRef.detectChanges()没有帮助
您需要调用AppComponent上的更改检测才能正常工作。谢谢您的回答!我仍然不明白为什么在前两种情况下,在使用静态选项卡或没有主机绑定的情况下,错误会消失。您对此有什么线索吗?至于更改检测,您的意思是运行cdRef.detectChanges()嘿,错误的确切措辞是什么?这是一个相当复杂的问题setup@Maximus错误:ExpressionChangedTerithasBeenCheckedError:表达式在检查后已更改。以前的值:“false”。当前值:“true”。我无法重现该问题。安装非常复杂。如果您设置具有可重现问题的plunker我要买一个look@Maximus更新的问题,带有指向plunker的错误链接。选项卡是静态创建的-静态是什么意思?感谢您的精彩解释!因此,最后,我想,在我的情况下,没有比超时更好的解决方法了?或者可能我的方法在架构上有问题?告诉我您的想法该怎么办?我想做一些基本的标签功能。主要是基于这些图特:,@VladislavNikolayev,我明白了…复杂的功能,不幸的是没有时间详细研究它