RxJs:可观察搜索结果的模式

问题描述 投票:1回答:1

我的场景是一个带有搜索表单和结果列表的经典网页。我想封装在Observable中加载结果的行为。

这是我目前在TypeScript中所做的事情:

function loadResults(query): Observable<T[]> {}

const results = new Subject<ResultEvent<T[]>>();

const results: Observable<ResultEvent<T[]>> =
  form.valueChanges
    .distinctUntilChanged()
    .do(() => results.next(ResultEvent.pending()))
    .switchMap(query => loadResults(query))
    .subscribe({
      next: (data: T[]) => results.next(ResultEvent.present(data)),
      error: err => results.next(ResultEvent.failed(err)),
    });

这个想法是results总是包含搜索的当前状态:pendingpresentfailed。当查询更改时,结果将设置为pending,并且当服务返回数据时,结果将设置为present

我不喜欢这个解决方案是明确调用subscribe()。我宁愿有一个简单的Observable,可以订阅取消订阅(例如,在Angular中使用async管道),而不创建明确的订阅。在do的副作用似乎也相当hacky。

const results: Obserbable<ResultEvent<T[]>> = 
  form.valueChanges.distinctUntilChanged()
  . /* here be dragons */;

感谢您的任何建议和想法!

rxjs reactivex
1个回答
2
投票

我想你想要这样的东西:

const results$ = form.valueChanges
  // This is up to you, but with user input it might make sense to
  // give it just a little bit of time before we hit the server since
  // most user input will be more than a single character.
  //.debounceTime(100)

  .distinctUntilChanged()

  // Using switchMap guarantees that the inner observable will be
  // cancelled if the input changed while we are still waiting for
  // a result. Newer is always better!
  .switchMap(query => loadResults(query)
    // If we get data, we use it.
    .map(results => ResultEvent.present(results))

    // We catch errors and turn them into a failure event.
    .catch(err => Observable.of(ResultEvent.failed(err)))

    // Whatever happens, first things first.
    .startWith(ResultEvent.pending())
  );

顺便说一句,我也会考虑在那里添加一个debounceTime

这里有一个片段,您可以将其复制粘贴到https://rxviz.com中以查看它的运行情况(不幸的是,它们的共享链接功能不再起作用)。确保将时间窗口设置为10秒。

const ResultEvent = {
  pending: () => 'Pending',
  failed: err => 'Error: ' + err,
  present: data => 'Data: ' + data,
};

const loadResults = query => query === 2
  ? Rx.Observable.of(null).delay(500).switchMap(() => Rx.Observable.throw('Oops'))
  : Rx.Observable.of(42).delay(500)

const input$ = Rx.Observable.timer(0, 2000).take(4);

input$.switchMap(query => loadResults(query)
  .map(data => ResultEvent.present(data))
  .catch(err => Rx.Observable.of(ResultEvent.failed(err)))
  .startWith(ResultEvent.pending())
)
© www.soinside.com 2019 - 2024. All rights reserved.