这是我要尝试的操作:我有一个来自异步数据源的用户列表。源返回一个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;
}));
}
但是,我不想要再次解决用户。我只想本地更新异步用户列表。我该怎么办?
答案 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将在所有管道中运行。将它们视为“数据存储”是初始Subject
(this.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()
感到满意,但请自己尝试一下。