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

Angular(Taiga UI)表格复选框交互与分页状态处理问询

Taiga UI服务端分页表格复选框实现方案

一、主复选框三态实现

Taiga UI的tui-checkbox原生支持indeterminate(部分选中)状态,通过组件内的状态计算逻辑实现三态切换:

  1. 核心变量维护

    • selectedIds: Set<string>:存储跨分页已选中的文档ID
    • isAllSelected: boolean:标记是否全选所有分页的文档
    • excludedIds: Set<string>:全选所有时,存储需要排除的文档ID
  2. 三态计算逻辑

get masterCheckboxState(): boolean | null {
  if (this.isAllSelected) {
    // 全选所有时,有排除项则为部分选中,否则全选
    return this.excludedIds.size > 0 ? null : true;
  }
  const currentPageIds = this.documents.map(doc => doc.id);
  const selectedInCurrent = currentPageIds.filter(id => this.selectedIds.has(id));
  
  if (selectedInCurrent.length === currentPageIds.length) return true;
  return selectedInCurrent.length > 0 ? null : false;
}
  1. 模板绑定
<tui-checkbox
  [(ngModel)]="masterCheckboxState"
  [indeterminate]="masterCheckboxState === null"
  (ngModelChange)="onMasterCheckboxChange($event)"
></tui-checkbox>

二、分页切换保留选中状态

通过全局存储选中状态,分页切换后重新匹配当前页数据的选中状态:

  1. 行复选框状态判断
isRowSelected(docId: string): boolean {
  if (this.isAllSelected) {
    return !this.excludedIds.has(docId);
  }
  return this.selectedIds.has(docId);
}
  1. 分页切换逻辑
    分页切换时仅请求当前页数据,无需修改选中状态存储变量,渲染行复选框时通过isRowSelected匹配状态:
<tui-checkbox
  [ngModel]="isRowSelected(doc.id)"
  (ngModelChange)="onRowCheckboxChange(doc.id, $event)"
></tui-checkbox>

三、全选场景下服务端通信优化

无需获取所有文档ID,通过标记+排除项的方式与服务端交互:

  1. 两种选中场景的请求格式
submitSelected(): void {
  let requestPayload;
  if (this.isAllSelected) {
    // 全选所有:传全选标记+排除ID+筛选条件(如有)
    requestPayload = {
      isAllSelected: true,
      excludedIds: Array.from(this.excludedIds),
      filter: this.currentFilter // 表格的筛选条件
    };
  } else {
    // 部分选中:仅传已选中ID集合
    requestPayload = {
      selectedIds: Array.from(this.selectedIds)
    };
  }
  // 调用服务端接口处理
  this.documentService.processSelected(requestPayload).subscribe();
}
  1. 服务端处理逻辑
  • 收到isAllSelected: true时,根据筛选条件查询所有符合要求的文档,再排除excludedIds中的条目
  • 收到selectedIds时,直接处理指定ID的文档

四、完整核心代码示例

组件类

import { Component } from '@angular/core';
import { TuiTablePagination } from '@taiga-ui/addon-table';

interface Document {
  id: string;
  name: string;
}

@Component({
  selector: 'app-document-table',
  templateUrl: './document-table.component.html',
})
export class DocumentTableComponent {
  documents: Document[] = [];
  pagination = new TuiTablePagination({ page: 1, pageSize: 10 });
  selectedIds = new Set<string>();
  isAllSelected = false;
  excludedIds = new Set<string>();
  currentFilter = {}; // 表格筛选条件

  get masterCheckboxState(): boolean | null {
    if (this.isAllSelected) {
      return this.excludedIds.size > 0 ? null : true;
    }
    const currentPageIds = this.documents.map(doc => doc.id);
    const selectedInCurrent = currentPageIds.filter(id => this.selectedIds.has(id));
    
    if (selectedInCurrent.length === currentPageIds.length) return true;
    return selectedInCurrent.length > 0 ? null : false;
  }

  isRowSelected(docId: string): boolean {
    if (this.isAllSelected) {
      return !this.excludedIds.has(docId);
    }
    return this.selectedIds.has(docId);
  }

  onMasterCheckboxChange(state: boolean | null): void {
    const currentPageIds = this.documents.map(doc => doc.id);
    if (state === true) {
      currentPageIds.forEach(id => this.selectedIds.add(id));
      this.isAllSelected = false;
      this.excludedIds.clear();
    } else if (state === false) {
      currentPageIds.forEach(id => this.selectedIds.delete(id));
      this.isAllSelected = false;
      this.excludedIds.clear();
    }
  }

  onRowCheckboxChange(docId: string, checked: boolean): void {
    if (this.isAllSelected) {
      checked ? this.excludedIds.delete(docId) : this.excludedIds.add(docId);
    } else {
      checked ? this.selectedIds.add(docId) : this.selectedIds.delete(docId);
    }
  }

  onPageChange(pagination: TuiTablePagination): void {
    this.pagination = pagination;
    this.loadDocuments();
  }

  loadDocuments(): void {
    // 调用服务端接口加载当前页数据
    // this.documentService.getDocuments(this.pagination, this.currentFilter).subscribe(res => {
    //   this.documents = res.data;
    // });
  }

  selectAllPages(): void {
    this.isAllSelected = true;
    this.selectedIds.clear();
    this.excludedIds.clear();
  }

  submitSelected(): void {
    const requestPayload = this.isAllSelected 
      ? { isAllSelected: true, excludedIds: Array.from(this.excludedIds), filter: this.currentFilter }
      : { selectedIds: Array.from(this.selectedIds) };
    
    // this.documentService.processSelected(requestPayload).subscribe();
  }
}

模板

<tui-table [data]="documents">
  <thead>
    <tr>
      <th>
        <tui-checkbox
          [(ngModel)]="masterCheckboxState"
          [indeterminate]="masterCheckboxState === null"
          (ngModelChange)="onMasterCheckboxChange($event)"
        ></tui-checkbox>
      </th>
      <th>文档名称</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let doc of documents">
      <td>
        <tui-checkbox
          [ngModel]="isRowSelected(doc.id)"
          (ngModelChange)="onRowCheckboxChange(doc.id, $event)"
        ></tui-checkbox>
      </td>
      <td>{{ doc.name }}</td>
    </tr>
  </tbody>
</tui-table>

<tui-table-pagination
  [pagination]="pagination"
  (paginationChange)="onPageChange($event)"
></tui-table-pagination>

<div style="margin-top: 16px;">
  <button tuiButton (click)="selectAllPages()">全选所有分页</button>
  <button tuiButton (click)="submitSelected()" style="margin-left: 8px;">提交选中</button>
</div>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 09:35:15