#javascript #angular #ecmascript-6 #rxjs #behaviorsubject
#javascript #angular #ecmascript-6 #rxjs #behaviorsubject
Вопрос:
Я откладываю загрузку данных, используя поддельный сервер. Серверная часть возвращает массив. Поскольку я хочу отложить загрузку данных, я буферизую их каждые 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 x 100) последние 29 никогда не отображаются. Чего мне не хватает для отображения последних 29 элементов? Я смог проверить, что объект выдал все значения.
2) Есть ли какой-либо лучший способ достижения той же функциональности, которую я разрабатываю здесь? Я сопоставляю массив (вместо этого это может быть цикл forEach) из-за того, что после запроса данных я получу только один элемент (массив) со всеми элементами (429). Это не позволит мне буферизировать элементы, если я хочу выполнить отложенную загрузку.
3) Я должен предоставить начальное значение для BehaviorSubject, есть ли какой-либо способ избежать этого?
Заранее спасибо!
Ответ №1:
Это кажется очень запутанным способом выполнения действий
data.map( category => this.categorySubject.next(category));
Эта строка возвращает новый массив той же длины, что и данные, содержащие неопределенные значения в каждом слоте, если вы хотите создать наблюдаемый объект из массива, тогда вы идете
this.categories$ = from(data);
Объект BehaviorSubject без начального значения является объектом.
Помимо всего этого, у вас уже есть ваши данные в массиве, нет необходимости создавать из него новый observable.
Создайте BehaviorSubject с числом в нем, и при каждом нажатии клавиши увеличивайте число и используйте combineLatest, чтобы получить из массива нужное количество элементов.
combineLatest(categories$, page$).pipe(map(([categories, page]) => categories.filter((item, index) => index < page * pageSize)))
Это все, что вам действительно нужно сделать, когда вы начинаете страницу с 1 и увеличиваете ее при каждом нажатии кнопки.