我的子模块中的每个EventEmiiter都会出现此错误,但我无法找到解决方法.
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.
这是触发我的EventEmitter的原因:
ngOnChanges(changes: any) {
if (changes.groupingTabValid) {
if (changes.groupingTabValid.currentValue !== changes.groupingTabValid.previousValue) {
this.groupingTabValidChange.emit(this.groupingTabValid);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的"主要"组件的HTML
<year-overview-grouping [definitionDetails]="definitionDetails"
[fixedData]="fixedData"
[showValidation]="showValidation"
[groupingTabValid]="groupingTabValid"
(groupingTabValidChange)="setValidators('groupingTab', $event)">
</year-overview-grouping>
Run Code Online (Sandbox Code Playgroud)
哪个叫这个功能
public setValidators(validator: string, e: boolean) {
switch (validator) {
case "groupingTab":
this.groupingTabValid = e;
break;
case "selectionTab":
this.selectionTabValid = e;
break;
}
if (this.groupingTabValid && this.selectionTabValid) {
this.valid = true;
} else {
this.valid = false;
}
}
Run Code Online (Sandbox Code Playgroud)
1)在一个简单的解释中,是什么导致了这个错误? …
我有一个看起来像这样的子组件:
@Component({
selector: 'app-child',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
{{text}}
`
})
export class ChildComponent {
@Input() text = '';
constructor(public host: ElementRef) { }
}
Run Code Online (Sandbox Code Playgroud)
还有一个看起来像这样的父组件:
@Component({
selector: 'app-parent',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-content></ng-content>`
})
export class ParentComponent {
@ContentChild(ChildComponent) child: ChildComponent;
constructor(private cdr: ChangeDetectorRef) { }
ngAfterContentInit() {
this.child.text = 'hello';
this.child.host.nativeElement.addEventListener('click', () => {
this.child.text = 'from click';
this.cdr.detectChanges();
});
}
Run Code Online (Sandbox Code Playgroud)
第一次分配给text属性工作正常,但是当我单击按钮并尝试text再次更改属性时,什么也没有发生。
这很令人困惑,因为据我所知: 1. click 事件应该触发更改检测,并且 text 属性不同,因此应该已更新。2.我明确地打电话detectChanges(),这也应该检查我所知道的孩子们。
我错过了什么?
我想知道使用其中一种比另一种有什么优点或缺点:
constructor(private app:ApplicationRef, private ref:ChangeDetectorRef) {
this.ref.markForCheck();
// OR
this.ref.detectChanges()
// will do same thing?
...
Run Code Online (Sandbox Code Playgroud)
与
zone.run
(() => doSomething())
...
Run Code Online (Sandbox Code Playgroud)
与
app.tick();
Run Code Online (Sandbox Code Playgroud)
它们本质上都会标记组件以进行检查和更新/重绘 UI。
我知道app.tick()会对整个应用程序执行此操作,但在我的测试中它实际上并没有强制 UI 更新。
zone.run两者markforCheck都强制 UI 在下一个区域循环检查时更新,那么为什么要使用其中一个而不是另一个呢?
我将从我在 StackOverflow 上看到一个类似问题的概念开始这个问题,但那个问题只回答了差异。
我要问的是我应该根据情况使用什么以及一种或另一种方法可能有哪些缺点。
我知道detectChanges在一个元素及其子元素上运行即时更改检测周期,同时markForCheck只将当前元素及其祖先标记为脏,并且应该在下一个更改检测周期中检查它们。
我问这个主要是因为我觉得我不应该总是markForCheck在异步调用中使用。
例如,我有一个InputComponent它是常规 HTML 输入的包装器。这InputComponent已ChangeDetectionStrategy.OnPush启用。
当我对服务器进行异步调用并获取数据时,我需要对其运行更改检测InputComponent以更新选项列表,我有两个选项。
首先(我觉得我应该使用的)是detectChanges因为它只会对这个确切的组件应用检查,同时markForCheck会导致检查整个树枝。
那么我应该使用什么,我需要使用markForCheck什么,为什么?
我不明白为什么即使我使用ChangeDetectionStrategy.OnPush和changeDetectorRef.detach()函数ngDoCheck一直被调用.我的应用程序中有数千个组件,如果从child2引发了一个事件(鼠标点击等),我想阻止child1的changeDetection.
这是一个掠夺者
如你所见,我有一个父组件
@Component({
selector: 'my-app',
template: `
<app-child1 test="test"></app-child1>
<app-child2 test="test"></app-child2> `,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent {
test = 'test';
constructor() { }
ngDoCheck() {
console.log("### ngDoCheck app component");
}
}
Run Code Online (Sandbox Code Playgroud)
和2个相同的孩子:
@Component({
selector: 'app-child1',
template: `<input type="text" [(ngModel)]="test" /><br/> `,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class Child1Component implements OnInit {
@Input()
test: string;
constructor(public cd: ChangeDetectorRef) { }
ngAfterViewInit() {
console.log("###### DETACH child 1");
this.cd.detach();
}
ngDoCheck() {
console.log("### ngDoCheck child 1");
}
}
Run Code Online (Sandbox Code Playgroud)
如果我开始输入child1的输入,则调用child2的ngDoCheck函数.
想想有成千上万的孩子,它真的很慢...... …
我们的Angular应用程序中有许多组件需要定期显示每个组件唯一的新值(倒计时,时间戳,已用时间等).最自然的方法是创建使用RxJS timer和interval工厂函数的observable .但是,这些会在调用间隔函数时多次触发整个应用程序的每个间隔的角度变化检测.如果我们在页面上有许多组件,则会触发每秒或每个时间段对整个应用程序进行数十次检测,从而产生很大的性能开销.
到目前为止,我有两种尝试解决问题的方法.对这两者的一个好的答案将是非常有帮助的 - 理想的是两者.我想避免手动触发变化检测,而是依赖于Observables发出的新值,并让异步管道/ OnPush变化检测策略负责触发变化检测.如果这是不可能的,我想了解原因.
timer或interval函数触发角度变化检测?使用NgZone zone.runOutsideAngular(() => this.interval$ = interval(1000) ... )似乎不会这样做.StackBlitz示例:https://stackblitz.com/edit/angular-zo5h39Subject结合setInterval被调用的内部创建一个Observable流zone.runOutsideAngular,为什么在从主题发出新值时不会为子组件触发更改检测?StackBlitz示例:https://stackblitz.com/edit/angular-yxdjgd到处都说 markForCheck 只是将当前组件视图和所有父组件(直到根组件)标记为脏组件。因此下次执行 DetectChanges 时它将更新视图。从这一点上我有两个问题。两者都在该组件具有的上下文中changeDetection: ChangeDetectionStrategy.OnPush
1)如果“async pipeline”除了调用markForCheck(源代码)什么都不做,为什么视图会更新?
2)如果我尝试在某个异步进程中调用markForCheck,视图也会更新。
演示:stackblitz
您能帮助我了解这些过程中发生的情况以及视图实际更新的原因吗?我期待有人在 1) 和 2) 之后调用 DetectChanges 方法,但是谁......
我可以执行以下操作:
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, BComponent],
bootstrap: [AppComponent, BComponent] <---------- here two components
})
Run Code Online (Sandbox Code Playgroud)
它将创建两个 html 标签:
<my-app ng-version="2.4.5" _nghost-lii-0=""><h1 _ngcontent-lii-0="">Hello Angular</h1></my-app>
<b-app ng-version="2.4.5" _nghost-lii-0=""><h1 _ngcontent-lii-0="">Hello Angular</h1></b-app>
Run Code Online (Sandbox Code Playgroud)
我想知道这种设置的含义是什么?我是要在一个注入器中拥有两个组件树,还是它们将充当两个不同的应用程序?还有什么我没想过的吗?
我有一个简单的警报组件,该组件正在视图中动态创建。由于它是动态创建的,因此我设置了一个选项,在初始化警报后自动显示该警报。
尽管它可以正常工作,但我想了解为什么在这种特殊情况下为什么必须手动触发更改检测。
码:
export class OverlayMessageComponent implements AfterViewInit {
...
ngAfterViewInit() {
if(this.autoShow) {
this.show();
}
this.changeDetector.detectChanges();
}
...
}
Run Code Online (Sandbox Code Playgroud)
完整样本: https : //plnkr.co/edit/8NvfhDvLVBd71I7DR0kW
我遇到this.changeDetector.detectChanges();以下错误时必须添加:
例外:检查表达式后,表达式已更改。
我的印象是使用AfterViewInit有助于避免该问题,但我认为我认为这是错误的。有没有一种方法可以更好地结构化代码以避免这种错误?
我想更好地理解为什么返回此错误。我之前已经看过几次这个错误,而且我知道有人说使用a setTimeout()或enableProdMode()确实可以解决问题,但是对我来说,当框架本身通知您存在问题时,这似乎是一种错误的解决方法。