问题
Note this is simplified question of Angular template binding with Observable async pipe issue
template:
<div>{{foo()$ | async}}</div>
source code:
import { Component } from "@angular/core";
import { BehaviorSubject, of, Observable } from "rxjs";
import { tap, delay, map, switchMap, concatMap } from "rxjs/operators";
@Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
private index = 0;
foo$(): Observable<any> {
console.log("aaa")
return of("Delayed");
}
}
The above code works as expected:
However if I added .pipe(delay(1))
to the foo$()
:
foo$(): Observable<any> {
return of("Delayed").pipe(delay(1));
}
it won't work and keep "aaa" in the console log.
See https://stackblitz.com/edit/angular-qbhkg3
回答1:
A method called from the template, is called every change detection cycle. Because you are using the async
pipe, the change detection is triggered with every emit. So basically you are creating an infinite loop and that's why it will never show a value, because the change detection never finishes:
- on view init template calls
foo$()
foo$()
creates a -new- observable and delays the emit- the observable emits after the delay
- the emit triggers a change detection from within the
async
pipe - change detection calls
foo$()
from the template, and we're back to step 2
It's not really clear what you are trying to achieve, but I don't think you should return Observables
from methods to be consumed in templates. These should be readonly class field:
readonly foo$ = of("Delayed").pipe(delay(1));
It is however possible to use a method, but you have to make sure this method returns the same Observable
:
private readonly _foo$: Observable<any> = of("Delayed").pipe(delay(1));
foo$(): Observable<any> {
console.log('here');
return this._foo$;
}
example
Because the observable object stays the same (===
), it's all good in the hood. Once you add a pipe
to the Observable
you create a new reference and you come back into the infinite loop.
The reason it does not reach the infinite loop if you just return of('delayed')
, is because the Observable
is not asynchronous this way. The Observable will return a value immediately to the async
pipe, and when the async pipe calls detectChanges()
nothing really happens, because it's still in the same cycle as the change detection cycle which triggered the foo$()
template call.
I see you also linked a previous question you posted which involves the use of a decorator. You should change that decorator to a class field decorator, and you can then do the following:
@NeedsElement(sp(115621), ap(116215))
readonly insuredType$!: Observable<string>;
I think I can think of a way to make it work with a method call, but before I dive into that, I first want to know why you want it to be a method call in the first place
来源:https://stackoverflow.com/questions/61772610/template-binding-with-function-return-observable-and-async-pipe