[英]Rxjs scan does not trigger combineLatest
I am battling with combineLatest and scan rxjs operators (with Angular 8).我正在与 combineLatest 作战并扫描 rxjs 运算符(使用 Angular 8)。 Basically, I am displaying a list that can be filtered.
基本上,我正在显示一个可以过滤的列表。
So I have a first Observable list$ = this.api.getAll()
所以我有一个第一个 Observable
list$ = this.api.getAll()
and a second one filteredList$ = combineLatest(this.list$, this.form.valueChanges())
第二个
filteredList$ = combineLatest(this.list$, this.form.valueChanges())
and, in my HTML, I'm displaying filteredList$
content using filteredList$ | async
并且,在我的 HTML 中,我使用
filteredList$ | async
显示filteredList$
内容。 filteredList$ | async
. filteredList$ | async
。 This works fine (meaning, any change in the form update my filteredList$
)这很好用(意思是,表单中的任何更改都会更新我的
filteredList$
)
However, the end user is able to update one element of the list;但是,最终用户可以更新列表中的一个元素; when he does, I update my
list$
Observable this way:当他这样做时,我会以这种方式更新我的
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], []),
);
});
This also works well.这也很有效。
However, the combineLatest
is not triggered and therefore, my UI is never updated until I refresh my page.但是,不会触发
combineLatest
,因此,在刷新页面之前,我的 UI 永远不会更新。 I assume it is because the list$
pointer has not change, so theere's no reason for the combineLatest to be triggered;我认为这是因为
list$
指针没有改变,所以没有理由触发 combineLatest; but how can I force it?但我怎么能强迫它呢?
BTW, using changeDetector to detectChanges or markForCheck does not work.顺便说一句,使用 changeDetector 来检测Changes 或 markForCheck 不起作用。
You can force both with a conditional and concatenate them;您可以使用条件强制并连接它们;
concat(list$, filteredList$).subscribe(function() {/*do something */});
What exactly is it you want to achieve?你到底想达到什么目的? If it is changing the updated Element in your list then you should consider something like this:
如果它正在更改列表中的更新元素,那么您应该考虑这样的事情:
Define your List as Subject:将您的列表定义为主题:
this.list$ = new Subject<ListType[]>();
Get the current list and next the list$获取当前列表和下一个列表$
this.api.getAll().subscribe(this.list$.next); // dont forget to unsubscribe or youll get a memory leak
Observe for updates:观察更新:
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
Combine your list$ with valueChanges将您的 list$ 与 valueChanges 结合起来
combineLatest([this.list$, this.form.valueChanges()])).pipe(....)
this.list$ =... sets a new value to the property, but combineLatest ist subscribing to the old value of this.list$. this.list$ =... 为属性设置了一个新值,但是 combineLatest 订阅了 this.list$ 的旧值。 Which will result in a memory leak.
这将导致 memory 泄漏。
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>
Consider the following code:考虑以下代码:
let a = 1;
const b = 2;
const c = a + b;
a = 10;
console.log(c);
I'm sure you've seen this sort of thing before.我敢肯定你以前见过这种事情。 Of course, this prints 3 ( 1 + 2 ) and not 12 ( 10 + 2 ).
当然,这会打印 3 ( 1 + 2 ) 而不是 12 ( 10 + 2 )。 So the question "how do I force c to be 12 here?"
所以问题“我如何在这里强制 c 为 12?” is a tough question to answer.
是一个很难回答的问题。
The answer might be to remove c entirely.答案可能是完全删除 c。
let a = 1;
const b = 2;
a = 10;
console.log(a + b);
But if you want to be able to reference c later, this doesn't really work.但是,如果您希望以后能够引用 c,这实际上是行不通的。 Maybe you could thunk c (make it a parameterless function) and invoke it to get a lazy value.
也许你可以 thunk c (使其成为无参数函数)并调用它以获得惰性值。
let a = 1;
const b = 2;
const c = () => a + b;
a = 10;
console.log(c());
These sorts of problems have as many solutions as there are stars and they all depends on what you're trying to accomplish.这类问题的解决方案与星星一样多,它们都取决于您要完成的工作。 There's no silver bullet.
没有灵丹妙药。
You're using RxJS, so I think it fits to lean on some of that machinery in order to accomplish your goal.您正在使用 RxJS,所以我认为它适合依靠其中一些机器来实现您的目标。
Instead of having list$
be an api observable, why not subscribe your list$
to that observable somehow instead.与其让
list$
成为 api 可观察对象,不如让您的list$
以某种方式订阅该可观察对象。 Of course, to do that, you'd need list$
to be both an observable (stream of value) and an observer.当然,要做到这一点,您需要
list$
既是可观察的(价值流)又是观察者。
Fortunately, RxJS comes with a few constructs that do this already.幸运的是,RxJS 带有一些已经做到这一点的结构。 You can read up a bit on Subjects, as I'll use a ReplaySubject here (but perhaps a Subject or BehaviourSubject is better suited to the job. Depends on your needs).
您可以阅读一些关于主题的内容,因为我将在这里使用 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$);
This isn't the cleanest possible solution since you need to re-create your update logic each time an update comes in, but it's still relatively close to what you had before.这不是最简洁的解决方案,因为您需要在每次更新时重新创建更新逻辑,但它仍然相对接近您之前的内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.