I have a simple component which uses a service returning an Observable. Such Observable is used to control the creation of an html element via *ngIf
.
Here the code
@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));
}
}
As you can see, getStuff()
returns and Observable with a delay
and everything works as expected.
Now I remove the delay
and I get the following error on the console
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: null'. Current value: 'ngIf: [object Object]'.
This is a Stackbliz that reproduces the case.
Is this inevitable? Is there a way to avoid this error?
This small example replicates a real world example, where MyService
queries a back end, so the delay.
This is for me relevant because I would like to be able to run tests in a synchronous way simulating the real backend and, when I try such an approach, I get the same result.
The error basically says that one change triggered another change. It happens only without delay(100)
because RxJS is strictly sequential and subscriptions happen synchronously. If you use delay(100)
(even if you use just delay(0)
it makes the emission asynchronous so it happens in another frame and is caught by another change detection cycle.
A very simple way to avoid this is just wrapping assigning to a variable with setTimeout()
or with NgZone.run()
.
Or more "Rx way" is using subscribeOn(async)
operator:
import { asyncScheduler } from 'rxjs';
import { observeOn } from 'rxjs/operators';
...
return of(stuff).pipe(
observeOn(asyncScheduler)
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With