После просмотра и чтения ряда руководств у меня есть решение, которое позволяет мне иметь список продуктов на странице и ограничивает отображаемое количество, увеличиваясь по мере прокрутки пользователя, чтобы предотвратить чрезмерное чтение. Это работает, как ожидалось. "поисковый запрос". Опять же, эта функция работает ... но только когда она не сочетается с бесконечной прокруткой.
Моя проблема в том, что я изо всех сил пытаюсь найти правильный способ объединить функциональность поиска и бесконечной прокрутки.
В настоящее время я думаю, что часть моего запроса startAfter работает некорректно, однако я не уверен на 100%. Кажется, что Observable, который я использую для отображения продуктов на моей веб-странице, не сбрасывается при выполнении поискового запроса.
TypeScript:
export class ProductFeedComponent implements OnInit {
@ViewChild(CdkVirtualScrollViewport, {static: false})
viewport: CdkVirtualScrollViewport;
//detects the end of the product list
theEnd = false;
offset = new BehaviorSubject(null);
infinite: Observable<any[]>;
searchText = '';
isHandset$: Observable<boolean> = this.breakpointObserver.observe([Breakpoints.Handset])
.pipe(
map(result => result.matches),
shareReplay()
);
constructor(private seo: SeoService, private db: AngularFirestore, private notepad: NotepadsComponent, public dialog: MatDialog, private breakpointObserver: BreakpointObserver) {
this.searchProducts();
}
regularDistribution = 100 / 4 + '%';
searchQuery: string;
ngOnInit() {
this.seo.generateTags({
title: 'Product Feed',
description: 'A catalogue filled with products.'
});
}
openDialog(productName: string){
this.dialog.open(AddToPadComponent,{
data: {productData: productName}
});
}
nextBatch(e, offset){
//checks whether this is the end of the list
if (this.theEnd)
{
return;
}
const end = this.viewport.getRenderedRange().end;
const total = this.viewport.getDataLength();
console.log(this.viewport.getDataLength());
if (end === total)
{
this.offset.next(offset);
}
}
searchProducts(){
const batchMap = this.offset.pipe(
throttleTime(500), mergeMap(n => this.getBatch(n)), scan((acc, batch) => {
return {...acc, ...batch};
}, {})
);
this.infinite = batchMap.pipe(map(v => Object.values(v)));
}
trackByIndex(i){
return i;
}
getBatch(lastSeen: string){
console.log('firing search...')
console.log(lastSeen)
const search = (<HTMLInputElement>document.getElementById("productSearch")).value;
console.log(search);
return this.db.collection('products', ref => ref.where('keywords', 'array-contains', search).orderBy('productName').startAfter(lastSeen).limit(batchSize))
.snapshotChanges().pipe(tap(arr => (arr.length ? null : (this.theEnd = true))),map(arr => {
return arr.reduce((acc, cur) => {
const id = cur.payload.doc.id;
console.log(id);
const data = cur.payload.doc.data();
console.log(data);
return {...acc, [id]: {id, data}};
}, {});
})
);
}
}
HTML:
<input (keyup)="searchProducts()" id="productSearch" placeholder="search products...">
<ng-container *ngIf="infinite | async as products">
<cdk-virtual-scroll-viewport itemSize="100" (scrolledIndexChange)="nextBatch($event, (products[products.length - 1].data.productName))">
<div class="product-row" fxLayout="row wrap" fxLayout.xs="column wrap" fxFlexFill fxLayoutAlign="center center">
<div fxFlex.gt-xs="50%" fxFlex.gt-md="30%" *cdkVirtualFor="let p of products; let i = index; trackByIndex">
<mat-card class="product-tile">
<img class="product-thumbnail" src= "{{ p.data.thumbnail }}" [routerLink]="p.id">
<div class="product-brand">
{{ p.id }}
</div>
<div class="product-details">
<span id="product-name">{{ p.data.productName }}</span> <span id="product-price">{{ p.data.price }}</span>
</div>
<div class="add-to-pad-button">
<button (click)="openDialog(p.id)" mat-raised-button color="accent">Add to Pad</button>
<a href="{{ p.data.link }}" class="mat-raised-button color=accent">Click Me</a>
</div>
</mat-card>
</div>
</div>
</cdk-virtual-scroll-viewport>
</ng-container>
Я убежден, что упускаю что-то очевидное. Я знаю, что функция работает правильно, когда пользователь выполняет поиск, однако я получаю сообщение об ошибке «Не удается прочитать данные свойства undefined в Object.eval [as handleEvent]», поэтому что-то не так с моим запросом, который не происходит, когда ничего не ищется (например, когда страница изначально загружается).
Рабочий пример: https://www.padder.co.uk/catalogue
два продукта имеют доступные для поиска значения ' синий »для одного и« фиолетовый »для другого. В том же массиве ключевых слов, который используется для получения продуктов, есть значение, которое должно позволять показывать все продукты, когда в поле поиска не введено значение, однако это не работает должным образом.