AngularFire2中Firestore无限滚动分页过滤器失效问题求助
It looks like the main issue here is that your filter logic isn’t integrated with the infinite scroll pagination—right now, switching filters replaces your observable with a non-paginated stream, which breaks the infinite scroll behavior. Let’s fix this by combining the filter and pagination logic properly, and also address a potential type mismatch in your filter parameter.
Step 1: Fix the Filter Parameter Mismatch
First, there’s a mismatch between what you’re passing to your filter function and what Firestore expects. You’re sending categoria.nombre (a string) to filtrarData, but the method is typed to accept a Categoria object. If your Firestore documents store the category as a string (the nombre), this comparison will fail. Let’s correct that:
Component.html:
<button *ngFor="let categoria of categoriasTest | async" (click)="filtrarData(categoria.nombre)" class="btn btn-outline-primary btn-sm mx-1"> {{ categoria.nombre }} </button>
Component.ts:
filtrarData(categoriaNombre: string) { this.selectedCategoria = categoriaNombre; // Store the string value this.resetAndLoadFilteredData(categoriaNombre); }
Step 2: Integrate Filter with Pagination in the Service
We need to modify your service to handle both filtering and pagination. We’ll track the active filter and last loaded document, then build queries that include both.
Service.ts:
import { AngularFirestore, QueryDocumentSnapshot } from '@angular/fire/firestore'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class AvisosService { private pageSize = 10; // Adjust based on your needs private lastDoc: QueryDocumentSnapshot<any> | null = null; private activeFilter: string | null = null; constructor(private afs: AngularFirestore) {} // Set active filter and reset pagination setFilter(categoria: string | null) { this.activeFilter = categoria; this.lastDoc = null; // Reset to load first page of filtered results } // Load next page of results (with active filter) async loadNextPage(): Promise<any[]> { let query = this.afs.collection('avisosTest').ref; // Apply active filter if present if (this.activeFilter) { query = query.where('categoria', '==', this.activeFilter); } // Add orderBy (required for consistent pagination in Firestore) // Replace 'createdAt' with your preferred sort field (e.g., 'id') query = query.orderBy('createdAt', 'desc').limit(this.pageSize); // Add pagination cursor if we have a last document if (this.lastDoc) { query = query.startAfter(this.lastDoc); } const snapshot = await query.get(); // Update lastDoc if we have results if (snapshot.docs.length > 0) { this.lastDoc = snapshot.docs[snapshot.docs.length - 1]; } return snapshot.docs.map(doc => doc.data()); } }
Step 3: Update Component to Manage Paginated Results
Your component will now track accumulated results, reset them when filters change, and trigger load more on scroll.
Component.ts:
import { Component } from '@angular/core'; import { AvisosService } from './avisos.service'; @Component({ selector: 'app-your-component', templateUrl: './your-component.html' }) export class YourComponent { filteredAvisos: any[] = []; selectedCategoria: string | null = null; isLoading = false; constructor(private fs: AvisosService) {} async resetAndLoadFilteredData(categoriaNombre: string) { this.isLoading = true; this.fs.setFilter(categoriaNombre); this.filteredAvisos = []; // Clear existing results const firstPage = await this.fs.loadNextPage(); this.filteredAvisos.push(...firstPage); this.isLoading = false; } async loadMore() { if (this.isLoading) return; // Prevent duplicate requests this.isLoading = true; const nextPage = await this.fs.loadNextPage(); this.filteredAvisos.push(...nextPage); this.isLoading = false; } onScroll(event: Event) { const container = event.target as HTMLElement; // Trigger load more when 100px from the bottom if (container.scrollTop + container.clientHeight >= container.scrollHeight - 100) { this.loadMore(); } } }
Component.html:
<!-- Filter buttons --> <button *ngFor="let categoria of categoriasTest | async" (click)="resetAndLoadFilteredData(categoria.nombre)" class="btn btn-outline-primary btn-sm mx-1" [class.active]="selectedCategoria === categoria.nombre"> {{ categoria.nombre }} </button> <!-- Infinite scroll container --> <div class="avisos-list" (scroll)="onScroll($event)" style="overflow-y: auto; height: 500px;"> <div *ngFor="let aviso of filteredAvisos" class="aviso-item"> <!-- Display your aviso data here --> <h3>{{ aviso.titulo }}</h3> <p>{{ aviso.descripcion }}</p> </div> <div *ngIf="isLoading" class="text-center">Loading more...</div> </div>
Key Notes:
- Firestore Indexes: If you get an error about missing composite indexes, Firestore will provide a link to create the required index automatically (this is needed when combining
whereandorderByclauses). - Consistent Pagination: Always include an
orderByclause when usingstartAfter—this ensures Firestore returns consistent pages. - Loading State: The
isLoadingflag prevents multiple simultaneous load requests when the user scrolls quickly.
This approach keeps your filter and pagination logic tightly integrated, so switching filters will reset the pagination and load the first page of the filtered results, while allowing infinite scroll to work with the active filter.
内容的提问来源于stack exchange,提问作者Paco Zevallos

