[英]observable not updating in template directive
我的组件文件中定义了一个Observable
。 当用双卷曲( {{example}}
)插值时,它会适当更新。 但它并没有在模板指令中更新,即使我使用的是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
}
显然,注释掉第二个async
会使第一个行为适当。
答案既简单又阴暗。 这里有个小提示:
updateIsLoading: any;
isLoading$ = new Observable((observer) => {
console.log("Created!");
observer.next(false);
this.updateIsLoading = function (newValue: boolean) {
observer.next(newValue);
observer.complete();
};
});
Created
将在控制台中记录两次。 所以每次你打电话| async
这个 Observable 上的| async
- 你传递给构造函数的 function 被执行,并且updateIsLoading
被覆盖,所以只有最后一个|async
绑定在工作。
所以如果你想有 2 个async
管道 - 使用Subject
。
isLoading$ = new Subject<boolean>();
updateIsLoading = (value: boolean) => this.isLoading$.next(value);
注意:Subject 中没有初始值,所以在is loading: (value)
中的值为空字符串。
或者您可以使用share()
运算符:
isLoading$ = new Observable((observer) => {
this.updateIsLoading = function (newValue: boolean) {
observer.next(newValue);
observer.complete();
};
}).pipe(share(), startWith(false));
将按“预期”工作。
这只是对async
pipe 和/或 Observables 的误解。
isLoading$ | async
的每个实例 isLoading$ | async
创建一个单独的订阅。
此订阅将执行回调 function,用新的 function 覆盖this.updateIsLoading
。
因此,您的点击处理程序只会在最后一个isLoading$ | async
时触发observer.next(newValue)
isLoading$ | async
订阅。
理想情况下,您只想调用isLoading$ | async
isLoading$ | async
一次并将其放入模板变量中。
不幸的是 Angular 没有内置指令来声明单个模板变量。 尽管您可以自己编写,并且有一些软件包,例如ng-let
。
您可以使用as
将所有内容包装在*ngIf
中以获取模板变量,但如果您希望允许虚假值通过,那将不起作用。
您可以使用ng-template
let-*
语法来完成它。 这个想法是定义一个模板,并通过ngTemplateOutletContext
将您的异步变量作为参数传递。 实际渲染是由ng-container
完成的。
<ng-container
[ngTemplateOutlet]="myAsyncTemplate"
[ngTemplateOutletContext]="{isLoading: isLoading$ | async}"
></ng-container>
<ng-template #myAsyncTemplate let-isLoading="isLoading">
<ng-container *ngIf="isLoading; else elseBlock">
<p>is loading: {{isLoading}}</p>
</ng-container>
<ng-template #elseBlock> Else Block</ng-template>
<p>is loading: {{isLoading}}</p>
<button (click)="handleClick()">SET TO TRUE</button>
</ng-template>
Stackblitz: https://stackblitz.com/edit/angular-x4msbz?file=src/main.html
或者,您可以像 Sergey 建议的那样将 observable 变成共享的 stream 。 这将使您可以进行任意数量的订阅,这些订阅都具有相同的价值。 取决于您的用例。
as
是一个 Angular 关键字,您可以使用该关键字将async
pipe 的结果分配给其他变量。
{{ isLoading$ | async }}
{{ isLoading$ | async }}
现在是模板中的变量isLoading
。
将其用作:
<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>
这可能是一个不受欢迎的答案,但我更喜欢保持模板非常简单,并在组件中放置任何必要的复杂性,其中可以使用 ts/js 的完整表现力。
在这种情况下,我认为在组件中订阅会更容易/更容易理解/更易于维护,并更新一个可以在模板中轻松多次使用的属性而不影响订阅。
恕我直言,异步 pipe 只是不必要地使这里的事情复杂化。
TLDR:保持模板简单,并保持其逻辑简单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.