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

Angular中如何编写可复用函数处理不同组件的差异化数据?

Angular 组件复用方案:处理相似结构的列表类组件

这是个很典型的Angular代码复用场景,针对你这种大量结构相似、仅细节差异的组件,我推荐几个实用的方案,按复用程度从高到低排序:

方案一:抽象基类(最适合你的场景)

把所有组件的通用逻辑抽成抽象基类,让具体组件继承并实现差异化部分。这种方式能最大化复用代码,同时保留组件各自的独特性。

步骤1:定义通用类型与抽象基类

先给通用部分定义类型,再写抽象基类封装重复逻辑:

import { OnInit } from '@angular/core';

// 通用分页类型
export interface PaginationState {
  totalData: number | null;
  currentPage: number | null;
}

// 通用数据容器类型(泛型支持不同数据结构)
export interface DataContainer<T> {
  items: T[] | null;
  isEmpty: boolean | null;
  getData: () => void;
}

// 抽象基类,泛型T对应数据项类型,F对应过滤参数类型
export abstract class BaseListComponent<T, F> implements OnInit {
  data: DataContainer<T>;
  pagination: PaginationState & {
    goNext: () => void;
    goLast: () => void;
  };
  filter: F;

  constructor() {
    // 封装通用分页逻辑
    this.pagination = {
      totalData: null,
      currentPage: 1, // 默认第一页
      goNext: () => {
        if (this.pagination.currentPage !== null) {
          this.pagination.currentPage += 1;
          this.data.getData();
        }
      },
      goLast: () => {
        if (this.pagination.totalData !== null) {
          this.pagination.currentPage = this.pagination.totalData;
          this.data.getData();
        }
      }
    };
  }

  ngOnInit(): void {
    // 通用初始化逻辑
    this.initFilter();
    this.data.getData();
  }

  // 抽象方法:子类必须实现过滤参数的初始化
  protected abstract initFilter(): void;
}

步骤2:具体组件继承基类

以PlanningComponent为例,只需要实现差异化的data和filter:

// 定义当前组件的专属类型
interface PlanningItem { /* 规划数据的字段 */ }
interface PlanningFilter {
  filterA: string | null;
  filterB: number | null;
  filterC: string | null;
  isEnabled: boolean | null;
  isToggled: boolean | null;
  onReset: () => void;
}

export class PlanningComponent extends BaseListComponent<PlanningItem, PlanningFilter> {
  constructor() {
    super();
    // 初始化专属数据逻辑
    this.data = {
      items: null,
      isEmpty: null,
      getData: () => {
        // 这里写获取规划数据的具体逻辑,同时更新pagination.totalData
        // 示例:
        // this.planningService.fetchItems(this.filter, this.pagination.currentPage)
        //   .subscribe(res => {
        //     this.data.items = res.list;
        //     this.pagination.totalData = res.totalPages;
        //     this.data.isEmpty = res.list.length === 0;
        //   });
      }
    };
  }

  protected initFilter(): void {
    // 初始化专属过滤参数与重置逻辑
    this.filter = {
      filterA: null,
      filterB: null,
      filterC: null,
      isEnabled: null,
      isToggled: null,
      onReset: () => {
        this.filter.filterA = this.filter.filterB = this.filter.filterC = null;
        this.data.getData();
      }
    };
  }
}

HarvestComponent的实现逻辑完全一致,只需要替换对应的HarvestItem和HarvestFilter类型即可。

优点:代码复用率极高,所有通用逻辑只写一次;类型安全,通过泛型保证组件数据结构的正确性。
缺点:继承带来一定耦合性,若基类逻辑变动,所有子类都会受影响。


方案二:服务封装通用逻辑

如果不想用继承,可以把分页、过滤这类通用逻辑封装成服务,组件按需调用服务生成对应的逻辑对象。

示例:ListService封装通用方法

import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ListService {
  // 生成分页逻辑
  createPagination(loadData: () => void) {
    const state = {
      totalData: null as number | null,
      currentPage: 1 as number | null
    };

    return {
      ...state,
      goNext: () => {
        if (state.currentPage !== null) {
          state.currentPage += 1;
          loadData();
        }
      },
      goLast: () => {
        if (state.totalData !== null) {
          state.currentPage = state.totalData;
          loadData();
        }
      }
    };
  }

  // 生成过滤重置逻辑
  createFilterReset<T>(filter: T, resetFields: (keyof T)[], loadData: () => void) {
    return () => {
      resetFields.forEach(field => filter[field] = null);
      loadData();
    };
  }
}

组件中使用服务

export class PlanningComponent implements OnInit {
  data = {
    items: null as PlanningItem[] | null,
    isEmpty: null as boolean | null,
    getData: () => { /* 具体数据逻辑 */ }
  };
  pagination: ReturnType<ListService['createPagination']>;
  filter = {
    filterA: null as string | null,
    filterB: null as number | null,
    filterC: null as string | null,
    isEnabled: null as boolean | null,
    isToggled: null as boolean | null,
    onReset: () => {}
  };

  constructor(private listService: ListService) {}

  ngOnInit(): void {
    // 调用服务生成分页逻辑
    this.pagination = this.listService.createPagination(() => this.data.getData());
    // 调用服务生成过滤重置逻辑
    this.filter.onReset = this.listService.createFilterReset(
      this.filter,
      ['filterA', 'filterB', 'filterC'],
      () => this.data.getData()
    );
  }
}

优点:耦合性低,组件可以灵活选择是否使用服务的逻辑;适合不需要完全统一结构的场景。
缺点:每个组件仍需要初始化基础的data和filter对象,复用程度略低于抽象基类。


方案三:独立UI组件(复用UI+逻辑)

如果你的分页、过滤的UI也高度相似,可以把它们拆成独立的子组件,通过@Input/@Output与父组件交互。

示例:PaginationComponent

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-pagination',
  template: `
    <div class="pagination">
      <button (click)="goNext.emit()" [disabled]="currentPage >= totalData">
        Next Page
      </button>
      <button (click)="goLast.emit()" [disabled]="currentPage === totalData">
        Last Page
      </button>
      <span>Page {{currentPage}} / {{totalData || 0}}</span>
    </div>
  `
})
export class PaginationComponent {
  @Input() totalData: number | null = null;
  @Input() currentPage: number | null = null;
  @Output() goNext = new EventEmitter<void>();
  @Output() goLast = new EventEmitter<void>();
}

父组件中使用子组件

<!-- PlanningComponent模板 -->
<app-pagination
  [totalData]="pagination.totalData"
  [currentPage]="pagination.currentPage"
  (goNext)="handleGoNext()"
  (goLast)="handleGoLast()">
</app-pagination>
// PlanningComponent逻辑
handleGoNext() {
  this.pagination.currentPage += 1;
  this.data.getData();
}

handleGoLast() {
  this.pagination.currentPage = this.pagination.totalData;
  this.data.getData();
}

过滤组件的实现逻辑类似,把过滤表单封装成子组件,通过@Output发出过滤参数变化的事件。

优点:同时复用UI和逻辑,适合需要统一页面样式的场景。
缺点:需要额外维护子组件,适合UI相似的情况,若UI差异较大则不适用。


选择建议

  • 如果你的组件逻辑、结构高度一致,优先选抽象基类,能最大程度减少重复代码;
  • 如果组件之间有一定差异,不想用继承,选服务封装更灵活;
  • 如果UI也高度相似,再结合独立UI组件进一步复用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:55:45