Search code examples
angularpaginationfilteringngforng-bootstrap

Search working only on the current page in Angular 17


This search function is searching only from the shown page. If I'm on page1, it searches only on page1; Likewise, if I'm on page2, it searches only on page2.

What changes do I need to do to fix this?

Angular HTML:

<div>
  <input type="text" [(ngModel)]="searchQuery" (ngModelChange)="filterPatients()" />
</div>

<div *ngFor="let item of filteredOpdPatients; let count = index">
  <div class="row">
    <div class="col">{{ count + 1 }}</div>
    <div class="col">{{ item.registration.patientRegistrationId }}</div>
    <div class="col">{{ item.registration.firstName }}</div>
    <div class="col">{{ item.date | date: "dd MMM, yyyy '' hh:mm a" }}</div>
  </div>
</div>
<app-pagination [page]=paginationData.page [collectionSize]=paginationData.total [pageSize]=paginationData.limit  (paginateEvent)="paginateTo($event)"></app-pagination>

Angular TS code:

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { HttpParams } from '@angular/common/http';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Opd } from 'src/app/interfaces/opd';
import { Pagination } from 'src/app/interfaces/pagination';
import { CoreService } from 'src/app/services/core.service';
import { DashboardService } from '../../dashboard.service';

@Component({
  selector: 'app-opd-patients',
  templateUrl: './opd-patients.component.html',
  styleUrls: ['./opd-patients.component.css']
})
export class OpdPatientsComponent implements OnInit {
  public opdPatients: Opd[] = [];
  public filteredOpdPatients: Opd[] = [];
  public paginationData: Pagination = {} as Pagination;
  public componentQuery = new HttpParams();
  public searchQuery: string = '';

  constructor(
    private coreService: CoreService,
    private router: Router,
    private dashboardService: DashboardService,
    private ngbModal: NgbModal
  ) {}

  ngOnInit(): void {
    this.getPatients();
  }

  getPatients(): void {
    this.coreService.getOpds(this.componentQuery).subscribe({
      next: (res: any) => {
        this.opdPatients = res.data;
        this.paginationData = res;
        this.filterPatients();
      }
    });
  }

  filterPatients(): void {
    if (!this.searchQuery) {
      this.filteredOpdPatients = [...this.opdPatients];
    } else {
      const query = this.searchQuery.toLowerCase();
      this.filteredOpdPatients = this.opdPatients.filter(patient =>
        patient.registration.patientRegistrationId.toLowerCase().includes(query)
      );
    }
  }

  paginateTo(page: number): void {
    this.componentQuery = this.componentQuery.set('page', page);
    this.getPatients();
  }
}

This is the pagination HTML:

<ngb-pagination (pageChange)="paginateEvent.emit($event)" [collectionSize]="collectionSize" [pageSize]="pageSize" [(page)]="page" aria-label="Custom pagination">
    <ng-template ngbPaginationPrevious>Prev</ng-template>
    <ng-template ngbPaginationNext>Next</ng-template>
</ngb-pagination>

This is the Pagination TS file

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { NgbPaginationConfig, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-pagination',
  standalone: true,
  imports: [NgbPaginationModule],
  templateUrl: './pagination.component.html',
  styleUrl: './pagination.component.css'
})
export class PaginationComponent {
@Input() page:number = 1
@Input() pageSize:number = 0
@Input() collectionSize:number = 0
@Output() paginateEvent = new EventEmitter<number>();

constructor(
  ngbPaginationConfig: NgbPaginationConfig
){
  ngbPaginationConfig.size = 'sm';
}
}

Please note that I'm getting 10 items per page from the backend.


Solution

  • The problem seems to be on the server side, you are fetching a particular page of results and then filtering, hence you are getting this behaviour. To fix this, you should first filter the search string on the server and then get the data by pages.

    I would suggest you discard the angular side filtering and focus on filtering the data on the API side.

    ...
    export class OpdPatientsComponent implements OnInit {
      public opdPatients: Opd[] = [];
      public filteredOpdPatients: Opd[] = [];
      public paginationData: Pagination = {} as Pagination;
      public componentQuery = new HttpParams();
      public searchQuery: string = '';
    
      constructor(
        private coreService: CoreService,
        private router: Router,
        private dashboardService: DashboardService,
        private ngbModal: NgbModal
      ) {}
    
      ngOnInit(): void {
        this.getPatients();
      }
    
      getPatients(): void {
        this.componentQuery.set('searchString', this.searchQuery);
        this.coreService.getOpds(this.componentQuery).subscribe({
          next: (res: any) => {
            this.opdPatients = res.data;
            this.paginationData = res;
            this.filterPatients();
          }
        });
      }
    
      paginateTo(page: number): void {
        this.componentQuery = this.componentQuery.set('page', page);
        this.getPatients();
      }
    }
    

    We can also get rid of the filter code both in TS and HTML:

    <div>
      <input type="text" [(ngModel)]="searchQuery" />
    </div>
    ...