- 首页
- 其他开发
- 获取 ExpressionChangedAfterItHasBeenCheckedError 错误我从另一个组件更新了一个组件的属性
获取 ExpressionChangedAfterItHasBeenCheckedError 错误我从另一个组件更新了一个组件的属性
[英] Getting ExpressionChangedAfterItHasBeenCheckedError error I update property of a Component from another Component
本文介绍了获取 ExpressionChangedAfterItHasBeenCheckedError 错误我从另一个组件更新了一个组件的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
此处提供演示 - https://stackblitz.com/edit/angular-a9wcee一个>
请打开控制台窗口查看错误.
说明我有一个 Bootstrap
组件 ComponentA
它应该以两种不同的方式启动,具体取决于应用程序的启动过程.假设应用程序是使用 url home.com
启动的,那么 ComponentA
不应在启动时显示弹出对话框,但如果应用程序是使用 home.com 启动的;注册
然后应用程序应该显示一个弹出窗口.
我已经读到 Input
不适用于引导程序组件,所以我将一个属性从我的 index.html
传递给 ComponentA
作为启动上下文".
<comp-a someAttr="someValue"></comp-a><!--取决于 someAttr 是否为空 (""),不应该分别显示或显示 pop -->
和 ComponentA
在其模板中使用 DialogComponent
如下
<comp-dialog [message]=""></comp-dialog><!-- 我希望 ComponentB 的输入为空,以 -->
ComponentB
是一个 Bootstrap
css
对话框,并接受一个 Input
消息,当对话框变为可见的.棘手的一点是 ComponentB
在 ComponentA
的模板中,因此 Angular 将它初始化为 ComponentA
启动的一部分,但在 之前>ComponentA
的启动完成,ComponentA
尝试更改 ComponentB
(message
属性)如果它确定它必须显示对话框(ComponentB
)(通过检查属性).我认为这会导致 Change Detection
出现问题,而我的 Angular
正在抛出 ExpressionChangedAfterItHasBeenCheckedError
错误.我如何重新设计组件交互
ComponentA
的代码片段是
检查我是如何开始的.
ngAfterViewChecked(){this.signup = this.el.nativeElement.getAttribute('signup');//获取属性this.isSignupProcess();//检查ComponentB是否需要显示并更新其message属性}isSignupProcess(){console.log("注册是"+this.signup)if(this.signup!==""){//显示ComponentBif(this.signup === "成功") {this.showDialog("注册成功",new DialogContext("",""))//设置ComponentB的message属性}else if(this.signup === "error") {this.showDialog("错误:注册失败",new DialogContext("",""))} 别的 {this.showDialog("无法识别的消息:"+this.signup,new DialogContext("",""))}this.signup = "";//不需要显示ComponentB} 别的 {}
}
更新ComponentB
的message
属性并显示它的逻辑
showDialog(message:string,context:DialogContext) {this.dialogComponent.dialogShow(message,context);}
ComponentB
简单地调用Bootstrap
的模态函数
dialogShow(message:string, context:DialogContext){this.dialogContext = 上下文;this.dialogMessage = 消息;console.log("对话框得到上下文",this.dialogContext);$(this.dialogRef.nativeElement).modal('show');}
ComponentB
的 html
是
<div #modal class="modalfade" tabindex="-1" role="dialog" id={{dialogID}}><div class="modal-dialog modal-dialog-centric" role="document"><div class="modal-content"><div class="modal-header"><h5 class="modal-title">CodingJedi</h5><button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="dialogHide()"><span aria-hidden="true">×</span>按钮>
<div class="modal-body"><p>{{dialogMessage}}</p>
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="dialogHide()">关闭</button>
解决方案
这篇文章很好地解释了导致错误的原因 - https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
引自链接,Angular 中的变更检测经过以下步骤1) 更新所有子组件/指令的绑定属性,即插值值 {{}}2) 在所有子组件/指令上调用 ngOnInit、OnChanges 和 ngDoCheck 生命周期钩子3) 更新当前组件的DOM4) 为子组件运行变更检测5) 为所有子组件/指令调用 ngAfterViewInit 生命周期钩子
在每次操作之后,Angular 都会记住它用于执行操作的值.它们存储在组件视图的 oldValues 属性中.
在对所有组件进行检查后,Angular 会将当前值与它从前一个摘要循环中记住的值进行比较:1) 检查传递给子组件的值是否与现在用于更新这些组件的属性的值相同2) 检查用于更新 DOM 元素的值是否与现在用于更新这些元素的值相同3) 对所有子组件执行相同的检查
在此之后,Angular 运行一个验证循环来检查用于创建子组件和 DOM 中的值是否仍然相同,否则更改检测在一个循环后将不会稳定并可能以无限循环结束.如果 Angular 发现用于创建子组件或在 DOM 上使用的值在当前更改检测周期完成之前发生了更改,则会抛出 ExpressionChangedAfterItHasBeenCheckedError
在我的代码中,DialogComponent 是使用 dialogMessage
和 dialogContext
的初始值创建的.现在,根据应用程序的启动方式,我想更新消息并显示对话框.如果我只是更新 dialogMessage
,Angular 将给出错误 ExpressionChangedAfterItHasBeenCheckedError
,因为 dialogMessage
的值在当前更改检测周期完成之前已更改.>
有两种方法可以解决此问题.使用 setTimeout
创建下一个任务,该任务将在当前更改检测任务完成后或更改 DialogBoxComponent
的 dialogMessage
后执行,运行更改检测再次使用 detectChanges()
ngAfterViewChecked(){setTimeout(()=>this.isSignupProcess());}或者ngAfterViewChecked(){this.isSignupProcess();this.cd.detectChanges();}
The demo is available here - https://stackblitz.com/edit/angular-a9wcee
Please open the console window to see the error.
Description
I have a Bootstrap
component ComponentA
which should start up in two different ways depending on the application's startup process. Say if the application is started using url home.com
then ComponentA
should not show a pop up dialog on startup but if the application is started using home.com;signup
then the application should show a pop up.
I have read that Input
doesn't work for bootstrap components so I am passing an attribute to ComponentA
from my index.html
as a "startup context".
<comp-a someAttr="someValue"></comp-a> <!--depending on whether someAttr is empty ("") or not, the pop should not be shown or shown respectively -->
and ComponentA
's uses a DialogComponent
in its template as follows
<comp-dialog [message]=""></comp-dialog> <!-- I want the input to ComponentB to be empty to begin with -->
ComponentB
is a Bootstrap
css
dialog box and takes an Input
message which it shows when the dialog becomes visible. The tricky bit is that ComponentB
is in ComponentA
's template and thus Angular initialised it as part of ComponentA
's start up but before ComponentA
's start up finished, ComponentA
tries to change ComponentB
(the message
property) if it determines that it has to show the dialog box (ComponentB
) (by checking the attribute). I think this is creating issues with Change Detection
and I am Angular
is throwing ExpressionChangedAfterItHasBeenCheckedError
error. How could I redesign the component interaction
The code snippets of ComponentA
are
Check how I was started.
ngAfterViewChecked(){
this.signup = this.el.nativeElement.getAttribute('signup'); //get the attribute
this.isSignupProcess();//check if ComponentB needs to be shown and update its message property
}
isSignupProcess(){
console.log("sign up is "+this.signup)
if(this.signup!==""){ //show ComponentB
if(this.signup === "success") {
this.showDialog("Signup was successful",new DialogContext("","")) //set message property of ComponentB
}else if(this.signup === "error") {
this.showDialog("Error: Signup wasn't successful",new DialogContext("",""))
} else {
this.showDialog("Unrecognised message: "+this.signup,new DialogContext("",""))
}
this.signup = ""; //no need to show ComponentB
} else {
}
}
the logic to update message
property of ComponentB
and show it is
showDialog(message:string,context:DialogContext) {
this.dialogComponent.dialogShow(message,context);
}
ComponentB
simply calls Bootstrap
's modal function
dialogShow(message:string, context:DialogContext){
this.dialogContext = context;
this.dialogMessage = message;
console.log("dialog got context ",this.dialogContext);
$(this.dialogRef.nativeElement).modal('show');
}
the html
of ComponentB
is
<div #modal class="modal fade" tabindex="-1" role="dialog" id={{dialogID}}>
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">CodingJedi</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="dialogHide()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>{{dialogMessage}}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="dialogHide()">Close</button>
</div>
</div>
</div>
</div>
解决方案
The article explains well what causes the error - https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4
Quoting from the link, Change detection in Angular goes through the following steps
1) update bound properties for all child components/directives i.e. and interpolation values {{}}
2) call ngOnInit, OnChanges and ngDoCheck lifecycle hooks on all child components/directives
3) update DOM for the current component
4) run change detection for a child component
5) call ngAfterViewInit lifecycle hook for all child components/directives
After each operation Angular remembers what values it used to perform an operation.
They are stored in the oldValues property of the component view.
After the checks have been done for all components, Angular compares the current values with the ones it remembers from the previous digest cycle:
1) check that values passed down to the child components are the same as the values that would be used to update properties of these components now
2) check that values used to update the DOM elements are the same as the values that would be used to update these elements now
3) perform the same checks for all child components
After this, Angular runs a verification cycle to check that the values used to create Child Components and in DOM are still the same otherwise the change detection will not stabalise after one cycle and could end in infinite loop. If Angular finds that the value used to create a child component or used on DOM has changed before the current change detection cycle has finished then it will throw ExpressionChangedAfterItHasBeenCheckedError
In my code, DialogComponent is created with initial values of dialogMessage
and dialogContext
. Now, depending on how the app got started, I want to update the message and show the dialog box. If I just update the dialogMessage
, Angular will give error ExpressionChangedAfterItHasBeenCheckedError
because the value of dialogMessage
had changed before the current change detection cycle finished.
There are two ways to fix the issue. Use setTimeout
to create the next task which will be executed once the current change detection task finishes OR after changing the dialogMessage
of DialogBoxComponent
, run change detection again using detectChanges()
ngAfterViewChecked(){
setTimeout(()=>this.isSignupProcess());
}
OR
ngAfterViewChecked(){
this.isSignupProcess();
this.cd.detectChanges();
}
这篇关于获取 ExpressionChangedAfterItHasBeenCheckedError 错误我从另一个组件更新了一个组件的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文