Kev*_*rge 243 angular2-changedetection angular2-databinding angular
请向我解释为什么我一直收到这个错误: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
显然,我只是在开发模式下获得它,它不会在我的生产版本中发生,但它非常烦人,而我根本不理解在我的开发环境中出现错误的好处 - 不会出现在prod上 - - 可能是因为我缺乏理解.
通常,修复很容易,我只是将错误导致代码包装在setTimeout中,如下所示:
setTimeout(()=> {
this.isLoading = true;
}, 0);
Run Code Online (Sandbox Code Playgroud)
或者使用如下构造函数强制检测更改constructor(private cd: ChangeDetectorRef) {}::
this.isLoading = true;
this.cd.detectChanges();
Run Code Online (Sandbox Code Playgroud)
但为什么我经常遇到这个错误?我想了解它,以便将来可以避免这些hacky修复.
小智 96
我有类似的问题.看一下生命周期钩子文档,我改变ngAfterViewInit了ngAfterContentInit它并且它起作用了.
Gün*_*uer 84
此错误表示应用程序中存在实际问题,因此抛出异常是有意义的.
在devMode更改检测中,在每次常规更改检测运行后添加一个额外的转弯,以检查模型是否已更改.
如果模型在常规和附加变化检测转弯之间发生了变化,则表明这两者之一
哪些都不好,因为不清楚如何继续,因为模型可能永远不会稳定.
如果Angular运行更改检测,直到模型稳定,它可能会永远运行.如果Angular未运行更改检测,则视图可能不会反映模型的当前状态.
Sha*_*asi 66
我正在使用ng2-carouselamos(Angular 8 和 Bootstrap 4)
采取这些步骤解决了我的问题:
AfterViewCheckedconstructor(private changeDetector : ChangeDetectorRef ) {}ngAfterViewChecked(){ this.changeDetector.detectChanges(); }Kev*_*rge 63
一旦我理解了Angular Lifecycle Hooks及其与变化检测的关系,就会有很多理解.
我试图让Angular更新绑定到*ngIf元素的全局标志,并且我试图在ngOnInit()另一个组件的生命周期钩子内部更改该标志.
根据文档,在Angular已经检测到更改后调用此方法:
在第一次ngOnChanges()之后调用一次.
因此更新内部标志ngOnChanges()不会启动更改检测.然后,一旦更改检测自然再次触发,则标志的值已更改并且引发错误.
在我的情况下,我改变了这个:
constructor(private globalEventsService: GlobalEventsService) {
}
ngOnInit() {
this.globalEventsService.showCheckoutHeader = true;
}
Run Code Online (Sandbox Code Playgroud)
对此:
constructor(private globalEventsService: GlobalEventsService) {
this.globalEventsService.showCheckoutHeader = true;
}
ngOnInit() {
}
Run Code Online (Sandbox Code Playgroud)
它解决了问题:)
Arn*_*d P 36
更新
我强烈建议首先从OP的自我回应开始:正确思考在constructorvs应该做什么可以做什么ngOnChanges().
原版的
这是一个侧面说明而不是答案,但它可能对某人有所帮助.当我试图使按钮的存在取决于表单的状态时,我偶然发现了这个问题:
<button *ngIf="form.pristine">Yo</button>
Run Code Online (Sandbox Code Playgroud)
据我所知,这种语法导致根据条件添加和删除DOM按钮.这反过来导致了ExpressionChangedAfterItHasBeenCheckedError.
在我的情况下修复(虽然我没有声称要掌握差异的全部含义),display: none而是使用:
<button [style.display]="form.pristine ? 'inline' : 'none'">Yo</button>
Run Code Online (Sandbox Code Playgroud)
epe*_*per 23
有一些有趣的答案,但我似乎找不到符合我需要的答案,最接近的是来自@ chittrang-mishra,它只涉及一个特定的功能而不是我的应用程序中的几个切换.
我不想使用[hidden]趁*ngIf甚至不作为DOM的一部分,所以我发现下面的解决方案,它可能不是最好的一切,因为它抑制了错误,而不是试图解决这个问题,但对我来说,我知道最终结果是正确的,我的应用程序似乎没问题.
我所做的是实现AfterViewChecked,添加constructor(private changeDetector : ChangeDetectorRef ) {}然后
ngAfterViewChecked(){
this.changeDetector.detectChanges();
}
Run Code Online (Sandbox Code Playgroud)
我希望这有助于其他许多人帮助我.
And*_*sta 20
在我的情况下,我在我的spec文件中遇到了这个问题,同时运行我的测试.
我不得不换ngIf 到 [hidden]
<app-loading *ngIf="isLoading"></app-loading>
Run Code Online (Sandbox Code Playgroud)
至
<app-loading [hidden]="!isLoading"></app-loading>
Run Code Online (Sandbox Code Playgroud)
Chi*_*hra 19
请按照以下步骤操作:
1.使用'ChangeDetectorRef'从@ angular/core导入它,如下所示:
import{ ChangeDetectorRef } from '@angular/core';
Run Code Online (Sandbox Code Playgroud)
2.在构造函数()中实现它,如下所示:
constructor( private cdRef : ChangeDetectorRef ) {}
Run Code Online (Sandbox Code Playgroud)
3.将以下方法添加到您正在调用单击按钮等事件的函数中.所以它看起来像这样:
functionName() {
yourCode;
//add this line to get rid of the error
this.cdRef.detectChanges();
}
Run Code Online (Sandbox Code Playgroud)
小智 16
我遇到了同样的问题,因为我的组件中的一个数组中的值发生了变化.但是,我没有检测到值变化的变化,而是将组件变化检测策略更改为onPush(它将检测对象更改的更改而不是值更改).
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
selector: -
......
})
Run Code Online (Sandbox Code Playgroud)
Lij*_*ijo 14
Angular运行更改检测,当它发现已传递给子组件的某些值已更改时,Angular会抛出错误ExpressionChangedAfterItHasBeenCheckedError 单击"更多"
为了纠正这个问题,我们可以使用AfterContentChecked生命周期钩子和
import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';
constructor(
private cdref: ChangeDetectorRef) { }
ngAfterContentChecked() {
this.cdref.detectChanges();
}
Run Code Online (Sandbox Code Playgroud)
ATH*_*HER 13
因此,变更检测背后的机制实际上是以同步执行变更检测和验证摘要的方式工作的.这意味着,如果我们异步更新属性,则在验证循环运行时不会更新值,我们也不会收到ExpressionChanged...错误.我们得到此错误的原因是,在验证过程中,Angular看到的值与更改检测阶段记录的值不同.所以要避免......
1)使用changeDetectorRef
2)使用setTimeOut.这将作为宏任务在另一个VM中执行您的代码.Angular在验证过程中不会看到这些更改,您将不会收到该错误.
setTimeout(() => {
this.isLoading = true;
});
Run Code Online (Sandbox Code Playgroud)
3)如果你真的想在同一个VM上执行代码,请使用
Promise.resolve(null).then(() => this.isLoading = true);
Run Code Online (Sandbox Code Playgroud)
这将创建一个微任务.在当前同步代码完成执行之后处理微任务队列,因此在验证步骤之后将对属性进行更新.
ted*_*edw 10
ExpressionChangedAfterItHasBeenCheckedError如果您在触发EventEmitter内部时看到ngAfterViewInit(),那么您可以true在创建时传递,EventEmitter以使其在当前更改检测周期后异步发出。
@Component({
...
})
export class MyComponent implements AfterViewInit {
// Emit asynchronously to avoid ExpressionChangedAfterItHasBeenCheckedError
@Output() valueChange = new EventEmitter<number>(true);
...
ngAfterViewInit(): void {
...
this.valueChange.emit(newValue);
...
}
}
Run Code Online (Sandbox Code Playgroud)
小智 7
这是我对正在发生的事情的想法。我还没有阅读文档,但确信这是显示错误的部分原因。
*ngIf="isProcessing()"
Run Code Online (Sandbox Code Playgroud)
当使用 *ngIf 时,每次条件发生变化时,它都会通过添加或删除元素来物理更改 DOM。因此,如果条件在渲染到视图之前发生变化(这在 Angular 的世界中很有可能),则会抛出错误。请参阅此处有关开发和生产模式的说明。
[hidden]="isProcessing()"
Run Code Online (Sandbox Code Playgroud)
使用时[hidden]它不会物理改变,而只是从视图中DOM隐藏,最有可能在后面使用。该元素仍然存在于 DOM 中,但根据条件的值不可见。这就是为什么使用时不会出现错误。elementCSS[hidden]
@HostBinding 可能是此错误的一个令人困惑的来源。例如,假设您在组件中具有以下主机绑定
// image-carousel.component.ts
@HostBinding('style.background')
style_groupBG: string;
Run Code Online (Sandbox Code Playgroud)
为简单起见,假设此属性是通过以下输入属性更新的:
@Input('carouselConfig')
public set carouselConfig(carouselConfig: string)
{
this.style_groupBG = carouselConfig.bgColor;
}
Run Code Online (Sandbox Code Playgroud)
在父组件中,您以编程方式将其设置在 ngAfterViewInit
@ViewChild(ImageCarousel) carousel: ImageCarousel;
ngAfterViewInit()
{
this.carousel.carouselConfig = { bgColor: 'red' };
}
Run Code Online (Sandbox Code Playgroud)
这是发生的事情:
carousel(通过 ViewChild)carousel直到ngAfterViewInit()(它将为空)style_groupBG = 'red'background: red主机 ImageCarousel 组件carousel.style.background并且不够聪明,无法知道这不是问题,因此会引发异常。一种解决方案是引入另一个包装器 div 内幕 ImageCarousel 并在其上设置背景颜色,但随后您无法获得使用的一些好处HostBinding(例如允许父级控制对象的完整边界)。
更好的解决方案,在父组件中设置config后添加detectChanges()。
ngAfterViewInit()
{
this.carousel.carouselConfig = { ... };
this.cdr.detectChanges();
}
Run Code Online (Sandbox Code Playgroud)
这可能看起来很明显,并且与其他答案非常相似,但存在细微差别。
考虑@HostBinding在开发过程中稍后才添加的情况。突然你得到这个错误,它似乎没有任何意义。
这个错误可能非常令人困惑,并且很容易对它发生的确切时间做出错误的假设。我发现在受影响的组件中的适当位置添加大量这样的调试语句很有帮助。这有助于理解流程。
在父级 put 语句中,像这样(确切的字符串“EXPRESSIONCHANGED”很重要),但除此之外,这些只是示例:
console.log('EXPRESSIONCHANGED - HomePageComponent: constructor');
console.log('EXPRESSIONCHANGED - HomePageComponent: setting config', newConfig);
console.log('EXPRESSIONCHANGED - HomePageComponent: setting config ok');
console.log('EXPRESSIONCHANGED - HomePageComponent: running detectchanges');
Run Code Online (Sandbox Code Playgroud)
在子/服务/计时器回调中:
console.log('EXPRESSIONCHANGED - ChildComponent: setting config');
console.log('EXPRESSIONCHANGED - ChildComponent: setting config ok');
Run Code Online (Sandbox Code Playgroud)
如果您detectChanges也手动运行添加日志记录:
console.log('EXPRESSIONCHANGED - ChildComponent: running detectchanges');
this.cdr.detectChanges();
Run Code Online (Sandbox Code Playgroud)
然后在 Chrome 调试器中只需按“EXPRESSIONCHANGES”进行过滤。这将准确地向您显示所设置的所有内容的流程和顺序,以及 Angular 在什么时候抛出错误。
您还可以单击灰色链接来放置断点。
另一件需要注意的事情是,如果您的应用程序中具有类似命名的属性(例如style.background),请确保您正在调试您认为的属性 - 通过将其设置为模糊的颜色值。
当我添加时我的问题很明显,*ngIf但这不是原因。该错误是由于更改标签中的模型{{}}然后尝试*ngIf稍后在语句中显示更改后的模型而引起的。这是一个例子:
<div>{{changeMyModelValue()}}</div> <!--don't do this! or you could get error: ExpressionChangedAfterItHasBeenCheckedError-->
....
<div *ngIf="true">{{myModel.value}}</div>
Run Code Online (Sandbox Code Playgroud)
为了解决这个问题,我将通话地点更改changeMyModelValue()为更有意义的地方。
changeMyModelValue()在我的情况下,每当子组件更改数据时我都想调用。这需要我在子组件中创建并发出一个事件,以便父组件可以处理它(通过调用changeMyModelValue(). 请参阅https://angular.io/guide/component-interaction#parent-listens-for-child-event
尝试了上面建议的大多数解决方案。在这种情况下,只有这对我有用。我正在使用 *ngIf 根据 api 调用切换角度材料的不确定进度条,它正在抛出ExpressionChangedAfterItHasBeenCheckedError.
在相关组件中:
constructor(
private ngZone: NgZone,
private changeDetectorRef: ChangeDetectorRef,
) {}
ngOnInit() {
this.ngZone.runOutsideAngular(() => {
this.appService.appLoader$.subscribe(value => {
this.loading = value;
this.changeDetectorRef.detectChanges();
});
});
}
Run Code Online (Sandbox Code Playgroud)
诀窍是使用 ngzone 绕过角度组件的变化检测。
PS:不确定这是否是一个优雅的解决方案,但使用 AfterContentChecked 和 AfterViewChecked 生命周期挂钩必然会引发性能问题,因为您的应用程序会因多次触发而变得越来越大。
static: true到@ViewChild装饰器@ViewChild(UploadComponent, { static: true }) uploadViewChild: UploadComponent;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
149350 次 |
| 最近记录: |