在不解决先前的运算符的情况下更新可观察值

时间:2018-10-10 13:26:44

标签: javascript angular rxjs observable

这是我要尝试的操作:我有一个来自异步数据源的用户列表。源返回一个Observable<User[]>。然后,通过Angular的async管道将列表显示在模板中。

<ng-template #loading>Loading...</ng-template>
<ul *ngIf="users$ | async; let users; else loading">
  <li *ngFor="let user of users">{{user.name}}</li>
</ul>

可以搜索用户列表。使用反应式,我已经绑定了搜索字段的更改事件,如下所示(this.resolve执行异步任务,签名为(text: string) => Observable<User[]>

search = new FormControl('');
users$ = this.search.valueChanges
  .pipe(startWith(this.search.value))
  .pipe(debounceTime(250))
  .pipe(distinctUntilChanged())
  .pipe(switchMap(this.resolve));

这很好用。搜索输入被反跳,并且当搜索输入发生更改时,将触发一个新的异步请求来解决用户问题。

现在我有问题。用户可能会在客户端进行更改,因此需要在列表中进行更新,无需,而无需再次执行异步任务。我尝试在更新函数中使用另一个管道操作导致this.resolve再次执行。

update(user: User): void {
  this.users$ = this.users$
    .pipe(map(users => {
      // replace user in users
      return users;
    }));
}

但是,我不想要再次解决用户。我只想本地更新异步用户列表。我该怎么办?

2 个答案:

答案 0 :(得分:3)

您可以使用Subject并将其合并到users$之后的switchMap链中。

otherUsers$ = new Subject();

users$ = this.search.valueChanges
  .pipe(
    startWith(this.search.value),
    debounceTime(250)),
    distinctUntilChanged()),
    switchMap(this.resolve),
    merge(otherUsers$),
  );

然后您可以致电next()并避免遍历整个链:

otherUsers$.next([{}, {}, {}, ...]);

答案 1 :(得分:1)

除了马丁的merge()建议之外,我还将“分享”用户结果以防止resolve再次触发。

每次添加订阅时,

RXJS将在所有管道中运行。将它们视为“数据存储”是初始Subjectthis.search.valueChanges)的转换器。因此,每次订阅时,RXJS都会在管道中运行以获取转换后的值。

所以您想看一下RXJS share()shareReplay(1)。这些操作将在内部创建一个new Subject()(对于share())或new ReplaySubject(1)(对于shareReplay(1)),以将转换后的值保存在一个新值中。 “数据存储”,如果可以的话。

search = new FormControl('');
otherUsers$ = new Subject();
users$ = merge(
  otherUsers$,
  this.search.valueChanges.pipe(
    startWith(this.search.value)),
    debounceTime(250)),
    distinctUntilChanged()),
    switchMap(this.resolve))
  )
).pipe(share());

Subject vs ReplaySubject取决于您是否要保留一个值,即使没有订阅也是如此。 (这是我根据自己的用法进行的解释。)我发现shareReplay(1)在有obs.pipe(first()).subscribe(...)这样的订阅的情况下很有用。但是,您的订阅之一是DOM中的AsyncPipe,因此您可能对share()感到满意,但请自己尝试一下。