Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/31.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 使用*ngFor和ContentChildren时,获取了ExpressionChangedTerithasBeenCheckedError_Javascript_Angular_Exception - Fatal编程技术网

Javascript 使用*ngFor和ContentChildren时,获取了ExpressionChangedTerithasBeenCheckedError

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

我有选项卡和选项卡组件:

tabs.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:['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,我明白了…复杂的功能,不幸的是没有时间详细研究它