You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 9中搜索框与ngx-infinite-scroll联动失效问题

问题:Angular搜索框与ngx-infinite-scroll联动失效

使用Angular 9 + ngx-infinite-scroll组件实现搜索框与无限滚动联动,空搜索时无限滚动正常,可滚动触发loadMoreData()加载更多数据;但输入搜索内容后,scrolled事件及loadMoreData()均不触发,清空搜索框后问题仍存在。


组件代码

TypeScript代码

export class TaskMarketComponent implements OnInit, OnDestroy  {
  @ViewChild('searchInput') searchInput!: ElementRef;
  @ViewChild('lastTaskCard', { read: ElementRef }) lastTaskCard!: ElementRef;

  public taskList: TaskDto[] = [];
  public filteredTaskList: TaskDto[] = [];
  public tasksLength: number;
  
  private isNewSearchQuery = false;
  private searchQuery$ = new Subject<string>();
  private pageIndex = 1;
  private pageSize: number;
  private hasMoreData = true;
  private lastTaskId: number | null = null;

  searchQuery: string = '';

  constructor(private taskService: TaskService) {}

  ngOnInit(): void {
    this.calculatePageSize();
    this.loadTasksWithSearchResult(this.searchQuery).subscribe(r => {
      this.processTask(r);
    })
    this.searchQuery$
    .pipe(
      debounceTime(300),
      distinctUntilChanged(),
      switchMap((searchQuery) => {
        this.pageIndex = 1;
        return this.loadTasksWithSearchResult(searchQuery);
      })
    )
    .subscribe((task) => {
      this.taskList = [];
      this.processTask(task);
      this.setFocusOnSearchInput();
    });
  }

  ngOnDestroy(): void {
    this.searchQuery$.unsubscribe();
  }

  searchTaskList(): void {
    this.pageIndex = 1;
    this.taskList = [];
    this.isNewSearchQuery = true
    this.searchQuery$.next(this.searchQuery);
  }
  
  private loadTasksWithSearchResult(searchQuery?: string): Observable<TaskListDto> {
    const filters = this.fillTasksFilters(searchQuery);
    return this.taskService.getTasks(filters);
  }

  private fillTasksFilters(searchQuery?: string): TaskFilterDto {
    return {
      filters: {
        taskId: null,
        recipientFullName: '',
        name: '',
        name: '',
        createdDate: '',
        executionDate: '',
        status: ''
      },
      pagination: {
        pageNumber: this.pageIndex,
        pageSize: this.pageSize
      },
      sort: {
        column: '',
        asc: true
      },
      searchQuery: searchQuery ?? this.searchQuery
    };
  }

  private setFocusOnSearchInput(): void {
    setTimeout(() => {
      if (this.searchInput) {
        this.searchInput.nativeElement.focus();
      }
    }, 0);
  }

  private processTask(tasks: TaskListDto): void {
    tasks.data.forEach((task) => {
      task.TaskId = task.TaskId;
    });

    if (tasks.data.length <= 0) {
      this.hasMoreData = false;
    }

    if (this.searchQuery) {
      this.filteredTaskList = tasks.data;
    } else {
      this.taskList = this.taskList.concat(tasks.data);
      this.filteredTaskList = this.taskList;
    }
  
    this.tasksLength = tasks.totalElements;

    const lastTask = tasks.data[tasks.data.length - 1];
    this.lastTaskId = lastTask ? lastTask.TaskId : null;

    if (this.lastTaskId && !this.isNewSearchQuery) {
      const lastTaskCard = document.getElementById(`taskCard_${this.lastTaskId}`);
      if (lastTaskCard) {
        lastTaskCard.scrollIntoView({ behavior: 'smooth' });
      }
    }
    this.isNewSearchQuery = false;
    this.pageIndex++;
  }

  private calculatePageSize(): void {
    const screenHeight = window.innerHeight;
    this.pageSize = Math.ceil(screenHeight / 200);
  }

  loadMoreData(): void {
    if (this.hasMoreData) {
      this.loadTasksWithSearchResult(this.searchQuery)
        .subscribe((task) => {
          this.processTask(task);
        });
    }
  }
}

HTML代码

<div class="tab-list-content">
  <div class="row justify-content-between align-items-center table-title-row">
    <div class="col-md-2 col-12 search-bar">
      <input type="text"
             #searchInput
             placeholder="Search"
             class="form-control"
             [(ngModel)]="searchQuery"
             (ngModelChange)="searchTaskList()"/>
    </div>
  </div>
  <div #scrollContainer class="scroll-container" infinite-scroll (scrolled)="loadMoreData()">
    <div *ngFor="let task of filteredTaskList; let last = last" class="card" [attr.id]="'taskCard_' + task.TaskId" #lastTaskCard >
      <div class="row card-body">
        <!-- some card body -->
      </div>
    </div>
  </div>
</div>

解决方案

1. 重置搜索相关状态

每次发起搜索(包括清空搜索)时,重置加载状态、清空结果列表,确保新查询能重新触发滚动逻辑:

searchTaskList(): void {
  this.pageIndex = 1;
  this.taskList = [];
  this.filteredTaskList = []; // 清空搜索结果列表
  this.hasMoreData = true; // 重置加载状态
  this.lastTaskId = null; // 重置最后任务ID
  this.isNewSearchQuery = true;
  this.searchQuery$.next(this.searchQuery);
}

2. 修正搜索结果累加逻辑

搜索时不再直接覆盖filteredTaskList,改为累加数据,保证列表高度足够触发滚动事件:

private processTask(tasks: TaskListDto): void {
  tasks.data.forEach((task) => {
    task.TaskId = task.TaskId;
  });

  if (tasks.data.length <= 0) {
    this.hasMoreData = false;
  }

  // 修改搜索结果赋值逻辑,支持累加
  if (this.searchQuery) {
    this.filteredTaskList = [...this.filteredTaskList, ...tasks.data];
  } else {
    this.taskList = [...this.taskList, ...tasks.data];
    this.filteredTaskList = this.taskList;
  }

  this.tasksLength = tasks.totalElements;

  const lastTask = tasks.data[tasks.data.length - 1];
  this.lastTaskId = lastTask ? lastTask.TaskId : null;

  if (this.lastTaskId && !this.isNewSearchQuery) {
    const lastTaskCard = document.getElementById(`taskCard_${this.lastTaskId}`);
    if (lastTaskCard) {
      lastTaskCard.scrollIntoView({ behavior: 'smooth' });
    }
  }
  this.isNewSearchQuery = false;
  this.pageIndex++;
}

3. 确保滚动容器样式正确

给.scroll-container添加固定高度和滚动属性,保证滚动事件能正常触发:

.scroll-container {
  max-height: calc(100vh - 180px); /* 根据页面布局调整高度 */
  overflow-y: auto;
}

4. 优化搜索管道内的状态重置(可选)

在搜索流管道中提前重置状态,避免重复逻辑:

this.searchQuery$
.pipe(
  debounceTime(300),
  distinctUntilChanged(),
  switchMap((searchQuery) => {
    this.pageIndex = 1;
    this.filteredTaskList = [];
    this.hasMoreData = true;
    return this.loadTasksWithSearchResult(searchQuery);
  })
)
.subscribe((task) => {
  this.taskList = [];
  this.processTask(task);
  this.setFocusOnSearchInput();
});

内容的提问来源于stack exchange,提问作者star213

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 06:16:04