繁体   English   中英

可观察的不在模板指令中更新

[英]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));

将按“预期”工作。

附加细节: rxjs 中的 Observable 和 Subject 有什么区别?

这只是对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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM