Rxjs 扫描不会触发 combineLatest

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

所以我有第一个 Observable list$ = this.api.getall()
和第二个filteredList$ = combineLatest(this.list$,this.form.valueChanges())
并且,在我的 HTML 中,我使用 filteredList$ 显示 filteredList$ | async 内容。 这工作正常(意思是,表单中的任何更改都会更新我的 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 不起作用。

TOTTI128 回答:Rxjs 扫描不会触发 combineLatest

您可以使用条件强制两者并将它们连接起来;

concat(list$,filteredList$).subscribe(function() {/*do something */});
,

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

  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>
,

类似问题

考虑以下代码:

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 observable,不如以某种方式将您的 list$ 订阅到该 observable。当然,要做到这一点,您需要 list$ 既是可观察的(价值流)又是观察者。

幸运的是,RxJS 附带了一些已经可以做到这一点的构造。您可以阅读一些关于 Subjects 的内容,因为我将在这里使用 ReplaySubject(但也许 Subject 或 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$);

这不是最干净的解决方案,因为每次有更新时您都需要重新创建更新逻辑,但它仍然与您之前拥有的相对接近。

本文链接:https://www.f2er.com/289279.html

大家都在问