0

我正在与 combineLatest 和 scan rxjs 运算符(使用 Angular 8)作战。基本上,我正在显示一个可以过滤的列表。

所以我有第一个 Observablelist$ = this.api.getAll()
和第二个Observable filteredList$ = combineLatest(this.list$, this.form.valueChanges())
,在我的 HTML 中,我filteredList$使用filteredList$ | async. 这很好用(意思是,表单中的任何更改都会更新 my filteredList$

但是,最终用户可以更新列表中的一个元素;当他这样做时,我会以这种方式更新我的list$Observable:

this.api.update(listElement)
    .subscribe((updatedElmt) => {
         this.list$ = this.list$.pipe(
            concatMap(value => of(...value)),
            scan((acc, listElmt) => listElmt._id === updatedElmt._id ? [...acc, updatedElmt] : [...acc, listElmt], []),
        );
    });

这也很有效。
但是,combineLatest不会触发,因此,在我刷新页面之前,我的 UI 永远不会更新。我认为这是因为list$指针没有改变,所以没有理由触发 combineLatest ;但我怎么能强迫它?

顺便说一句,使用 changeDetector 来检测Changes 或 markForCheck 不起作用。

4

3 回答 3

0

你到底想达到什么目的?如果它正在更改列表中的更新元素,那么您应该考虑这样的事情:

  1. 将您的列表定义为主题:

     this.list$ = new Subject<ListType[]>();
    
  2. 获取当前列表和下一个列表$

     this.api.getAll().subscribe(this.list$.next); // dont forget to unsubscribe or youll get a memory leak
    
  3. 观察更新:

    updateList$ = this.api.update(listElement).pipe(
        withLatestFrom(this.currentList$),
        map(([updatedElement, currentList]) => {
            return currentList.map(listElement => listElement._id === udaptedElement._id ? updatedElement : listElement),
        })
    ).subscribe(this.list$.next) // dont forget to unsubscribe (takeUntil()) should work here
    
  4. 将您的 list$ 与 valueChanges 结合起来

    combineLatest([this.list$, this.form.valueChanges()])).pipe(....)
    

this.list$ = ... 为属性设置一个新值,但是 combineLatest 订阅了 this.list$ 的旧值。这将导致内存泄漏。

this.list$ = someObservableFunc$(); // gives first Observable<any>
const observeList$ = combineLatest([this.list$, otherObservable$]); // gives combination of first Observable<any> and otherObservable$
this.list$ = anotherObservableFunc$(); // gives second Observable<any> but observeList$ keeps subscription to first Observable<any>
于 2021-06-08T13:31:03.383 回答
0

您可以使用条件强制并连接它们;

concat(list$, filteredList$).subscribe(function() {/*do something */});
于 2021-06-08T13:01:48.030 回答
0

类似的问题

考虑以下代码:

let a = 1;
const b = 2;
const c = a + b;
a = 10;
console.log(c);

我敢肯定你以前见过这种事情。当然,这会打印 3 ( 1 + 2 ) 而不是 12 ( 10 + 2 )。所以问题是“我如何在这里强制 c 为 12?” 是一个很难回答的问题。

答案可能是完全删除 c。

let a = 1;
const b = 2;
a = 10;
console.log(a + b);

但是,如果您希望以后能够引用 c,这实际上是行不通的。也许您可以 thunk c (使其成为无参数函数)并调用它以获取惰性值。

let a = 1;
const b = 2;
const c = () => a + b;
a = 10;
console.log(c());

这类问题的解决方案与星星一样多,它们都取决于您要完成的工作。没有灵丹妙药。


一个可能的解决方案

你正在使用 RxJS,所以我认为它适合依靠一些机器来实现你的目标。

与其list$成为可观察的 api,不如list$以某种方式订阅您的可观察对象。当然,要做到这一点,您需要list$既是可观察的(价值流)又是观察者。

幸运的是,RxJS 已经提供了一些可以做到这一点的结构。您可以阅读一些关于主题的内容,因为我将在这里使用 ReplaySubject(但也许主题或 BehaviourSubject 更适合这项工作。取决于您的需要)。

// list$ is an observable **and** and observer
const list$ = new ReplaySubject();

// Tell list to observe the api
this.api.getAll().subscribe(this.list$);

// Keep this as-is
filteredList$ = combineLatest(this.list$, this.form.valueChanges())

// To update
combineLatest(this.list$, this.api.update(listElement)).pipe(
  take(1),
  map(([list, updatedElmt]) => list.map(listElmt => 
    listElmt._id === updatedElmt._id ? updatedElmt : listElmt
  ))
).subscribe(this.list$);

这不是最简洁的解决方案,因为您需要在每次更新时重新创建更新逻辑,但它仍然相对接近您之前的内容。

于 2021-06-08T13:47:11.740 回答