Как сбросить оператор сканирования RXJS на основе другого Observable - PullRequest
3 голосов
/ 27 мая 2019

У меня есть компонент, который вызывает событие onScrollEnd при отображении последнего элемента в виртуальном списке. Это событие выполнит новый запрос API для извлечения следующей страницы и объединения их с предыдущими результатами с помощью оператора scan.

Этот компонент также имеет поле поиска, которое вызывает событие onSearch.

Как очистить предыдущие накопленные результаты от оператора scan при запуске события поиска? Или мне нужно реорганизовать мою логику здесь?

const loading$ = new BehaviorSubject(false);
const offset$ = new BehaviorSubject(0);
const search$ = new BehaviorSubject(null);

const options$: Observable<any[]> = merge(offset$, search$).pipe(
  // 1. Start the loading indicator.
  tap(() => loading$.next(true)),
  // 2. Fetch new items based on the offset.
  switchMap(([offset, searchterm]) => userService.getUsers(offset, searchterm)),
  // 3. Stop the loading indicator.
  tap(() => loading$.next(false)),
  // 4. Complete the Observable when there is no 'next' link.
  takeWhile((response) => response.links.next),
  // 5. Map the response.
  map(({ data }) =>
    data.map((user) => ({
      label: user.name,
      value: user.id
    }))
  ),
  // 6. Accumulate the new options with the previous options.
  scan((acc, curr) => {
    // TODO: Dont merge on search$.next 
    return [...acc, ...curr]);
  }
);

// Fetch next page
onScrollEnd: (offset: number) => offset$.next(offset);
// Fetch search results
onSearch: (term) => {
  search$.next(term);
};

Ответы [ 3 ]

1 голос
/ 28 мая 2019

Я думаю, что вы могли бы достичь того, что вы хотите, просто реструктурировав свою цепочку (я опускаю tap вызовов, которые запускают загрузку для простоты):

search$.pipe(
  switchMap(searchterm =>
    concat(
      userService.getUsers(0, searchterm),
      offset$.pipe(concatMap(offset => userService.getUsers(offset, searchterm)))),
    ).pipe(
      map(({ data }) => data.map((user) => ({
        label: user.name,
        value: user.id
      }))),
      scan((acc, curr) => [...acc, ...curr], []),
    ),
  ),
);

Каждое излучение из search$ создаст новую внутреннюю Наблюдаемую со своим собственным scan, которая начнется с пустого аккумулятора.

1 голос
/ 28 мая 2019

Это интересный поток. Размышляя об этом, смещение $ и поиск $ на самом деле представляют собой 2 отдельных потока, но с разной логикой, поэтому их следует объединять в самом конце, а не в начале.

Кроме того, мне кажется, что поиск должен сбросить смещение до 0, и я не вижу этого в текущей логике.

Так вот моя идея:

const offsettedOptions$ = offset$.pipe(
    tap(() => loading$.next(true)),    
    withLatestFrom(search$),
    concatMap(([offset, searchterm]) => userService.getUsers(offset, searchterm)),
    tap(() => loading$.next(false)),
    map(({ data }) =>
    data.map((user) => ({
      label: user.name,
      value: user.id
    })),
    scan((acc, curr) => [...acc, ...curr])
);

const searchedOptions$ = search$.pipe(
    tap(() => loading$.next(true)),
    concatMap(searchTerm => userService.getUsers(0, searchterm)),
    tap(() => loading$.next(false)),
    map(({ data }) =>
    data.map((user) => ({
      label: user.name,
      value: user.id
    })),
);

const options$ = merge(offsettedOptions, searchedOptions);

Посмотрите, работает ли это или будет иметь смысл. Возможно, мне не хватает некоторого контекста.

0 голосов
/ 28 мая 2019

Нашел рабочее решение: я проверяю текущее смещение с помощью withLatestFrom перед оператором scan и сбрасываю аккумулятор при необходимости на основе этого значения.

Демонстрация Stackblitz

...