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

Angular 6中多个Mat-Select滚动到底部触发事件的优化实现问题

优化实现方案

核心思路

抽离所有下拉的通用逻辑,通过统一配置管理不同下拉的专属状态,同时新增请求锁解决重复触发接口的问题,全程无需引入外部依赖。

具体实现步骤

1. 定义通用配置结构存储各下拉状态

无需为每个下拉单独定义数据源、分页、加载状态变量,统一用映射对象管理:

// 定义下拉统一配置结构
interface SelectConfig {
  dataList: any[]; // 下拉选项数据源
  fetchApi: (pageParam: any) => Observable<any>; // 对应下拉的分页请求方法
  loading: boolean; // 防止重复请求的锁
  currentOffset: number; // 当前分页偏移量
}

// 所有下拉的配置集合,新增下拉仅需新增该对象的配置项
selectConfigMap: Record<string, SelectConfig> = {
  currency: {
    dataList: [],
    fetchApi: (param) => this.appService.getAllCurrencies(param),
    loading: false,
    currentOffset: 0
  },
  language: {
    dataList: [],
    fetchApi: (param) => this.appService.getAllLanguages(param),
    loading: false,
    currentOffset: 0
  },
  // 剩余国家、组织等下拉直接在此处新增配置即可,无需编写额外逻辑
};

windowSize = 10;
private readonly PIXEL_TOLERANCE = 3.0;
// 批量获取所有mat-select实例,无需单独声明@ViewChild
@ViewChildren(MatSelect) allSelects: QueryList<MatSelect>;

2. 统一注册所有下拉的滚动监听事件

在ngAfterViewInit中批量为所有下拉绑定展开事件和滚动监听,无需单独为每个下拉写订阅逻辑:

ngAfterViewInit() {
  this.allSelects.forEach(select => {
    select.openedChange.subscribe((isOpened: boolean) => {
      if (isOpened) {
        const panel = select.panel.nativeElement;
        const selectName = select.ngControl.name;
        // 先移除旧的滚动监听,避免重复绑定
        panel.removeEventListener('scroll', this.handleScroll.bind(this, selectName));
        // 绑定新的滚动监听
        panel.addEventListener('scroll', this.handleScroll.bind(this, selectName));
      }
    })
  })
}

3. 实现通用滚动判断和加载逻辑

所有下拉共用一套触底判断、数据加载逻辑,同时通过loading锁解决重复请求问题:

// 统一滚动事件处理
private handleScroll(selectName: string, event: Event) {
  const scrollTarget = event.target as HTMLElement;
  const currentConfig = this.selectConfigMap[selectName];
  // 正在加载中、未触底、配置不存在时直接返回
  if (!currentConfig || currentConfig.loading || !this.isScrollToBottom(scrollTarget)) {
    return;
  }
  // 上锁,防止重复触发请求
  currentConfig.loading = true;
  currentConfig.currentOffset += this.windowSize;
  // 构造分页请求参数
  const pageParam = {
    ...this.standardPageSizeObj,
    pageSize: this.windowSize,
    offset: currentConfig.currentOffset
  };
  // 调用对应下拉的请求方法
  currentConfig.fetchApi(pageParam).subscribe({
    next: (resp) => {
      currentConfig.dataList.push(...resp.data);
      // 请求完成解锁
      currentConfig.loading = false;
    },
    error: () => {
      // 请求失败也需要解锁,避免后续无法触发加载
      currentConfig.loading = false;
    }
  })
}

// 通用触底判断方法
private isScrollToBottom(target: HTMLElement): boolean {
  return Math.abs(target.scrollHeight - target.scrollTop - target.clientHeight) < this.PIXEL_TOLERANCE;
}

4. 修改HTML模板绑定

将原有单独的数据源替换为配置对象内的对应数据源即可:

<mat-select [disabled]="isDisabled" [(ngModel)]="model.currency" name="currency" (selectionChange)="OnChange('currency',$event.value)">   
  <mat-option [value]="getCurrency.currencyCode" *ngFor="let getCurrency of selectConfigMap.currency.dataList | filternew:searchCurrencyvaluedrop">
    {{ getCurrency.currencyDescription }}
  </mat-option>
</mat-select>

<mat-select [disabled]="isDisabled" [(ngModel)]="model.language" name="language" (selectionChange)="OnChange('language', $event.value)">                      
  <mat-option [value]="getLanguage.languageDescription" *ngFor="let getLanguage of selectConfigMap.language.dataList | filternew:searchLanguagevaluedrop">
    {{ getLanguage.languageDescription }}
  </mat-option>
</mat-select>

方案优势

  • 完全消除代码冗余,新增下拉仅需加1条配置,无需编写重复逻辑,适配任意数量的下拉字段
  • 新增的loading锁直接解决滚动到底部触发两次请求的问题
  • 未引入任何外部依赖,符合安全限制要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 04:24:01