简体   繁体   English

ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。 以前的值:“未定义”

[英]ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'

I know there are a lot of same questions already posted in stack-overflow and tried different solutions to avoid the run-time error but None of them are working for me.我知道堆栈溢出中已经发布了很多相同的问题,并尝试了不同的解决方案来避免运行时错误,但没有一个对我有用。

错误

Component and Html Code组件和 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>

Please Note: This Component is loaded dynamically using DynamicComponentLoader请注意:此组件是使用 DynamicComponentLoader 动态加载的

After My trouble shooting I have identified couple of issues在解决问题后,我发现了几个问题

First of all this child component is loaded dynamically by using DynamicComponentResolver and passing the input values like below首先,这个子组件是通过使用 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;

  }

Even If I changed my child component html like below I am getting this same error.Just add a angular ngclass attribute即使我像下面这样更改了我的子组件 html,我也会收到同样的错误。只需添加一个 angular ngclass 属性

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

</div>

My databinding and everything are working fine.Do I need to do anything on parent component?我的数据绑定和一切工作正常。我需要对父组件做任何事情吗? I already tried all the lifecyle events in child component我已经尝试了子组件中的所有生命周期事件

you have to tell angular that you updated the content after ngAfterContentChecked you can import ChangeDetectorRef from @angular/core and call detectChanges你要告诉angular ,你更新后的内容ngAfterContentChecked您可以导入ChangeDetectorRef@angular/core和呼叫detectChanges

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

constructor( private cdref: ChangeDetectorRef ) {}


ngAfterContentChecked() {

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

 }

The ngAfterContentChecked lifecycle hook is triggered when bindings updates for the child components/directives have been already been finished.当子组件/指令的绑定更新已经完成时,将触发ngAfterContentChecked生命周期钩子。 But you're updating the property that is used as a binding input for the ngClass directive.但是您正在更新用作ngClass指令的绑定输入的ngClass That is the problem.这就是问题所在。 When Angular runs validation stage it detects that there's a pending update to the properties and throws the error.当 Angular 运行验证阶段时,它检测到属性有待处理的更新并抛出错误。

To understand the error better, read these two articles:要更好地理解错误,请阅读这两篇文章:

Think about why you need to change the property in the ngAfterViewInit lifecycle hook.想想为什么需要更改ngAfterViewInit生命周期钩子中的属性。 Any other lifecycle that is triggered before ngAfterViewInit/Checked will work, for example ngOnInit or ngDoCheck or ngAfterContentChecked .ngAfterViewInit/Checked之前触发的任何其他生命周期都可以工作,例如ngOnInitngDoCheckngAfterContentChecked

So to fix it move renderWidgetInsideWidgetContainer to the ngOnInit() lifecycle hook.所以为了修复它,将renderWidgetInsideWidgetContainer移动到ngOnInit()生命周期钩子。

 ngAfterViewInit() {
   setTimeout(() => {
     this.renderWidgetInsideWidgetContainer();
   }, 0);
  }

That is a good solution to resolve this problem.这是解决此问题的好方法。

If you are using <ng-content> with *ngIf you are bound to fall into this loop.如果您将<ng-content>*ngIf您一定会陷入这个循环。

Only way out I found was to change *ngIf to display:none functionality我发现的唯一出路是将*ngIf更改为display:none功能

I was having trouble with .我遇到了麻烦。

ERROR: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.错误:ExpressionChangedAfterItHasBeenCheckedError:检查后表达式已更改。 Previous value for 'mat-checkbox-checked': 'true'. 'mat-checkbox-checked' 的先前值:'true'。 Current value: 'false'.当前值:'假'。

The Problem here is that the updated value is not detected until the next change Detection Cycle runs.这里的问题是在下一个更改检测周期运行之前不会检测到更新的值。

The easiest solution is to add a Change Detection Strategy.最简单的解决方案是添加变更检测策略。 Add these lines to your code:将这些行添加到您的代码中:

import { ChangeDetectionStrategy } from "@angular/core";  // import

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "abc",
  templateUrl: "./abc.html",
  styleUrls: ["./abc.css"],
})

This shall solve the problem这将解决问题

  constructor(private changeDetector: ChangeDetectorRef) {}

  ngAfterViewChecked() {
    this.changeDetector.detectChanges();
  }

Two Solutions:两种解决方案:

  1. Make Sure if you have some binding variables then move that code to settimeout ( { }, 0);确保您有一些绑定变量,然后将该代码移动到settimeout ( { }, 0);
  2. Move your related code to ngAfterViewInit method将您的相关代码移至ngAfterViewInit方法

*NgIf会在这里产生问题,所以要么使用 display none CSS 或更简单的方法是使用[hidden]="!condition"

setTimeout(() => { // your code here }, 0);

我用 setTimeout 包装了我的代码并且它起作用了

I had the same issue trying to do something the same as you and I fixed it with something similar to Richie Fredicson's answer.我在尝试做与您相同的事情时遇到了同样的问题,并且我用类似于 Richie Fredicson 的回答的方法修复了它。

When you run createComponent() it is created with undefined input variables.当您运行 createComponent() 时,它是使用未定义的输入变量创建的。 Then after that when you assign data to those input variables it changes things and causes that error in your child template (in my case it was because I was using the input in an ngIf, which changed once I assigned the input data).然后,当您将数据分配给这些输入变量时,它会更改内容并导致您的子模板中出现该错误(在我的情况下,这是因为我在 ngIf 中使用了输入,一旦我分配了输入数据,就会发生变化)。

The only way I could find to avoid it in this specific case is to force change detection after you assign the data, however I didn't do it in ngAfterContentChecked().在这种特定情况下,我能找到避免它的唯一方法是在分配数据后强制更改检测,但是我没有在 ngAfterContentChecked() 中这样做。

Your example code is a bit hard to follow but if my solution works for you it would be something like this (in the parent component):您的示例代码有点难以理解,但如果我的解决方案适合您,它将是这样的(在父组件中):

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();
  }
}

Mine is almost the same as that except I'm using @ViewChildren instead of @ViewChild as I have multiple host elements.我的几乎和那个一样,除了我使用 @ViewChildren 而不是 @ViewChild 因为我有多个宿主元素。

This code solved my Problem.这段代码解决了我的问题。

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

constructor( private cdref: ChangeDetectorRef ) {}

ngAfterContentChecked() {
  this.cdref.detectChanges();
}

RxJS timer() RxJS 定时器()

Like others suggested you can solve the issue by placing your code inside a setTimeout(..) function.就像其他人建议的那样,您可以通过将代码放在setTimeout(..)函数中来解决问题。 It will essentially move the execution outside the event-loop stack, and hence into Angular's next change-detection cycle.它本质上会将执行移到事件循环堆栈之外,从而进入 Angular 的下一个更改检测周期。

RxJS has its own timeout implementation called timer() - it accepts milliseconds as an argument and returns an observable. RxJS 有自己的超时实现,称为timer() - 它接受毫秒作为参数并返回一个可观察的。 Since we only need the execution to happen after the event loop stack is clean (and Angular has finished all its rendering calculations), we can use timer() without any argument (it'll anyway default to 0):由于我们只需要在事件循环堆栈干净(并且 Angular 完成所有渲染计算)后执行,我们可以使用timer()不带任何参数(它无论如何都会默认为 0):

ngAfterViewInit() {
    timer().subscribe(() => {
        // your code here
    })
}

JS event loop schema: JS 事件循环架构:

在此处输入图片说明

PS: My error details vary slightly, in the sense that mine detected a change in mat-focused value from true to false . PS:我的错误细节略有不同,因为我检测到mat-focused值从truefalse的变化。

During my research, I noticed that the error was thrown because I tried to bind the SelectedIndex property of mat-tab-group to the index of the selected item in a mat-select field inside tab 1 of the tab group (code excerpt below).在我的研究过程中,我注意到错误被抛出,因为我试图将mat-tab-groupSelectedIndex属性绑定到选项卡组的tab 1内的mat-select字段中所选项目的索引(下面的代码摘录) .

HTML extract: HTML摘录:

  <form [formGroup]="form" (ngSubmit)="onSubmit()">
    <mat-tab-group animationDuration="500ms" [selectedIndex]="titleId.value">
      <mat-tab label="Tab 1">
        <mat-radio-group formControlName="titleId">
          <mat-radio-button value="1">Mr</mat-radio-button>
          <mat-radio-button value="2">Mrs</mat-radio-button>
        </mat-radio-group>
      </mat-tab>
      <mat-tab label="tab 2">
        ...
      </mat-tab>
      <mat-tab label="tab 3">
        ...
      </mat-tab>
    </mat-tab-group>
  </form>

Although my code worked as expected (ie switching tabs based on a selected item from the dropdown list), the error was pretty frustrating to be ignored.虽然我的代码按预期工作(即根据从下拉列表中选择的项目切换选项卡),但忽略该错误非常令人沮丧。 Unfortunately, no suggested solution here worked for me.不幸的是,这里没有建议的解决方案对我有用。 However, I noticed that moving mat-radio-group outside the mat-tab-group component, fixed the issue.但是,我注意到将mat-radio-group移到 mat mat-tab-group组件之外,解决了这个问题。

New implementation:新实施:

  <form [formGroup]="form" (ngSubmit)="onSubmit()">
    <mat-radio-group formControlName="titleId">
      <mat-radio-button value="1">Mr</mat-radio-button>
      <mat-radio-button value="2">Mrs</mat-radio-button>
    </mat-radio-group>
    <mat-tab-group animationDuration="500ms" [selectedIndex]="titleId.value">
      <mat-tab label="Tab 1">
        ...
      </mat-tab>
      <mat-tab label="tab 2">
        ...
      </mat-tab>
      <mat-tab label="tab 3">
        ...
      </mat-tab>
    </mat-tab-group>
  </form>

I expected the code below to fix the issue, but it didn't.我希望下面的代码能够解决问题,但事实并非如此。 Maybe I wasn't doing something right.也许我做的事情不对。

abc.component.ts code extract abc.component.ts 代码摘录

  ngAfterViewInit() {
    setTimeout(() => {
      this.cdRef.detectChanges(); /*cdRef injected in constructor*/
    }, 0);
  }

I still believe there is a better way to fix this.我仍然相信有更好的方法来解决这个问题。

Try this, to call your code in ngOnInit()试试这个,在ngOnInit() 中调用你的代码

someMethod() // emitted method call from output
{
    // Your code 
}

ngOnInit(){
  someMethod(); // call here your error will be gone
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ExpressionChangedAfterItHasBeenCheckedError:检查后表达式已更改。 以前的值:“未定义”异常 - ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined' Exception ExpressionChangedAfterItHasBeenCheckedError:检查后表达式已更改。 以前的值:&#39;ngIf:true&#39;。 当前值:&#39;ngIf:false&#39; - ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: true'. Current value: 'ngIf: false' ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。 先前的值:&#39;ngIf:false&#39;。 当前值:&#39;ngIf:true&#39; - ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: false'. Current value: 'ngIf: true' Angular — ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。 (嵌套的FormArray) - Angular — ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. (Nested FormArray) ERROR 错误:ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。 Angular - ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Angular 调用toISOString()会引发“ ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。” - Calling toISOString() thows “ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.” Angular ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后已更改。 cdkdrag - Angular ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. cdkdrag 检查后表达式已更改。 以前的值:“ngTemplateOutlet:未定义”。 当前值:'ngTemplateOutlet:[object Object]' - Expression has changed after it was checked. Previous value: 'ngTemplateOutlet: undefined'. Current value: 'ngTemplateOutlet: [object Object]' Angular2,错误:检查表达式后,表达式已更改。 上一个值:“未定义”。 当前值:&#39;0 - Angular2, Error: Expression has changed after it was checked. Previous value: 'undefined'. Current value: '0 Angular:检查后表达式已更改。 以前的值:&#39;未定义&#39;。 当前值:&#39;[对象对象]&#39; - Angular: Expression has changed after it was checked. Previous value: 'undefined'. Current value: '[object Object]'
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM