Angular ExpressionChangedTerithasBeenCheckedError:表达式在检查后已更改。以前的值:';未定义';

Angular ExpressionChangedTerithasBeenCheckedError:表达式在检查后已更改。以前的值:';未定义';,angular,runtime-error,Angular,Runtime Error,我知道在stack overflow中已经发布了很多相同的问题,并尝试了不同的解决方案来避免运行时错误,但没有一个适合我 组件和Html代码 export class TestComponent implements OnInit, AfterContentChecked { @Input() DataContext: any; @Input() Position: any; sampleViewModel: ISampleViewModel = { DataConte

我知道在stack overflow中已经发布了很多相同的问题,并尝试了不同的解决方案来避免运行时错误,但没有一个适合我

组件和Html代码

export class TestComponent implements OnInit, AfterContentChecked {
    @Input() DataContext: any;
    @Input() Position: any;
    sampleViewModel: ISampleViewModel = { DataContext: : null, Position: null };
    constructor(private validationService: IValidationService, private modalService: NgbModal, private cdRef: ChangeDetectorRef) {
    }

    ngOnInit() {

    }
    ngAfterContentChecked() {

            debugger;
            this.sampleViewModel.DataContext = this.DataContext;
            this.sampleViewModel.Position = this.Position;

    }


<div class="container-fluid sample-wrapper text-center" [ngClass]="sampleViewModel.DataContext?.Style?.CustomCssClass +' samplewidget-'+ sampleViewModel.Position?.Columns + 'x' + sampleViewModel.Position?.Rows">
     //some other html here
</div>
导出类TestComponent实现OnInit,AfterContentChecked{
@Input()DataContext:任意;
@输入()位置:任意;
sampleViewModel:ISampleViewModel={DataContext::null,位置:null};
构造函数(私有验证服务:IValidationService,私有modalService:NgbModal,私有cdRef:ChangeDetectorRef){
}
恩戈尼尼特(){
}
ngAfterContentChecked(){
调试器;
this.sampleViewModel.DataContext=this.DataContext;
this.sampleViewModel.Position=this.Position;
}
//这里还有其他一些html
请注意:此组件是使用DynamicComponentLoader动态加载的

在排除故障后,我发现了几个问题

首先,通过使用DynamicComponentResolver动态加载此子组件,并传递如下所示的输入值

 ngAfterViewInit() {
    this.renderWidgetInsideWidgetContainer();

  }


  renderWidgetInsideWidgetContainer() {
    let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
    let componentFactory = this._componentFactoryResolver.resolveComponentFactory(component);
    let viewContainerRef = this.widgetHost.viewContainerRef;
    viewContainerRef.clear();
    let componentRef = viewContainerRef.createComponent(componentFactory);
    debugger;
    (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext;
    (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position;

  }
ngAfterViewInit(){
此.renderWidgetContainer()为;
}
RenderWidgetWidgetContainer(){
让component=this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
让componentFactory=this.\u componentFactoryResolver.resolveComponentFactory(component);
让viewContainerRef=this.widgetHost.viewContainerRef;
viewContainerRef.clear();
让componentRef=viewContainerRef.createComponent(componentFactory);
调试器;
(componentRef.instance).WidgetDataContext=this.dataSource.DataContext;
(componentRef.instance).WidgetPosition=this.dataSource.Position;
}
即使我像下面这样更改了我的子组件html,我也会遇到同样的错误

<div class="container-fluid ds-iconwidget-wrapper text-center" [ngClass]="Sample">

</div>

我的数据绑定和所有工作都很好。我需要在父组件上执行任何操作吗?
我已经在子组件中尝试了所有生命周期事件。当子组件/指令的绑定更新已经完成时,
ngAfterContentChecked
生命周期钩子被触发。但是您正在更新用作
ngClass
指令绑定输入的属性。这就是问题所在。当Angular运行验证阶段时,它检测到属性有一个挂起的更新,并抛出错误

要更好地理解错误,请阅读以下两篇文章:

考虑一下为什么需要更改
ngAfterViewInit
生命周期挂钩中的属性。在
ngAfterViewInit/Checked
之前触发的任何其他生命周期都可以工作,例如
ngOnInit
ngDoCheck
ngAfterContentChecked


因此,要修复它,请将
RenderWidgetInideWidgetContainer
移动到
ngOnInit()
生命周期挂钩。

您必须告诉
angular
您在
ngAfterContentChecked
之后更新了内容 您可以从
@angular/core
导入
ChangeDetectorRef
,并调用
detectChanges

import {ChangeDetectorRef } from '@angular/core';

constructor( private cdref: ChangeDetectorRef ) {}


ngAfterContentChecked() {

this.sampleViewModel.DataContext = this.DataContext;
this.sampleViewModel.Position = this.Position;
this.cdref.detectChanges();

 }

我也有同样的问题,试图做一些和你一样的事情,我用一些类似于里奇·弗雷迪克森的答案来解决它

运行createComponent()时,它是使用未定义的输入变量创建的。 然后,当您将数据分配给这些输入变量时,它会改变一些事情,并在您的子模板中导致该错误(在我的情况下,这是因为我在ngIf中使用了输入,在分配输入数据后,该输入发生了变化)

在这种特定情况下,我能找到的唯一避免这种情况的方法是在分配数据后强制进行更改检测,但我没有在ngAfterContentChecked()中执行此操作

您的示例代码有点难以理解,但如果我的解决方案适合您,它将是这样的(在父组件中):

导出类ParentComponent实现AfterViewInit{
//我假设你有一个WidgetDirection。
@ViewChild(WidgetDirective)widgetHost:WidgetDirective;
建造师(
专用componentFactoryResolver:componentFactoryResolver,
专用changeDetector:ChangeDetectorRef
) {}
ngAfterViewInit(){
RenderWidgetWidgetContainer();
}
RenderWidgetWidgetContainer(){
让component=this.storeFactory.getWidgetComponent(this.dataSource.ComponentName);
让componentFactory=this.componentFactoryResolver.resolveComponentFactory(component);
让viewContainerRef=this.widgetHost.viewContainerRef;
viewContainerRef.clear();
让componentRef=viewContainerRef.createComponent(componentFactory);
调试器;
//您在此处使用的此类型需要更改为组件
//键入用于调用上面的resolveComponentFactory()的类型(如果还没有)。
//它告诉它该组件实例是否属于该类型,然后它知道
//WidgetDataContext和WidgetPosition是它的@输入。
(componentRef.instance).WidgetDataContext=this.dataSource.DataContext;
(componentRef.instance).WidgetPosition=this.dataSource.Position;
this.changeDetector.detectChanges();
}
}
我的几乎与之相同,只是我使用了@ViewChildren而不是@ViewChildren,因为我有多个主机元素。

如果您将
*ngIf
一起使用,您肯定会陷入这个循环

我找到的唯一解决办法是将
*ngIf
更改为
display:none
功能

两种解决方案:

  • 确保您有一些绑定变量,然后将代码移动到settimeout({},0)
  • 将相关代码移动到nAfterViewInit方法
  • 我用setTimeout包装了我的代码,它成功了

    export class ParentComponent implements AfterViewInit { // I'm assuming you have a WidgetDirective. @ViewChild(WidgetDirective) widgetHost: WidgetDirective; constructor( private componentFactoryResolver: ComponentFactoryResolver, private changeDetector: ChangeDetectorRef ) {} ngAfterViewInit() { renderWidgetInsideWidgetContainer(); } renderWidgetInsideWidgetContainer() { let component = this.storeFactory.getWidgetComponent(this.dataSource.ComponentName); let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component); let viewContainerRef = this.widgetHost.viewContainerRef; viewContainerRef.clear(); let componentRef = viewContainerRef.createComponent(componentFactory); debugger; // This <IDataBind> type you are using here needs to be changed to be the component // type you used for the call to resolveComponentFactory() above (if it isn't already). // It tells it that this component instance if of that type and then it knows // that WidgetDataContext and WidgetPosition are @Inputs for it. (<IDataBind>componentRef.instance).WidgetDataContext = this.dataSource.DataContext; (<IDataBind>componentRef.instance).WidgetPosition = this.dataSource.Position; this.changeDetector.detectChanges(); } }
    setTimeout(() => { // your code here }, 0);
    
    someMethod() // emitted method call from output
    {
        // Your code 
    }
    
    ngOnInit(){
      someMethod(); // call here your error will be gone
    }
    
    import { ChangeDetectionStrategy } from "@angular/core";  // import
    
    @Component({
      changeDetection: ChangeDetectionStrategy.OnPush,
      selector: "abc",
      templateUrl: "./abc.html",
      styleUrls: ["./abc.css"],
    })
    
    import {ChangeDetectorRef } from '@angular/core';
    
    constructor( private cdref: ChangeDetectorRef ) {}
    
    ngAfterContentChecked() {
      this.cdref.detectChanges();
    }