如果我的预先输入得到的搜索结果为空,则应避免随后进行的任何向下排列的搜索查询。例如。如果搜索“ red ”为空,则搜索“ redcar ”没有任何意义。
我尝试使用pairwise()和scan()运算符。代码段:
import { tap, switchMap, filter, pairwise, scan, map } from 'rxjs/operators';
this.searchForm.get('search').valueChanges
.pipe(
switchMap( queryString => this.backend.search(queryString))
)
.subscribe()
更新 给定一个简化的方案:在后端中只有术语“ apple ”。用户正在键入搜索字符串(该请求不会被switchMap()中止):
不需要后端调用7.和8.,因为6.已经返回了EMPTY。因此,任何后续调用都可以省略。我认为需要一些记忆。
我想防止不必要的后端调用(http)。有什么办法可以在rxjs中实现?
答案 0 :(得分:2)
您可以使用filter运算符:
this.searchForm.get('search').valueChanges.pipe(
filter(query => query)
switchMap(query => this.backend.search(queryString))
)
您可以在这里尝试这种机制:RxJS-Editor
代码共享无效,因此您可以在此处获取代码:
const { of } = Rx;
const { filter } = RxOperators;
of('foo1', 'foo2', undefined, undefined, 'foo3').pipe(
filter(value => value)
)
答案 1 :(得分:1)
您想保留所有失败的搜索并检查是否在调用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)
}
)
}
代码未经测试,但您知道了
答案 2 :(得分:1)
这是一个有趣的用例,是极少数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);
实时演示:https://stackblitz.com/edit/rxjs-do457
请注意,在搜索"apple "
之后,不再有远程呼叫。同样,在3秒钟后,当您尝试搜索其他术语“ app”时,它的确会再次拨打远程电话。