angularjs 观察结果未在模板指令中更新

de90aj5v  于 2023-02-03  发布在  Angular
关注(0)|答案(4)|浏览(155)

我在组件文件中定义了一个Observable。当使用双curlys({{example}})进行插值时,它会相应地更新。但是,即使我使用的是async管道,它也不会在template指令中更新。
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会使第一个行为适当。

tuwxkamq

tuwxkamq1#

这只是对async管道和/或可观测量的误解。
isLoading$ | async的每个示例创建一个“单独”的订阅。
此订阅将执行回调函数,用新函数覆盖this.updateIsLoading
所以你的点击处理程序只会为最后一个isLoading$ | async订阅触发observer.next(newValue)
理想情况下,您只需要调用isLoading$ | async一次,并将其放入模板变量中。
不幸的是,Angular没有一个内置的指令来声明一个模板变量,尽管你可以自己编写,也有一些包,比如ng-let
可以用as Package *ngIf中的所有内容以获得一个模板变量,但是如果您想允许falsey值通过,那么这是行不通的。
你可以使用ng-templatelet-*语法来实现它,其思想是定义一个模板,并通过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>

堆栈 lightning 战:https://stackblitz.com/edit/angular-x4msbz?file=src/main.html
或者,你可以像Sergey建议的那样,将可观察性转化为一个共享流,这将允许你进行任意数量的订阅,所有订阅共享相同的值,这取决于你的用例。

m3eecexj

m3eecexj2#

答案有点简单,同时也有点见不得人。这里有一个小提示:

updateIsLoading: any;

isLoading$ = new Observable((observer) => {
  console.log("Created!");
  observer.next(false);

  this.updateIsLoading = function (newValue: boolean) {
    observer.next(newValue);
    observer.complete();
  };
});

Created将在控制台中被记录两次,所以每次在Observable上调用| async时,传递给构造函数的函数都会被执行,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));

会像“预期的”那样工作。
其他详细信息:What is the difference between Observable and a Subject in rxjs?

3ks5zfa0

3ks5zfa03#

as是一个Angular 关键字,使用它可以将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>
qgzx9mmu

qgzx9mmu4#

这可能是一个不受欢迎的答案,但我更喜欢保持模板非常简单,并把任何必要的复杂性放在组件中,在组件中可以获得ts/js的完整表达能力。
在这种情况下,我认为在组件中订阅会更容易/更容易理解/更容易维护,并在不影响订阅的情况下更新一个可以在模板中轻松多次使用的属性。
恕我直言,异步管道只是不必要地复杂化这里的事情。
TLDR:保持模板简单,保持它们的逻辑简单。

相关问题