错误:使用同步可观察对象测试角度组件时,发生ExpressionChangedAfterItHasBeenCheckedError

时间:2019-01-31 14:27:38

标签: angular rxjs angular-test

我有一个简单的组件,该组件使用返回Observable的服务。此类Observable用于通过*ngIf控制html元素的创建。

代码在这里

@Component({
  selector: 'hello',
  template: `<h1 *ngIf="stuff$ | async as obj">Hello {{obj.name}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent implements AfterViewInit {
  constructor(
    private myService: MyService,
  ) {}

  stuff$;

  ngAfterViewInit() {
    this.stuff$ = this.myService.getStuff();
  }
}


@Injectable({
  providedIn: 'root'
})
export class MyService {
  getStuff() {
    const stuff = {name: 'I am an object'};
    return of(stuff).pipe(delay(100));
  }
}

如您所见,getStuff()返回并且可以通过delay观察到,一切都按预期进行。

现在,我删除了delay,并在控制台上收到以下错误消息

错误:ExpressionChangedAfterItHasBeenCheckedError:检查表达式后,表达式已更改。上一个值:'ngIf:null'。当前值:“ ngIf:[object Object]”。

This is a Stackbliz that reproduces the case.

这是不可避免的吗?有办法避免这种错误吗? 这个小例子复制了一个真实的例子,其中MyService查询后端,因此延迟。

这对我来说很重要,因为我希望能够以同步方式运行测试以模拟真实的后端,并且当我尝试这种方法时,也会得到相同的结果。

4 个答案:

答案 0 :(得分:3)

该错误基本上表示一个更改触发了另一个更改。它仅在没有delay(100)的情况下发生,因为RxJS严格是顺序的并且订阅是同步发生的。如果您使用delay(100)(即使仅使用delay(0),也会使发射异步,因此它发生在另一帧中并被另一个更改检测周期捕获。

避免这种情况的一种非常简单的方法就是使用setTimeout()NgZone.run()包装分配给变量的变量。

或更多个“ Rx方式”正在使用subscribeOn(async)运算符:

import { asyncScheduler } from 'rxjs';
import { observeOn } from 'rxjs/operators';

...

return of(stuff).pipe(
  observeOn(asyncScheduler)
);

答案 1 :(得分:0)

您要在初始化视图后分配 。试试:

@Input() name: string;
stuff$ = this.myService.getStuff();

或者:

ngOnInit() { // or even in constructor()
    this.stuff$ = this.myService.getStuff();
}

答案 2 :(得分:0)

如果您需要在ngAfterViewInit生命周期挂钩中同步更改模型,则可以触发更改检测以避免ExpressionChangedAfterItHasBeenCheckedError错误:

constructor(private changeDetectorRef: ChangeDetectorRef) { }

ngAfterViewInit() {
  this.stuff$ = this.myService.getStuff();
  this.changeDetectorRef.detectChanges();
}

有关演示,请参见this stackblitz

答案 3 :(得分:-1)

为什么当您尝试从myService中获取东西时,为什么也将东西$作为@Input处理呢?渲染时是一种冲突。

也许ngAfterViewChecked会比ngAfterViewInit更好。但是解决方案仍然不是很好。