Как использовать наблюдаемые в случае ввода поля поиска и разбиения на страницы в конечной точке REST-API? - PullRequest
0 голосов
/ 12 января 2019

Я использую Angular 7 и создаю веб-приложение, извлекающее данные из конечной точки REST-API. Пользователи помещают свою строку поиска в общее поле formControl, которое наблюдается. Если значение изменяется в поле ввода, HTTP-запрос отправляется конечной точке API. Наконец я получаю http-ответ как заметное, на которое я могу подписаться. Но результат запроса также может содержать более 100 элементов. Затем конечная точка API отправляет СЛЕДУЮЩУЮ ссылку на вторую страницу и т. Д. (Разбиение на страницы в конечной точке API).

Моя проблема сейчас, я не могу найти правильный способ наблюдать поле ввода поиска и просматривать все СЛЕДУЮЩИЕ страницы, которые я получаю от конечной точки API. Отдельно это работает как шарм.

Кто-нибудь имеет хорошую практику для моего варианта использования?

Мой файл компонента:

export class GeomapComponent implements OnInit {
  searchTerm = new FormControl();
  constructor(private geomapService: GeomapService) { }

  ngOnInit() {
    this.searchTerm.valueChanges.pipe(
      debounceTime(400),
      distinctUntilChanged(),
      switchMap(term => this.geomapService.retrieveApi(term))
    ).subscribe(data => {
      if (data['results']) {
       ......
      }
    }
  }
}

Мой сервисный файл:

export class GeomapService {
  retrieveUrl = new Subject<string>();

  constructor(private http: HttpClient) { }

  public retrieveApi(searchTerm: string): Observable<ApiDataResponse> {
    this.retrieveUrl.next(baseApiUrl + '?q=' + searchTerm);
    return this.retrieveUrl.pipe(
      switchMap(url => this.sendRequest(url)),
    ).subscribe(data => {
      if (data['next']) {
        this.retrieveUrl.next(data['next']);
      }
    });
  }

  public sendRequest(url: string, httpOptions = {}): Observable<ApiDataResponse> {
    return this.http.get<ApiDataResponse>(url);
  }

}

К сожалению, я получаю следующую ошибку:

TS2322: тип «Подписка» не может быть назначен типу «Наблюдаемый». Свойство «_isScalar» отсутствует в типе «Подписка».

UPDATE: Сейчас я немного дальше, потому что я понял, что мне нужно объединить / объединить последовательные входящие наблюдаемые в сервисе (предоставляемые циклом).

  public requestApi(requestUrl: string): Observable<ApiDataResponse> {
    return this.sendRequest(requestUrl).pipe(
      mergeMap(result => {
        if (result['next']) {
          return this.requestApi(result['next']);
        } else {
          return of(result);
        }
      })
    );
  }

Тем не менее я все еще зависаю с правильным оператором комбинации / преобразования, потому что на этом этапе с mergeMap я получаю по подписке только результат последнего ответа. Но я также хочу получить все ответы, полученные ранее и объединенные в одно значение.

Кто-нибудь знает, как должны выглядеть возвраты в операторе?

UPDATE: Обновление кода, проблема с удаленным массивом (ApiDataResponse[] -> ApiDataResponse)

Ответы [ 3 ]

0 голосов
/ 12 января 2019

Итак, если я хорошо понимаю, у вас есть наблюдаемое в поле поиска, и при поиске вы вызываете API для получения результатов. Если результаты> 100, то ваш API отправляет первые 100 результатов и говорит вам сделать еще один запрос на следующие 100, пока результатов больше не будет.

Для меня немного странно, что вам нужно получить ВСЕ результаты сразу, разве нет смысла отправлять 100 первых результатов, чтобы ждать, пока пользователь запросит 100 следующих (например, нажав на " Кнопка «Следующая страница» или путем прокрутки до определенного предела)?

Я бы решил эту проблему, сохранив первоначальные первые 100 результатов в компоненте, который их отображает (вероятно, в BehaviourSubject), и когда пришло время получить следующую часть результатов (например, когда позиция прокрутки достигнет определенной величины). или когда пользователь нажимает на следующую кнопку), я бы тогда запросил следующие результаты, если они есть ...

0 голосов
/ 13 января 2019

Я согласен с @LaetitiaDallinge в том, действительно ли необходимо или уместно запрашивать все данные одновременно. Как правило, вы должны стараться свести к минимуму количество запросов http, которые вы делаете, и получать данные только тогда, когда они действительно необходимы.

Если вам нужно получить все данные одновременно, вы можете попробовать что-то вроде этого:

import { EMPTY } from 'rxjs';
import { expand, reduce, map } from 'rxjs/operators';

// I presumed that you get an object of this type back from your API,
// instead of an array as I don't understand how you can get back an array and then access 
// the property 'next' on that array, as your code implies. 
// (see my comments)
interface ApiDataResponse {
  ...
  next: string,
  results: Things[]
  ...
}

public fetchAllData(searchTerm: string): Observable<Things[]> {
  return this.http.get<ApiDataResponse>(baseApiUrl + '?q=' + searchTerm)
    .pipe(
      // we recursively call the GET requests until there is no further 'next' url
      expand(apiResponse => {
        if (!apiResponse.next) {
          return EMPTY;
        }
        return this.http.get<ApiDataResponse>(apiResponse.next);
      }),
      // we map the api response to the data we actually want to return
      map(apiResponse => apiResponse.results),
      // we reduce the data of all GET requests to a single array
      reduce((accData, data) => accData.concat(data))
    )
}
0 голосов
/ 12 января 2019

Вы не можете вернуть подписку. Либо используйте switchMap для возврата наблюдаемого, либо subsciebe

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...