I have an Observable defined in my component file. It is updating appropriately when interpolated with double curlys ({{example}}). But it is not updating inside the template directive, even though I am using an async pipe.
component.html
<ng-container *ngIf="isLoading$ | async as isLoading; else elseBlock">
is loading
</ng-container>
<ng-template #elseBlock> Add</ng-template> <--- constantly showing elseblock; not working!
is loading: {{ isLoading$ | async }} <--- is working correctly
component.ts
updateIsLoading: any;
isLoading$ = new Observable((observer) => {
observer.next(false);
this.updateIsLoading = function (newValue: boolean) {
observer.next(newValue);
observer.complete();
};
});
handleClick() {
this.updateIsLoading(true); <--- running this line updates interpolated value, but not the if statement
}
Edit
Apparently, commenting out the second async makes the first behave appropriately.
CodePudding user response:
as is a Angular keyword, with which you can assign the result of async pipe to other variables.
{{ isLoading$ | async }} is now a variable isLoading in the template.
Use it as:
<div>
<ng-container *ngIf="isLoading$ | async as isLoading; else elseBlock">
Welp
</ng-container>
<ng-template #elseBlock>
<button type="button" (click)="handleClick()">
Add
</button>
</ng-template>
is loading: {{ isLoading }}
</div>
CodePudding user response:
Answer is a bit simple and shady at the same time. Here is a small hint:
updateIsLoading: any;
isLoading$ = new Observable((observer) => {
console.log("Created!");
observer.next(false);
this.updateIsLoading = function (newValue: boolean) {
observer.next(newValue);
observer.complete();
};
});
Created will be logged twice in the console. So each time you call | async on this Observable - the function you passed to the constructor is executed, and updateIsLoading is overwritten, so only last |async binding is working.
So if you want to have 2 async pipes - use Subject.
isLoading$ = new Subject<boolean>();
updateIsLoading = (value: boolean) => this.isLoading$.next(value);
Note: there is no initial value in Subject, so in the is loading: (value) the value will be empty string.
OR you can use share() operator:
isLoading$ = new Observable((observer) => {
this.updateIsLoading = function (newValue: boolean) {
observer.next(newValue);
observer.complete();
};
}).pipe(share(), startWith(false));
Will work as "expected".
Additional details: What is the difference between Observable and a Subject in rxjs?
CodePudding user response:
This is just a misunderstanding of the async pipe and/or Observables.
Each instance of isLoading$ | async creates a separate subscription.
This subscription will execute the callback function, overwriting this.updateIsLoading with a new function.
So your click handler will only ever fire observer.next(newValue) for the last isLoading$ | async subscription.
