Angular *ngIf 变量与异步管道多个条件

Angular *ngIf variable with async pipe multiple conditions

在 Angular 中有关于使用 *ngIf 的很好的文档:https://angular.io/api/common/NgIf 但是,是否可以使用 *ngIf async 变量并对其进行多次检查? 类似于:

<div *ngIf="users$ | async as users && users.length > 1">
...
</div>

当然可以使用嵌套的*ngIf,比如:

<div *ngIf="users$ | async as users">
    <ng-container *ngIf="users.length > 1">
    ...
    </ng-container>
</div>

但如果只使用一个容器,而不是两个,那就太好了。

你可以这样做:

<ng-template ngFor let-user [ngForOf]="users$ | async" *ngIf="(users$ | async)?.length > 1 && (users$ | async)?.length < 5">
  <div>{{ user | json }}</div>
</ng-template>

请记住,当使用来自 http-request 的订阅时,这会触发请求两次。因此,您应该使用一些 state-management 模式或库,以免发生这种情况。

这里是stackblitz.

我遇到了同样的问题,需要一个 *ngIf + async 变量进行多次检查。

这对我来说效果很好。

<div *ngIf="(users$ | async)?.length > 0 && (users$ | async) as users"> ... </div>

或者如果您愿意

<div *ngIf="(users$ | async)?.length > 0 && (users$ | async); let users"> ... </div>

说明

由于 if 表达式的结果被分配给您指定的局部变量,只需以 ... && (users$ | async) as users 结束您的检查允许您指定多个条件 指定什么当所有条件都成功时,您希望局部变量保留的值。

备注

我最初担心在同一个表达式中使用多个 async 管道可能会创建多个订阅,但经过一些简单的测试(我可能是错的)似乎实际上只进行了一个订阅。

这是替代版本,模板更简洁:

<ng-template [ngIf]="(users$ | async)?.length > 1" [ngIfElse]="noUsersMessage">
  <div *ngFor="let user of (users$ | async)">{{ user | json }}</div>
</ng-template>

<ng-template #noUsersMessage>No users found</ng-template>

请注意,我们使用了 users$ | async 2 次。如果您将 shareReplay() 运算符添加到 user$ Observable:

,这将起作用
  public users$: Observable<any[]> = this.someService.getUsers()
    .pipe(shareReplay());

这样,内部模板将能够访问 Observable 的最后一个值并显示结果。

您可以在 stackblitz 上试用。

我看到每个人都在一个标签中一起使用 *ngFor 和 *ngIf 以及类似的解决方法,但我认为这是一种反模式。在大多数常规编码语言中,您不会在同一行上执行 if 语句和 for 循环,对吗?恕我直言,如果您因为他们特别不希望您这样做而不得不变通,那么您不应该那样做。 不练不练"best practice."

✅✅✅ 保持简单,如果您不需要声明来自 users$ | async 的 $implicit 值作为用户:

<!-- readability is your friend -->
<div *ngIf="(users$ | async).length > 1"> ... </div>

但如果确实需要声明as user,请将其包装起来。


✅ 对于复杂的用例; 请注意,生成的标记甚至不会有 HTML 标签,这就是 ng-templates 和 ng-containers 的优点!

@alsami 接受的、经过编辑的答案有效,因为带星号的 *ngFor 是带有 ngFor(无星号)的 ng-template 的 shorthand,更不用说双异步管道 .当您将无星号 ngFor 与 *ngIf 结合使用时,代码确实有不一致的味道;你明白了要点。 *ngIf 优先,那么为什么不将它包装在 ng-container/ng-template 中呢?它不会在生成的标记中使用 HTML 标记来包装您的内部元素。

<div *ngIf="users$ | async as prods; then thenB; else elseB"></div>

<ng-template #thenB>
  <!-- inner logic -->
  <div *ngIf="prods?.length > 1 && users?.length < 5; else noMatchB">
    Loaded and inbound
  </div>
  <ng-template #noMatchB>Loaded and out of bound</ng-template>
</ng-template>

<ng-template #elseB>Content to render when condition is false.</ng-template>

❌ 不要这样做

<!-- Big NO NO -->
<div *ngIf="(users$ | async)?.length > 1 && (users$ | async)?.length < 5"> ... </div>

了解管道对生命周期的敏感性后,管道有点令人生畏;他们疯狂地更新。这就是 Angular 没有 SortPipe 或 FilterPipe 的原因。所以,呃,不要这样做;你基本上是在创建 2 个 Observable,它们有时会疯狂更新,如果我是正确的话,可能会导致其子项的长期数据不匹配。

<div class="mb-5 mt-5 mat-typography text-center">
    <div *ngIf="(applications$ | async) as applications; else noApplication">
      <div *ngIf="applications != null && applications.length > 0; else noApplication">
        show all job applications
      </div>
    </div>
  <ng-template #noApplication>
    <h3>You haven't applied to any jobs. </h3>
  </ng-template>
</div>
<div *ngIf="(users$ | async)?.length" >
    .....
    .....
    .....
    .....
</div>