如何管理Angular2“;表达式在被选中后已更改;组件属性依赖于当前日期时间时发生异常
我的组件具有依赖于当前日期时间的样式。在我的组件中,我有以下函数如何管理Angular2“;表达式在被选中后已更改;组件属性依赖于当前日期时间时发生异常,angular,typescript,time,styles,components,Angular,Typescript,Time,Styles,Components,我的组件具有依赖于当前日期时间的样式。在我的组件中,我有以下函数 private fontColor( dto : Dto ) : string { // date d'exécution du dto let dtoDate : Date = new Date( dto.LastExecution ); (...) let color = "hsl( " + hue + ", 80%, " + (maxLigness - lightnessAmp) +
private fontColor( dto : Dto ) : string {
// date d'exécution du dto
let dtoDate : Date = new Date( dto.LastExecution );
(...)
let color = "hsl( " + hue + ", 80%, " + (maxLigness - lightnessAmp) + "%)";
return color;
}
lightnessAmp
根据当前日期时间计算。如果dtoDate
在过去24小时内,颜色会发生变化
准确的误差如下所示:
表达式在选中后已更改。以前的值:“hsl(123,80%,49%)”。当前值:“hsl(123,80%,48%)”
我知道只有在检查值时,异常才会出现在开发模式中。如果检查的值与更新的值不同,则引发异常
因此,我尝试使用以下钩子方法在每个生命周期更新当前日期时间,以防止出现异常:
ngAfterViewChecked()
{
console.log( "! changement de la date du composant !" );
this.dateNow = new Date();
}
…但没有成功。在更改后显式运行更改检测:
import { ChangeDetectorRef } from '@angular/core';
constructor(private cdRef:ChangeDetectorRef) {}
ngAfterViewChecked()
{
console.log( "! changement de la date du composant !" );
this.dateNow = new Date();
this.cdRef.detectChanges();
}
TL;DR
ngAfterViewInit() {
setTimeout(() => {
this.dateNow = new Date();
});
}
虽然这是一种解决方法,但有时确实很难用更好的方式解决这个问题,所以如果使用这种方法,不要责怪自己。没关系
示例:初始问题[],通过setTimeout()
[]解决
如何避免 通常,此错误通常发生在添加某个位置(甚至在父/子组件中)后
ngAfterViewInit
。所以,第一个问题是问你自己——我能不能没有ngAfterViewInit
?也许您将代码移动到某个地方(ngAfterViewChecked
可能是另一种选择)
示例:[]
同时 另外,
ngAfterViewInit
中影响DOM的异步内容也可能导致这种情况。也可以通过setTimeout
或在管道中添加delay(0)
操作符来解决:
ngAfterViewInit() {
this.foo$
.pipe(delay(0)) //"delay" here is an alternative to setTimeout()
.subscribe();
}
示例:[]
读得好
关于如何调试它以及它为什么会发生的好文章:,正如@leocaseiro在上所提到的 我找到了3种解决方案,适用于那些寻求简单解决方案的人 1) 从
ngAfterViewInit
移动到ngAfterContentInit
2) 与ChangeDetectorRef
as组合使用后移到NGAViewChecked
关于#14748的建议(评论)
3) 继续使用ngOnInit(),但在之后调用changedtectorref.detectChanges()
你的改变
我认为您可以想象的最好、最干净的解决方案是:
@组件({
选择器:“应用程序我的组件”,
模板:`{{myData?.anyfield}}`,
样式:['']
} )
导出类MyComponent实现OnInit{
私人myData;
构造函数(私有myService:myService){}
恩戈尼尼特(){
/*
异步..等待
清除ExpressionChangedTerithasBeenCheckedError异常。
*/
this.myService.myObservable.subscribe(
异步(数据)=>{this.myData=await data}
);
}
}
使用Angular 5.2.9进行测试,我使用了很多次
Promise.resolve(数据)。然后(()=>{
控制台日志(“!组件日期更改!”);
this.dateNow=新日期();
this.cdRef.detectChanges();
});代码>她你去两个解决方案
1.将ChangeDetectionStrategy修改为OnPush
对于这个解决方案,您基本上是在告诉angular:
停止检查更改;只有在我知道有必要的时候我才会这样做
快速解决方案:
修改组件,使其使用ChangeDetectionStrategy.OnPush
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
// ...
}
有了这个,事情似乎不再有效了。这是因为从现在起,您必须手动调用detectChanges()
this.cdr.detectChanges();
以下链接帮助我正确理解ChangeDetectionStrategy:
2.理解表达式更改后的检查错误
下面是tomonari_t关于这个错误原因的一个小摘录,我试图只包括帮助我理解这个错误的部分
完整的文章展示了关于这里显示的每一点的真实代码示例
根本原因是angular lifecycle的:
每次操作后,Angular都会记住它用来执行的值
手术。它们存储在的oldValues属性中
组件视图
对所有部件进行检查后,开始
下一个摘要周期,但它不执行操作,而是将当前值与它记忆的值进行比较
上一个摘要周期
在摘要循环中检查以下操作:
检查传递给子组件的值是否与
用于更新这些组件的属性的值
现在
检查用于更新DOM元素的值是否与
用于更新这些元素的值现在执行
一样
检查所有子组件
因此,当比较的值不同时,就会抛出错误。
,blogger说:
罪魁祸首总是子组件或指令
最后,这里是一些通常会导致此错误的真实示例:
- 共享服务
- 同步事件广播
- 动态组件实例化
setTimeout
解决方案,因为我的情况导致了“几乎”无限循环(21次调用,我不愿意告诉您如何激发它们)
我建议您始终牢记角度生命周期,以便在每次修改另一个组件的值时考虑它们将如何受到影响。有了这个错误,Angular告诉您:
你可能做得不对,你确定你是对的吗
该博客还说:<
@Component({
selector: 'app-spinner',
templateUrl: './spinner.component.html',
styleUrls: ['./spinner.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SpinnerComponent implements OnInit, OnDestroy, AfterContentChecked {
show = false;
private subscription: Subscription;
constructor(private spinnerService: SpinnerService, private changeDedectionRef: ChangeDetectorRef) { }
ngOnInit() {
this.subscription = this.spinnerService.spinnerState
.subscribe((state: SpinnerState) => {
this.show = state.show;
});
}
ngAfterContentChecked(): void {
this.changeDedectionRef.detectChanges();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
calendarView$ = new BehaviorSubject<View>(null);
ngAfterViewInit(): void {
// ViewChild is available here, so get the JS API
this.calendarApi = this.calendar.getApi();
}
ngAfterViewChecked(): void {
// The view has been checked and I know that the View object from
// fullcalendar is available, so emit it.
this.calendarView$.next(this.calendarApi.view);
}
export class SomeComponent {
header: string;
}
ngAfterViewInit() {
// change variable value here...
}
ngAfterContentInit() {
// change variable value here...
}