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

Angular 15实现表格每行复选框值获取与选中项数组维护

Angular 15 表格复选框实现与选中状态管理方案

需求说明

  • 在Angular 15项目中,为表格每行添加复选框,表格数据通过HTTP Get请求获取
  • 根据复选框选中/取消事件,将对应数据的paymentFor值添加至指定数组或从数组中移除

示例数据

[
  {paymentFor: 'P29', paymentPurpose: 'Advance payment', paymentAmount: 1000000, paymentType: 'Payment Schedule', isActiveTrigger: ''},
  {paymentFor: 'P30', paymentPurpose: 'Advance payment', paymentAmount: 2000000, paymentType: 'Payment Schedule', isActiveTrigger: ''},
  {paymentFor: 'P31', paymentPurpose: 'Head room', paymentAmount: 310000, paymentType: 'Payment Schedule', isActiveTrigger: ''},
  {paymentFor: 'U10', paymentPurpose: 'Mosquoto Mesh', paymentAmount: 60000, paymentType: 'Additional Quotation', isActiveTrigger: ''},
  {paymentFor: 'U9', paymentPurpose: 'Double door entrance', paymentAmount: 25000, paymentType: 'Additional Quotation', isActiveTrigger: ''}
]

现有代码

Service 代码

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Subject, Observable, tap } from 'rxjs';
import { environment } from 'src/environments/environment.development';

@Injectable({
  providedIn: 'root'
})
export class PaymentTriggerService {
  constructor(private httpClient: HttpClient) { }
  private _refreshNeeded$ = new Subject<void>();

  get refreshNeeded$(){
    return this._refreshNeeded$;
  }

  GetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger(companyID: number, quoteHeaderID: number): Observable<any> {
    let apiURL = `${environment.netapiurl}PaymentTriggers/GetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger/${companyID}/${quoteHeaderID}`;
    return this.httpClient.get<any>(apiURL) 
      .pipe(
        tap(() => {
          this._refreshNeeded$.next();
        })
      );
  }
}

HTML 代码

<div class="row mt-4">
  <div class="col-lg-12">
    <div class="form-group">
      <label *ngFor="let data of arrGetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger" class="form-control">
        <input type="checkbox" name="data" [(ngModel)]="data.paymentFor" value]="data.paymentFor" (selectionchange)="getValues(data.paymentFor)" > {{data.paymentFor}}
      </label>
    </div>  
  </div>
</div>

Component 代码

export class DataEntryComponent implements OnInit{
  arrGetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger: any = [];
  arrAppendAllPaymentForIDs: string[] = [];

  constructor(private paymentTriggerSvc: PaymentTriggerService) {}

  getValues(paymentFor: string){
    this.arrAppendAllPaymentForIDs.push(paymentFor);
    console.warn(this.arrAppendAllPaymentForIDs);
  }

  ngOnInit(): void {
    this.paymentTriggerSvc.GetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger(this.companyID, this.editQuotationQuoteHeaderID)
      .subscribe((response: any) => {                             
        this.arrGetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger = response;
        console.log(this.arrGetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger);
      });
  }
}

问题分析与优化方案

1. 现有代码核心问题

  • 复选框绑定错误:将ngModel绑定到字符串类型的paymentFor,无法正确维护选中状态
  • 事件绑定错误:使用非标准的selectionchange事件,复选框应使用change事件
  • 逻辑缺失:仅处理选中添加操作,未实现取消选中时的移除逻辑
  • 类型不安全:Service返回类型定义错误,Component数组未指定类型
  • 内存泄漏风险:HTTP请求未取消,组件销毁后可能残留订阅

2. 完整优化实现

步骤1:定义数据接口增强类型安全

创建payment-item.interface.ts文件:

export interface PaymentItem {
  paymentFor: string;
  paymentPurpose: string;
  paymentAmount: number;
  paymentType: string;
  isActiveTrigger: string;
  isChecked?: boolean; // 新增属性存储复选框选中状态
}

步骤2:修正Service返回类型

// 修改Service中的方法返回类型
GetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger(companyID: number, quoteHeaderID: number): Observable<PaymentItem[]> {
  let apiURL = `${environment.netapiurl}PaymentTriggers/GetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger/${companyID}/${quoteHeaderID}`;
  return this.httpClient.get<PaymentItem[]>(apiURL) 
    .pipe(
      tap(() => this._refreshNeeded$.next())
    );
}

步骤3:优化Component逻辑

import { OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { PaymentItem } from './payment-item.interface';

export class DataEntryComponent implements OnInit, OnDestroy{
  arrGetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger: PaymentItem[] = [];
  arrAppendAllPaymentForIDs: string[] = [];
  private dataSubscription?: Subscription;

  constructor(private paymentTriggerSvc: PaymentTriggerService) {}

  // 处理复选框选中/取消事件
  onCheckboxChange(item: PaymentItem) {
    if (item.isChecked) {
      // 避免重复添加
      if (!this.arrAppendAllPaymentForIDs.includes(item.paymentFor)) {
        this.arrAppendAllPaymentForIDs.push(item.paymentFor);
      }
    } else {
      // 取消选中时移除对应值
      const index = this.arrAppendAllPaymentForIDs.indexOf(item.paymentFor);
      if (index > -1) {
        this.arrAppendAllPaymentForIDs.splice(index, 1);
      }
    }
  }

  ngOnInit(): void {
    this.dataSubscription = this.paymentTriggerSvc.GetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger(this.companyID, this.editQuotationQuoteHeaderID)
      .subscribe((response: PaymentItem[]) => {                             
        // 初始化每个数据项的选中状态
        this.arrGetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger = response.map(item => ({
          ...item,
          isChecked: false
        }));
      });
  }

  // 组件销毁时取消订阅,避免内存泄漏
  ngOnDestroy(): void {
    this.dataSubscription?.unsubscribe();
  }
}

步骤4:修正HTML表格模板

<div class="row mt-4">
  <div class="col-lg-12">
    <div class="form-group">
      <table class="table table-striped">
        <thead>
          <tr>
            <th>选择</th>
            <th>Payment For</th>
            <th>Purpose</th>
            <th>Amount</th>
            <th>Type</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let data of arrGetPaymentSchAndUpgByQuoteHeaderIDForPaymentTrigger">
            <td>
              <input 
                type="checkbox" 
                [(ngModel)]="data.isChecked" 
                (change)="onCheckboxChange(data)">
            </td>
            <td>{{data.paymentFor}}</td>
            <td>{{data.paymentPurpose}}</td>
            <td>{{data.paymentAmount}}</td>
            <td>{{data.paymentType}}</td>
          </tr>
        </tbody>
      </table>
    </div>  
  </div>
</div>

3. 额外优化建议

  • 使用Reactive Forms替代ngModel:适合大型项目,更便于表单验证和状态管理
  • 添加加载状态与错误处理:请求时显示加载动画,捕获并处理HTTP错误
  • 使用Set存储选中ID:避免重复值,比数组的includes和splice操作更高效
  • 增加全选/取消全选按钮:提升批量操作效率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 17:47:09