Ant-design: No way to show aggregation statistics data in table

Created on 11 Apr 2017  ·  78Comments  ·  Source: ant-design/ant-design

Version

2.9.1

Environment

browser

Reproduction link

http://none

Steps to reproduce

I wanna to show aggregation statistics at Table footer, I am tryings following ways:

  1. using footer prop, add tr and td to it
      <Table
        dataSource={dataSource}
        footer={() => {
          return (
            <tr className='ant-table-row  ant-table-row-level-0'>
              <td>总计</td>
              <td>总计</td>
            <td>总计</td>
            </tr>
          )
        }}
      />
  1. add extra data in dataSource prop
dataSource.push({id: '总计', price: '3000'})

What is expected?

A picture is worth a thousand words:

What is actually happening?

  1. using footer prop, But the footer prop is not fit the table tbody.

  2. add extra data in dataSource prop, But if the dataSource length is over the pageSize , the extra data will be ignored.

Inactive IssueHuntFest ❓FAQ 💡 Feature Request

Most helpful comment

Maybe we can do something to make footer fit the table tbody?

All 78 comments

Hello @TangMonk, it seems that you are requesting a feature than reporting a bug, I'm afraid we don't have plan to implement this feature. You can disable table's default pagination and implement pagination by yourself.

Maybe we can do something to make footer fit the table tbody?

@yesmeck , thanks for your reply, But there is too many issues about this, I think this feature is really useful and necessary.

related issues: #3591, #1483, #4581

@afc163 ,thank you!

What's your suggestion or solution?

I am not sure, I think it can not strict limit table data size by using pageSize, data should processed by server side instead of client side, So we can add whatever extra data to table

Hi,
Are there any update on this features? Or another way to do it?

@yesmeck The simplest solution is to make footer render an actual <tfoot> inside the table, instead of appending an extra <div> outside the table.

Folks are trying to squeeze an extra row into dataSource simply because they want the table layout to align columns for them.

Yes, we already have a footer prop, can not figure out an appropriate name for <tfoot/>.

I am suggesting reimplement the current footer prop to make it render a <tfoot /> inside table, not adding a new tfoot prop. Don't think it'll break anything.

@yesmeck Seems like this feature is frequently requested. I'm also struggling to add a summary row. Got a work around, but involves hacky dom manipulation, happy to see a prettier solution.

Anyone on the team taking care of it already? If no maybe I could look into it.

You can try PR.

@yesmeck Here you go: https://github.com/react-component/table/pull/191
See example/renderFooterInTable for effect

After thinking a while for the tfoot API, I'd like to add it to column property:

const columns = [{
  title: 'Age',
  dataIndex: 'age',
  key: 'age',
  footer: (data) => {
    return <div>Summary: {data.reduce((sum, record) => sum + record.age, 0)}</div>
  },
}, {
  title: 'Name',
  dataIndex: 'name',
  key: 'name',
}, {
  title: 'Address',
  dataIndex: 'address',
  key: 'address',
}];

renders

screen shot 2018-02-27 at 15 03 33

It's more clear to make it a difference between the current Table[footer].

Good idea. I agree this would be a better place to squeeze in a new API.

@hackape Could you propose a new PR?

sure thing

https://codesandbox.io/s/m45r6o4nj8
实现了,但是滚动条联动出现了问题,更方便看下嘛

@yesmeck what's the state of react-component/table#195 now, any feedback?

Saw it merged 👍 @yesmeck

However, I got an additional feature request. I'd like to propose allowing the so called "footer" appear at top of table, right beneath the table header.

Example:
image

The actual requested feature being discussed the whole time is a mean to squeeze in an extra row for displaying statistic info—in other word a "summary row", without tampering the content of dataSource, not an "footer" in the positional sense.

@hackape You can use column grouping, see https://codesandbox.io/s/pmorq2x2lq

Yes, were it solely for positioning purpose, this would be a viable mean. But in business logic sense, it's way better to let user do it via my proposed API.

Think we can rename the column.footer API to column.summary.

const columns = [{
  title: 'Age',
  dataIndex: 'age',
  key: 'age',
  summary: {
    render: (data) => {
      return <div>Summary: {data.reduce((sum, record) => sum + record.age, 0)}</div>
    },
    position: 'bottom',  // default to 'bottom', could be 'top'
  }
}]

I'd propose to introduce two new pro named pinnedTopData and pinnedBottomData.

const columns = [
  { title: 'Price', dataIndex: 'price' },
  { name: 'Name', dataIndex: 'name' },
];

const data = [
  { price: 3, Name: 'Foo' },
  { price: 5, Name: 'Bar' },
];

const pinnedData = [
  { price: <span style={{ fontWeight: 'bold' }}>Total: 8</span> }
];

<Table
  columns={columns}
  dataSource={data}
  pinnedTopData={pinnedData}
  pinnedBottomData={pinnedData}
/>

Renders:

| Price | Name |
|----------|------|
| Total: 8 | |
| 3 | Foo |
| 5 | Bar |
| Total: 8 | |

It is better to resolve #6896 and this issue with same API.

Is there new progress?

@Nokecy 看他的解决方式有两种,他暂时没加到属性上,我看他的re-table有这个属性,前提是版本号升到最新,不过应该需要修改antd的package.json. 还有两种方法,
1.如果列不是很多,不出现滚动条的话,很好解决,我上面有个例子。
2.可以使用多个标题栏进行显示,感觉不是那么美观。

很期待他们把这个功能完善,一直在持续关注,不过我们的antd是2.,就算更新了,不知道2.版本会不会有这个属性

Any update for this issue??

before they can ship out an official API, for now i suggest supply a table.props.components.body.wrapper custom component, and tamper the props.children it received.

https://codesandbox.io/s/xpvwpx0npw
我直接操作了dom元素,封装了一个合计总计的,可以按照这个方法加很多footer,但是官方不推荐直接操作dom元素,如果急用,这个可以参考下

@LDD123 the scroll x is only on footer in your example. the body should also scroll with footer

image

@jagratiTomar the body will scroll when footer is scrolling, I bind the body‘s scroll-x with the footer's scroll-x

@LDD123 can you share and example where there is a scroll-y also in body, because in that case only body should scroll and not footer

@jagratiTomar now,i hidden scroll-x not scroll,it will be ok

@LDD123 so you are saying that when there is scroll-y this example would work righ?

yeap

Hello @yesmeck @afc163 ,

Our designers putting the footer (it has the aggregated values) on top of body and right below header for UX purposes. The arrows for the sorting on header and footer has only aggregated values. With ColumnGroup arrows for sorting gets appended to bottom. From styling perspective it is a lot more easier to style footer and set it at the top since footer container has different colour and only some cells have seperator.

I would just do:

.my-table tfoot > tr {
 background-color: gray;
}

Table Component could have something like this:

<Table appendColumnFooterTop={true}>
   <Column
      footer={(data) => <div>Summary: {data.reduce((sum, record) => sum + record.age, 0)}</div>}
   />
</Table>

Design:
screen shot 2018-05-30 at 17 07 38

Don't mind the unaligned text in the cells it's from a previously done page they have to align on the new one we are coding.

I could offer my help if I knew it could be released within 2 or 3 weeks (column footer and it's alignment). Else I will be forced to find other solutions.

Oh and I couldn't understand if #6896 is about column footer so I wrote here.

Thanks.

@kaanozcan I've submitted a PR implementing the same idea (see here) back in March, the core team merged it, then it got reverted. Seems like they have other idea on how the problem should be tackled. Given the situation, I would suggest you take the "find other solutions" path.

@kaanozcan Here is an example achieves your goal by custom components https://codesandbox.io/s/xjpkx9rzzw

@yesmeck That works perfectly. Thanks.

Closing due to the original request can be achieved by custom components.

@yesmeck Could us provide some demo about this usage case?

https://codesandbox.io/s/xjpkx9rzzw
这个例子并没有什么实际意义. 因为如果 data改变的话..最后的 sum并没有使用最新数据.而是上一次的刷新结果. 应该是哪里有脏数据.

@yesmeck 你给的例子并不能使用..因为data不能改变.
const data = [ { key: "1", name: "John Brown", age: 30, address: "New York No. 1 Lake Park", }, { key: "2", name: "Jim Green", age: 20, address: "London No. 1 Lake Park", }, { key: "3", name: "Joe Black", age: Math.random() * 20, address: "Sidney No. 1 Lake Park", }, ]; 例如如此修改...实际上最后的sum并不正确.

@yesmeck https://codesandbox.io/s/xjpkx9rzzw This demo has problem,when you set left of fixed ,You can see the style is in a mess。

I managed to do it using the component wrapper, but it's kind of awkward. Code looks like this

export default class extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      localData: props.data,
    };

    this.tableRef = React.createRef();
  }

  handleTableUpdate = () => {
    this.setState({ localData: this.tableRef.current.getLocalData() });
  }

  render() {
    return (
      <Table
        ref={this.tableRef}
        columns={this.props.columns}
        dataSource={this.props.data}
        onChange={this.handleTableUpdate}
        components={{
          body: {
            wrapper: (props) => (
              <BodyWithSummary
                columns={this.props.columns}
                data={this.state.localData}
                {...props}
              />
            ),
          },
        }}
      />
    );
  }
}

function BodyWithSummary(props) {
  const { data, columns, ...otherProps } = props;
  return (
    <tbody {...otherProps}>
      <React.Fragment>
        {data && data.length > 0 && (
          <tr>
            {props.columns.map((col) => renderColumn(col, data))}
          </tr>
        )}
        {props.children}
      </React.Fragment>
    </tbody>
  );
}

The annoying thing is that I need to call getLocalData if I want the aggregate data to be computed on the filtered rows.
Would be much nicer if the body wrapper would receive the table ref as a prop or have an easier way to access to the data that will be rendered (props.children is a list of react nodes, not very useful to compute the aggregates)

Could we use footer prop but render in table instead of additional div?

临时这样处理的:

20180927 0905

      .ant-table-footer {
        padding: 0;
      }
                                    footer={(currentPageData)=>{
                                        return (
                                            <Row style={{marginRight:17}}>
                                                <Col span={4} className="brd">
                                                    <Card bordered={false} className="bgfb" bodyStyle={{padding:'10px 15px'}}>合计</Card>
                                                </Col>
                                                <Col span={4} className="brd">
                                                    <Card bordered={false} className="bgfb" bodyStyle={{padding:'10px 15px'}}>123456789</Card>
                                                </Col>
                                                <Col span={4} className="brd">
                                                    <Card bordered={false} className="bgfb" bodyStyle={{padding:'10px 15px'}}>123456789</Card>
                                                </Col>
                                                <Col span={4} className="brd">
                                                    <Card bordered={false} className="bgfb" bodyStyle={{padding:'10px 15px'}}>123456789</Card>
                                                </Col>
                                                <Col span={4} className="brd">
                                                    <Card bordered={false} className="bgfb" bodyStyle={{padding:'10px 15px'}}>123456789</Card>
                                                </Col>
                                                <Col span={4}>
                                                    <Card bordered={false} className="bgfb" bodyStyle={{padding:'10px 15px'}}>123456789</Card>
                                                </Col>
                                            </Row>
                                        );
                                    }}

不是很完美咯。

期待columns的foot早日出来O(∩_∩)O

@ybning 你那个用固定列,会出问题吗?很多这种固定列都会出问题。。

@yesmeck Your example breaks internal table counting: https://codesandbox.io/s/rrpo02qwkq
Navigate to page 2 then go back to page 1, it duplicates the data on each navigation.

Any update for this issue?

@linrf no, I hope somebody can solve this problem

Could us add a total prop to deal aggregation statistics ?
total is different with footer,
only accept a function to deal column data, default is reduce

v7a kvx 5cig rjeo 696g
我最后把合计放到column里了

antd 的 table footer 内容的渲染确实不太好用,尤其是表格本身有固定列的情况下。

可以换下方式,用两个 table 来实现,一个渲染原 table, 一个渲染底部 footer 元素。再配合样式覆盖,隐藏掉 table footer 的 thead 以及原 table 滚动区域的滚动条。最后再加入 JS 代码,让两个 table 的水平滚动位置对齐,基本就差不多了。

组件:

import React, { PureComponent } from 'react';
import { Table } from 'antd';
import styles from './index.less';

export default class ChartTable extends PureComponent {
  constructor(props) {
    super(props);

    this.refBox = React.createRef();
  }
  componentDidMount() {
    const tableList = Array.from(this.refBox.current.querySelectorAll('.ant-table-body'));
    this.tableList = tableList;
    tableList.forEach((table) => {
      table.addEventListener('scroll', this.handleTableScroll);
    });
  }
  componentWillUnmount() {
    this.tableList.forEach((table) => {
      table.removeEventListener('scroll', this.handleTableScroll);
    });
  }
  handleTableScroll = (e) => {
    const current = e.currentTarget;
    this.tableList.forEach((table) => {
      if (table !== current && table.scrollLeft !== current.scrollLeft) {
        table.scrollLeft = current.scrollLeft;
      }
    });
  }
  renderFooterTable = () => {
    const { columns, dataSource } = this.props;
    const dataList = [{
      department: '合计',
    }];
    return (
      <Table
        className={styles.footerTable}
        pagination={false}
        scroll={{ x: 'max-content' }}
        size="small"
        columns={columns}
        dataSource={dataList}
      />
    );
  }
  render() {
    return (
      <div ref={this.refBox}>
        <Table
          className={styles.mainTable}
          pagination={false}
          scroll={{ x: 'max-content', y: 350 }}
          size="small"
          columns={this.props.columns}
          dataSource={this.props.dataSource}
        />
        {this.renderFooterTable()}
      </div>
    );
  }
}

样式覆盖文件:

mainTable {
  :global {
    .ant-table-body::-webkit-scrollbar {
      display: none;
    }
  }  
}
.footerTable {
  border-top: 1px solid #e8e8e8;

  :global {
    thead {
      display: none;
    }
  }
}

+1 Would love to have a way to show column aggregations in the footer.

This can be implemented using components prop.

const __Table = (totalsRow = initTotals(), key='tableKey') => ({
    className,
    style,
    children
}) => (
    <table className={className} style={style} key={key}>
        {children}
        <tfoot>
            <tr>
                // add tfoot td here
            </tr>
        </tfoot>
    </table>
);

const TableWithTFoot = ({allData, columns}) =>
    <Table
        dataSource={allData.data}
        columns={columns}
        pagination={false}
        components={{ table: __Table(allData.totals) }}
    />

@rororofff has funded $5.00 to this issue.


@piuccio According to your thinking, i write it with typescript.

https://github.com/Arweil/antd-ext/blob/master/src/TableExt/TableExt.tsx

https://github.com/ant-design/ant-design/issues/5710#issuecomment-450855438

this.refBox = React.createRef();

I use this version package , run this code is failed.
"antd": "2.13.14","react": "15.4.0", "react-dom": "15.4.0"

error message
Uncaught TypeError: _react2.default.createRef is not a function at new EstimationReport (psReport.js?18c1:47) at eval (ReactCompositeComponent.js?063f:295) at measureLifeCyclePerf (ReactCompositeComponent.js?063f:75) at

@carvendy It is probably your module-resolve setting when import react. Try change

import from 'react';

into this:

import * as react from 'react'

我做了一个组件,
利用了columns model来渲染footer
但目前还不支持滚动条,

import React from 'react';

class TableFooter extends React.Component {
  render() {
    const { dataSource, columns } = this.props;
    return (
      <table className="ant-table">
        <colgroup>
          {columns.map((colModel, index) => {
            return <col style={{
              width: colModel.width,
              minWidth: colModel.width
            }} key={index} />
          })}
        </colgroup>
        <tfoot >
          <tr >
            {columns.map((colum, idxCol) => {
              return <td style={{ padding: "0px 8px" }} className={colum.className} key={idxCol}>
                <strong>
                  {colum.footerContent ? colum.footerContent : (colum.footerSum ? dataSource.reduce((sum, record) => sum + parseFloat(record[colum.key]), 0) : "")}
                </strong>
              </td>
            })}
          </tr>
        </tfoot>
      </table>
    )
  }
}
export default TableFooter;

页面调用

...
render() {
  const columns = [
      {
        key: 'productName'
        footerContent: 'SUM:',
      },
      {
        key: 'quantity',
        footerSum: true,
      }
    }
];
return (
  <Table
    columns={columns}
    dataSource={dataSource}
    footer={()=> {
        return (
           <TableFooter dataSource={dataSource} columns={columns} />
        )
       }
    }
  />
)
....

var data = this.getLocalData();
var current;
var pageSize;
var state = this.state; // 如果没有分页的话,默认全部展示

  if (!this.hasPagination()) {
    pageSize = Number.MAX_VALUE;
    current = 1;
  } else {
    pageSize = state.pagination.pageSize;
    current = this.getMaxCurrent(state.pagination.total || data.length);
  } // 分页
  // ---
  // 当数据量少于等于每页数量时,直接设置数据
  // 否则进行读取分页数据


  if (data.length > pageSize || pageSize === Number.MAX_VALUE) {
    data = data.filter(function (_, i) {
      return i >= (current - 1) * pageSize && i < current * pageSize;
    });
  }

  return data;
}

这是table的处理数据的源码
能否在这里添加一个函数,计算列表数值的和,输出时添加data的汇总数,page和pagesize减下刚加的数据就好 @sorrycc

after 2 years, this problem still exits.

after 2 years, this problem still exist.

我自己原来在非分页表格里使用修改重写col的render并追加数据的方式来做到:

image

but 分页的情况下,如果多传数据会被干掉 https://github.com/ant-design/ant-design/blob/fee5e291632cce131611dd1a9e2ab89e02ee43a3/components/table/Table.tsx#L407

我想PR这个,但是怕自己API设计不符合老板 @afc163 的要求...

after 2 years, this problem still exist.

the new table on the way .Looking forward to the new table

https://github.com/ant-design/ant-design/issues/16911#issuecomment-523423460

@alexchen1875 That did not mention anything related to this.

@alexchen1875 That did not mention anything related to this.

Read the instructions on the table section carefully.

@alexchen1875 awesome!

我想提交个PR,临时加个“允许data不裁剪数据”的props,来支持跳过https://github.com/ant-design/ant-design/blob/61e319b66826530a1882c20a41862a15d57b120a/components/table/Table.tsx#L408 的filter逻辑,使 https://github.com/ant-design/ant-design/issues/5710#issuecomment-524164125 在分页的情况下也可用,不知道团队会不会接受...

I modified this demo,
added support for fixed columns , header grouping , cells merging and scroll ,
I used \ and paging will not be affected.
Temporarily used in my project

👉 new demo address

usage :

const calcRow = [
    {
        colspan: 2,
        render: () => 'total'
    },
    {
        render: sum => sum('age')
    },
    {
        render: sum => sum('score')
    }
]

image

antd v4 had supported Table summary: #21656

image

https://ant.design/components/table/#components-table-demo-summary

antd4支持了summary,不过我感觉这个summary还是要继续迭代,现在的使用方式虽然写法自由,但是同时带来的影响是代码过于繁琐,不太符合antd4越来越简单的初衷

常见的简单场景就是对于当前页面的数据进行summary,友好体验应该是在cloumn上直接配summary:true,那样代码会整洁、统一很多
复杂场景可以使用目前的使用summary总结栏

Was this page helpful?
0 / 5 - 0 ratings