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

Angular 7 Datatable后端分页模式下响应式列失效问题求助

Got it, let's tackle this responsive expansion issue in your server-side processed Angular DataTables. I’ve gone through your code and here’s exactly what’s breaking the functionality and how to fix it:

Root Cause

When using serverSide: true, DataTables takes full control of rendering table rows and handling data. Your current HTML template uses *ngFor to loop through configuration.data and build rows manually—this conflicts with DataTables' server-side rendering workflow. The library can’t recognize your custom-rendered columns, so it fails to trigger the responsive (+) expansion for hidden columns.

Additionally, your server-side ajax callback was returning an empty data: [] array, which meant DataTables had no actual rows to render at all. That’s a critical oversight!

Step-by-Step Fix

1. Simplify Your HTML Template

Let DataTables handle row rendering instead of manual *ngFor loops. Update your template to only define the table structure, not the rows:

<ng-container *ngIf="dtOptions">
  <table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="row-border hover">
    <thead>
      <tr>
        <th *ngFor="let column of configuration.columns" 
            [class.noExport]="column.data === 'remove' || column.data === 'more'">
          {{ column.title || '' }}
        </th>
      </tr>
    </thead>
    <!-- Let DataTables auto-generate tbody for server-side mode -->
    <tbody></tbody>
    <!-- Keep empty state only for local data mode -->
    <tbody *ngIf="!configuration.ajax && configuration.data?.length === 0">
      <tr style="width: 100% !important">
        <td class="no-data-available" [attr.colspan]="numberOfColumns">No data available</td>
      </tr>
    </tbody>
  </table>
</ng-container>

2. Centralize Column Rendering Logic

Create a reusable function to build DataTables column definitions with custom rendering (for icons, dates, and action buttons). This ensures consistency across both local and server-side modes:

private buildColumnDefs(): any[] {
  return this.configuration.columns.map(column => {
    const columnDef: any = {
      title: column.title,
      data: column.data,
      width: column.width,
      responsivePriority: column.responsivePriority,
      orderable: column.orderable ?? true,
      className: column.class
    };

    // Custom display rendering for each column
    columnDef.render = (data: any, type: string, row: any) => {
      if (type !== 'display') return data; // Return raw data for sorting/filtering

      let content = '';
      
      // Add icon if configured
      if (column.icon && this.showIcon(row, column)) {
        content += `<mat-icon style="color: ${column.icon.color || 'inherit'}" class="icon">${column.icon.iconName}</mat-icon>`;
      }

      // Format value (handle dates)
      let displayValue = data;
      if (column.dateFormat) {
        // Replicate Angular date pipe logic (adjust format options as needed)
        displayValue = new Date(data).toLocaleDateString('en-US', {
          year: 'numeric',
          month: column.dateFormat.includes('MM') ? '2-digit' : 'short',
          day: column.dateFormat.includes('dd') ? '2-digit' : 'numeric'
        });
      }
      content += `<span>${displayValue}</span>`;

      // Replace content with action buttons for remove/more columns
      if (column.data === 'remove' && this.configuration.remove) {
        content = `<button mat-icon-button mat-tooltip="${this.configuration.remove.tooltip}">
                    <mat-icon>${this.configuration.remove.icon}</mat-icon>
                  </button>`;
      } else if (column.data === 'more' && this.configuration.more) {
        content = `<button mat-icon-button mat-tooltip="${this.configuration.more.tooltip}">
                    <mat-icon>${this.configuration.more.icon}</mat-icon>
                  </button>`;
      }

      return `<div class="align-icon-text">${content}</div>`;
    };

    return columnDef;
  });
}

3. Fix Server-Side dtOptions Configuration

Update your ajax callback to return actual data, and use the centralized column definitions. Also, explicitly configure the responsive renderer for reliability:

if (this.configuration.ajax) {
  const pageSize = this.configuration.pageSize || 10;
  this.dtOptions = {
    processing: true,
    ajax: (dtParameters: any, callback) => {
      let URL = this.configuration.ajax.URL;
      const currentPage = (dtParameters.start + dtParameters.length) / pageSize;
      URL = `${URL}?page=${currentPage - 1}&pageSize=${pageSize}`;

      this.crud.get(URL, this.configuration.ajax.headers).subscribe((res) => {
        const tableData = this.getObjectByPath(res);
        callback({
          recordsTotal: res['paging']['total'],
          recordsFiltered: res['paging']['total'],
          data: tableData // Return actual data here (not empty array!)
        });
      });
    },
    dom: 'Bfrtip',
    bLengthChange: false,
    bFilter: true,
    bInfo: false,
    bAutoWidth: false,
    pageLength: pageSize,
    order: [],
    serverSide: true,
    pagingType: 'full_numbers',
    columns: this.buildColumnDefs(),
    responsive: {
      details: {
        renderer: $.fn.dataTable.Responsive.renderer.tableAll()
      }
    },
    language: { search: 'Filter: ' },
    buttons: [
      { extend: 'print', exportOptions: { columns: "thead th:not(.noExport)" } },
      { extend: 'excel', exportOptions: { columns: "thead th:not(.noExport)" } },
      { extend: 'csv', exportOptions: { columns: "thead th:not(.noExport)" } }
    ],
    // Bind action button clicks using rowCallback
    rowCallback: (row: Node, data: any) => {
      $(row).find('button[mat-icon-button]').each((_, btn) => {
        const iconText = $(btn).find('mat-icon').text();
        if (iconText === this.configuration.remove?.icon) {
          $(btn).off('click').on('click', () => this.deleteRow(data));
        } else if (iconText === this.configuration.more?.icon) {
          $(btn).off('click').on('click', () => this.showMore(data));
        }
      });
      return row;
    }
  };
}

4. Update Local Data Mode to Use Same Column Definitions

Keep consistency by applying the same column rendering logic to local data mode:

} else if (this.configuration.data) {
  if (typeof this.configuration.data === 'object' && !(this.configuration.data instanceof Array)) {
    this.configuration.data = [this.configuration.data];
  }
  const pageSize = this.configuration.pageSize || 10;
  this.dtOptions = {
    data: this.configuration.data,
    bLengthChange: false,
    bFilter: true,
    bInfo: false,
    bAutoWidth: false,
    pageLength: pageSize,
    order: [],
    dom: 'Bfrtip',
    pagingType: 'full_numbers',
    columns: this.buildColumnDefs(),
    responsive: true,
    language: { search: 'Filter: ' },
    buttons: [
      { extend: 'print', exportOptions: { columns: "thead th:not(.noExport)" } },
      { extend: 'excel', exportOptions: { columns: "thead th:not(.noExport)" } },
      { extend: 'csv', exportOptions: { columns: "thead th:not(.noExport)" } }
    ],
    rowCallback: (row: Node, data: any) => {
      // Reuse the same action button binding logic from server-side mode
      $(row).find('button[mat-icon-button]').each((_, btn) => {
        const iconText = $(btn).find('mat-icon').text();
        if (iconText === this.configuration.remove?.icon) {
          $(btn).off('click').on('click', () => this.deleteRow(data));
        } else if (iconText === this.configuration.more?.icon) {
          $(btn).off('click').on('click', () => this.showMore(data));
        }
      });
      return row;
    }
  };
}

Key Notes

  • Data Consistency: By letting DataTables render rows, the library can properly track column visibility and trigger responsive expansion.
  • Ajax Fix: Returning actual data from your server callback is essential—without it, DataTables had no rows to render at all.
  • Event Binding: Using rowCallback ensures action buttons work correctly even when rows are re-rendered (e.g., pagination, filtering).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:45:19