Rxjs 防止基于先前发出的值执行
如果my typeahead获得的搜索结果为空,则应防止任何后续查询使用向下搜索查询。例如,如果搜索“red”返回空,则搜索“redcar”没有意义 我尝试使用pairwise()和scan()运算符。代码段:Rxjs 防止基于先前发出的值执行,rxjs,Rxjs,如果my typeahead获得的搜索结果为空,则应防止任何后续查询使用向下搜索查询。例如,如果搜索“red”返回空,则搜索“redcar”没有意义 我尝试使用pairwise()和scan()运算符。代码段: import { tap, switchMap, filter, pairwise, scan, map } from 'rxjs/operators'; this.searchForm.get('search').valueChanges .pipe( switchMap
import { tap, switchMap, filter, pairwise, scan, map } from 'rxjs/operators';
this.searchForm.get('search').valueChanges
.pipe(
switchMap( queryString => this.backend.search(queryString))
)
.subscribe()
更新
给出一个简化的场景:后端只有“苹果”一词。用户正在键入搜索字符串(switchMap()不会中止请求):
this.searchForm.get('search').valueChanges.pipe(
过滤器(查询=>query)
switchMap(查询=>this.backend.search(查询字符串))
)
您可以在此处尝试此机制:
代码共享不起作用,因此您可以在此处获取代码:
const{of}=Rx;
常量{filter}=RX运算符;
of('foo1','foo2',未定义,未定义,'foo3')。管道(
过滤器(值=>value)
)
听起来您希望保留所有失败的搜索,并检查如果调用HTTP,当前搜索是否也会失败。我想不出任何一种优雅的方式可以把它放在一条流中,但有两条流:
_failedStreams = new Subject();
failedStreams$ = _failedStreams.asObservable().pipe(
scan((acc, curr) => [...acc, curr], []),
startWith('')
);
this.searchForm.get('search').valueChanges
.pipe(
withLatestFrom(failedStreams$),
switchMap([queryString, failedQueries] => {
return iif(() => failedQueries.find(failed => failed.startsWith(queryString)) ?
of('Not found') :
callBackend(queryString);
)
}
)
.subscribe()
callBackend(queryString) {
this.backend.search(queryString)).pipe(
.catchError(err => if(error.status===404) {
this._failedStreams.next(queryString);
// do something with error stream, for ex:
throwError(error.status)
}
)
}
代码没有经过测试,但您得到了这样的想法这是一个有趣的用例,也是少数情况下
mergeScan
有用的情况之一
基本上,您希望记住上一个搜索词和上一个远程调用结果,并根据它们的组合来决定是应该进行另一个远程调用还是只返回EMPTY
import { of, EMPTY, Subject, forkJoin } from 'rxjs';
import { mergeScan, tap, filter, map } from 'rxjs/operators';
const source$ = new Subject();
// Returns ['apple'] only when the entire search string is contained inside the word "apple".
// 'apple'.indexOf('app') returns 0
// 'apple'.indexOf('apple ap') returns -1
const makeRemoteCall = (str: string) =>
of('apple'.indexOf(str) === 0 ? ['apple'] : []).pipe(
tap(results => console.log(`remote returns`, results)),
);
source$
.pipe(
tap(value => console.log(`searching "${value}""`)),
mergeScan(([acc, previousValue], value: string) => {
// console.log(acc, previousValue, value);
return (acc === null || acc.length > 0 || previousValue.length > value.length)
? forkJoin([makeRemoteCall(value), of(value)]) // Make remote call and remember the previous search term
: EMPTY;
}, [null, '']),
map(acc => acc[0]), // Get only the array of responses without the previous search term
filter(results => results.length > 0), // Ignore responses that didn't find any results
)
.subscribe(results => console.log('results', results));
source$.next('a');
source$.next('ap');
source$.next('app');
source$.next('appl');
source$.next('apple');
source$.next('apple ');
source$.next('apple p');
source$.next('apple pi');
source$.next('apple pie');
setTimeout(() => source$.next('app'), 3000);
setTimeout(() => source$.next('appl'), 4000);
现场演示:
请注意,在搜索
“apple”
后,将不再有远程呼叫。另外,在3秒钟后,当你尝试搜索另一个术语“app”时,它会再次进行远程调用。skipWhile(val=>val=='red')感谢您的更新。是否只想在返回空时阻止呼叫?因为它的措辞方式,不仅7,8是不必要的,而且2(也许3)到5也是不必要的。我就把它放在这里,虽然它并不完全符合您的要求,但这通常是人们实现类似功能的方式,可能值得一看,谢谢,但我不认为过滤器是合适的解决方案。我更新了这个问题来澄清一下。这正是我想要实现的。我尝试一下,明天提供一些反馈。嗨,朱利叶斯,虽然你的解决方案很有效,但我认为Acceped答案中的mergeScan()是以更rxjs的方式解决问题的“缺失环节”。是的,这是最好的答案+1。但我有一种不好的感觉,你的一位同事可能会开枪自杀。有些解决方案对于现实世界来说太聪明了。也许在rxjs中比在其他地方更多。我记住了这一点。