代码之家  ›  专栏  ›  技术社区  ›  Picci

错误:当使用同步观测值测试角度分量时,表达式ChangedTerithasBeenCheckedError

  •  0
  • Picci  · 技术社区  · 6 年前

    我有一个简单的组件,它使用一个返回可观察到的服务。这种可观察性用于通过以下方式控制html元素的创建: *ngIf .

    @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

    现在我删除 延迟 我在控制台上得到以下错误

    错误:ExpressionChangedTerrithasBeenCheckedError:表达式在检查后已更改。上一个值:“ngIf:null”。当前值:“ngIf:[对象]”。

    This is a Stackbliz that reproduces the case.

    这个小示例复制了一个真实世界的示例,其中 MyService

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

    4 回复  |  直到 6 年前
        1
  •  9
  •   martin    6 年前

    这个错误基本上是说一个变化触发了另一个变化。它只发生在没有 delay(100) 因为RxJS是严格顺序的,订阅是同步进行的。如果你使用 延迟(100) delay(0) 它使发射异步,因此它发生在另一帧中,并被另一个更改检测周期捕获。

    避免这种情况的一种非常简单的方法是使用 setTimeout() 或与 NgZone.run()

    subscribeOn(async) 接线员:

    import { asyncScheduler } from 'rxjs';
    import { observeOn } from 'rxjs/operators';
    
    ...
    
    return of(stuff).pipe(
      observeOn(asyncScheduler)
    );
    
        2
  •  2
  •   ConnorsFan    6 年前

    ngAfterViewInit 生命周期挂钩,您可以触发更改检测以避免 ExpressionChangedAfterItHasBeenCheckedError 错误:

    constructor(private changeDetectorRef: ChangeDetectorRef) { }
    
    ngAfterViewInit() {
      this.stuff$ = this.myService.getStuff();
      this.changeDetectorRef.detectChanges();
    }
    

    看见 this stackblitz

        3
  •  0
  •   Harun Yilmaz    6 年前

    您正在分配您的可观察对象 之后

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

    ngOnInit() { // or even in constructor()
        this.stuff$ = this.myService.getStuff();
    }
    
        4
  •  -1
  •   Gundelino85    6 年前

    当你试图从myService获取$Input时,为什么还要将其作为@Input处理?这是渲染时的一种冲突。

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

    推荐文章