【问题标题】:How to add element to a list when it's async loaded from service via observable in Angular?如何通过Angular中的observable从服务中异步加载元素时将元素添加到列表中?
【发布时间】:2020-01-15 14:07:18
【问题描述】:

在我的页面中,我有两个 ngFor 对象列表。一个列表是分配的成员,另一个是可分配的成员。两者都由以下模式显示。

<ng-container *ngIf="available$ | async as available">
  <div *ngFor="let item of available">
    <span (click)="assign(item)"</span>{{item.name}}
  </div>
</ng-container>

this.available$ = this.service.getMembers(false)
    .pipe(
      map(__ => ...), ... );

当我单击可分配成员时,我希望它从列表中消失并出现在另一个列表中。我想知道如何在不显式运行 subscribe(...) 并分配给列表的情况下实现这一目标。

一种方法是将内容保存到服务器,然后重新加载数据,但这似乎是一个巨大的矫枉过正。另一种选择是拥有本地的、不可观察的列表并对其进行管理。但是,我有点喜欢 async 管道,并且希望将组件代码保持在最低限度。 (另外,我已经根据页面中的管道设置了整个加载/失败的 GUI,所以重写工作量很大。)

【问题讨论】:

  • 请提供更多代码
  • @devhedgehog 不确定用于什么目的或使用哪个代码。我创建了一个由指南定义的最小示例。或者至少,打算,呵呵。如果我遗漏了一些可以使问题更清楚的内容,请告诉我那是什么,我会尽力提供遗漏的信息。
  • 我同意。如果您可以在 stackblitz 上添加一个最小的复制品,那就是 Konrad,那会很棒

标签: angular typescript rxjs observable


【解决方案1】:

不确定我的问题是否正确,但让我用伪代码试一试

import {merge, of, from, ...} from 'rxjs/operators';

assign(item){
 this.assigned = merge(this.assigned, of(item)); // add to list
 this.available = this.available.pipe(map(x => x.filter(y => y.id != item.id)); //remove from list
}

如果您可能想要实施 Ivan 提供的 combileLatest 解决方案,这里有一些伪代码如何从列表中过滤掉已删除的项目:

this.available$ = this.service.getMembers(false)
    .pipe(
      map(__ => ...),
      combineLatest(newMember.pipe(map(delItem => {action:"del", item = delItem}))
      filter(x => x[action] != 'del')));

这样的?

【讨论】:

  • 我更喜欢你的建议。它看起来很优雅。它不能“按原样”工作,但我们会到达那里。我在我的机器上注意到的是 item 需要用括号括起来(因为它是一个实例并且 of(...) 似乎需要一个数组。在那之后 - 错误消失了。但是,屏幕上的列表没有更新。我想我需要以某种方式戳它,以便通知订阅迭代器有关管道中的新值。对吗?当我订阅结果并打印出来时,不过,它只显示原始元素。
  • @KonradViltersten 很抱歉,我最近没有太多时间了解您的问题。您是否使用 Ivan 的解决方案解决了您的问题?
  • 不用担心。我没有用他的解决方案解决我的问题,因为最后,我意识到这对于我们的业务案例来说是不够的,因为在将数据暴露在屏幕上之前还有额外的过滤和其他规则,我没有提到为了切中要害。尽管如此,他的回答教会了我如何在合适的那一天使用该方法。最后,我相信这就是你要问的,对吧?如果,且仅当,您觉得自己有时间进行投资,我们热烈欢迎您再试一次,但我不会被困住,所以这取决于您。
【解决方案2】:

据我了解,您想要的是将您的可观察服务与您的分配功能的输出结合起来,而不必使用订阅。我建议您在您的班级中创建一个主题$newMember,该主题将发出您因分配功能而拥有的新成员的值。此后,您可以执行类似

的操作
this.available$ = combineLatest(this.service.getMembers(false), newMember$).pipe(map(...));

在演示中,您可以找到以下内容:

    this.repos = combineLatest(
  this.http.get(this.path),
  this.itemObs.asObservable()
).pipe(
  map(([req, action]) => {
    switch (action.operation) {
      case "add": {

        return;
      }
      case "remove": {
        this.newReps = this.newReps.filter(item => item.id !== action.id);
        return this.newReps;
      }
      default: {
        this.newReps = req["items"];
        return this.newReps;
      }
    }
  })
);

并且您的订阅将使用异步管道保持不变。

这是一个 Stackblitz demo

如果您想了解不同的运算符,现在需要查看 rxjs 文档。

merge - 输出 Observable 仅在所有输入 Observable 完成后才完成。您每次都必须重新触发您的请求。

combineLatest - 每当其中一个输入有新值时,将为所有输入发出最新值的投影。

此外,至少有 15 家运营商对行为略有不同的两个或多个流进行这种合并。您只需要确保选择最适合您的情况。

【讨论】:

  • 有趣的提议。只是为了验证,这不会从服务向后端发出新的调用,对吧?它会重用已经加载的值并修改新的值吗?我将如何处理相反的事情? IE。从列表中删除(而不是修改到列表?
  • 很好的答案,竖起大拇指
  • 所以 combineLatest 不会第二次调用你的后端,但是 $newMember observable 需要是 BehaviourSubject 而不是 Subject 因为 combineLatest 只有在两个 observables 都发出初始值后才会触发。我想象甚至比 devhedgehog 更简单的场景,您想要的所有功能(例如添加/删除/编辑)都将在地图中发生,因为您唯一的问题实际上是到达可以使用数组的地步......过滤只是一个选项。
  • @IvanMihaylov 确定发布您的解决方案,我想看看
  • 我也喜欢他的解决方案。但是,由于某种原因,合并不会通过额外的元素增加列表。屏幕上的输出保持不变,订阅分配的 observable 只显示原始元素。
猜你喜欢
  • 1970-01-01
  • 2015-08-01
  • 2020-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
相关资源
最近更新 更多