Ленивая загрузка данных с субъектом RxJS.Последняя партия не отображается, тема никогда не завершается - PullRequest
0 голосов
/ 05 марта 2019

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

multiselect.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

export interface ProductCategory {
  id: number;
  name: string;
}

@Injectable({
  providedIn: 'root'
})

export class MultiSelectService {

  constructor(private http: HttpClient) { }

  private readonly categorySubject = new BehaviorSubject(undefined);
  readonly categories$ = this.categorySubject.asObservable();

  // URL to web api - mocked. Categories is the object present in the mock server file
  private categoriesUrl = 'api/categories';

  /** GET heroes from the server */
  getCategories(): void {
    this.http.get<Array<ProductCategory>>(this.categoriesUrl, httpOptions)
    .subscribe( data => {
      data.map( category => this.categorySubject.next(category));
      this.categorySubject.subscribe( xyz => console.log(xyz));
    });
  }
}

multiselect.component.ts

import { Component, AfterViewInit } from '@angular/core';
import { zip, Observable, fromEvent } from 'rxjs';
import { MultiSelectService, ProductCategory } from './multiselect.service';
import { bufferCount, startWith, map, scan } from 'rxjs/operators';

@Component({
  selector: 'multiselect',
  templateUrl: './multiselect.component.html',
  styleUrls: ['./multiselect.component.scss']
})
export class MultiselectComponent implements AfterViewInit {

  SLICE_SIZE = 100;

  constructor(private data: MultiSelectService) { }

  ngAfterViewInit() {
    const loadMore$ = fromEvent(document.getElementsByTagName('button')[0], 'click');
    this.data.getCategories(); // loads the data

    zip(
      this.data.categories$.pipe(bufferCount(this.SLICE_SIZE)),
      loadMore$.pipe(startWith(0)),
    ).pipe(
      map(results => results[0]),
      scan((acc, chunk) => [...acc, ...chunk], []),
    ).subscribe({
      next: v => console.log(v),
      complete: () => console.log('complete'),
    });
  }

}

multiselect.component.html

<button>Fetch more results</button>

1) Количество элементов - 429, и я показываю их партиями по 100 элементов.После четырех кликов (4 х 100) последние 29 никогда не показываются.Чего мне не хватает, чтобы отобразить последние 29 элементов?Мне удалось проверить, что субъект выдал все значения.

2) Есть ли лучший способ достижения той же функциональности, которую я здесь разрабатываю?Я сопоставляю (это может быть цикл forEach) массив из-за того, что после запроса данных я получу только один элемент (массив) со всеми элементами (429).Это не позволит мне буферизовать элементы, если я захочу выполнить отложенную загрузку.

3) От меня требуется предоставить начальное значение для поведенияSubject, есть ли способ избежать этого?

Заранее спасибо!

1 Ответ

0 голосов
/ 05 марта 2019

Это выглядит как очень запутанный способ действий

data.map( category => this.categorySubject.next(category));

Эта строка возвращает новый массив той же длины, что и данные, содержащие неопределенные значения в каждом слоте, если вы хотите создать наблюдаемую из массива, товы идете

this.categories$ = from(data);

BehaviorSubject без начального значения является субъектом.

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

Имейте BehaviorSubject с номером в нем, и каждое нажатие клавиши увеличивает номер и использует объединение Последнее, чтобы получить количество элементов, которые вы хотите из массива.

combineLatest(categories$, page$).pipe(map(([categories, page]) => categories.filter((item, index) => index < page * pageSize)))

Это все, что вам действительно нужноделать, где вы начинаете страницу с 1 и увеличиваете ее при каждом нажатии кнопки.

...