Browse Source

Merge pull request #651 from ant-design/remove-table-datasource

Remove remote mode datasource of Table
pull/658/head
afc163 9 years ago
parent
commit
c7bcea8559
  1. 113
      components/table/demo/ajax.md
  2. 14
      components/table/demo/local-data.md
  3. 2
      components/table/demo/locale.md
  4. 2
      components/table/demo/nopagination.md
  5. 2
      components/table/demo/paging.md
  6. 2
      components/table/demo/row-selection.md
  7. 8
      components/table/filterDropdown.jsx
  8. 181
      components/table/index.jsx
  9. 55
      components/table/index.md
  10. 2
      package.json
  11. 1
      scripts/demo.js

113
components/table/demo/ajax.md

@ -1,15 +1,20 @@
# 动态加载数据
# 远程加载数据
- order: 7
远程读取的表格是**更为常见的模式**,下面的表格使用了 `dataSource` 对象和远程数据源绑定和适配,并具有筛选、排序等功能以及页面 loading 效果
`0.11.0` 以后,`dataSource` 远程模式被移除,用户可以自行实现数据读取方式
**注意,此示例是静态数据模拟,数据并不准确,请打开网络面板查看请求。**
这个例子通过简单的 ajax 读取方式,演示了如何从服务端读取并展现数据,具有筛选、排序等功能以及页面 loading 效果。开发者可以自行接入其他数据处理方式。
另外,本例也展示了筛选排序功能如何交给服务端实现,列不需要指定具体的 `onFilter``sorter` 函数,而是在把筛选和排序的参数发到服务端来处理。
**注意,此示例是静态数据模拟,展示数据不会变化,请打开网络面板查看请求。**
---
````jsx
import { Table, Button } from 'antd';
import { Table } from 'antd';
import reqwest from 'reqwest';
const columns = [{
title: '姓名',
@ -30,23 +35,20 @@ const columns = [{
dataIndex: 'address'
}];
const dataSource = new Table.DataSource({
url: '/components/table/demo/data.json',
resolve: function(result) {
return result.data;
},
data: {},
// 和后台接口返回的分页数据进行适配
getPagination: function(result) {
const Test = React.createClass({
getInitialState() {
return {
total: result.totalCount,
pageSize: result.pageSize
data: [],
pagination: {},
loading: false,
};
},
// 和后台接口接收的参数进行适配
// 参数里提供了分页、筛选、排序的信息
getParams: function(pagination, filters, sorter) {
console.log('getParams 的参数是:', pagination, filters, sorter);
handleTableChange(pagination, filters, sorter) {
const pager = this.state.pagination;
pager.current = pagination.current;
this.setState({
pagination: pager
});
const params = {
pageSize: pagination.pageSize,
currentPage: pagination.current,
@ -56,61 +58,38 @@ const dataSource = new Table.DataSource({
for (let key in filters) {
params[key] = filters[key];
}
this.fetch(params);
},
fetch(params = {}) {
console.log('请求参数:', params);
return params;
}
});
const Test = React.createClass({
getInitialState() {
return {
dataSource: null,
pagination: {
onChange: this.hanlePageChange
this.setState({ loading: true });
reqwest({
url: 'demo/data.json',
method: 'get',
data: params,
type: 'json',
success: (result) => {
const pagination = this.state.pagination;
pagination.total = result.totalCount;
this.setState({
loading: false,
data: result.data,
pagination,
});
}
};
},
hanlePageChange(page) {
// 使用受控属性 current,方便外部设置页数
const pagination = this.state.pagination;
pagination.current = page;
this.setState({
pagination,
dataSource: dataSource.clone(),
});
},
refresh() {
// 回到第一页
const pagination = this.state.pagination;
pagination.current = 1;
this.setState({
pagination,
dataSource: dataSource.clone(),
});
},
changeAndRefresh() {
// 回到第一页
const pagination = this.state.pagination;
pagination.current = 1;
// 可以修改原来的 dataSource 再发请求
this.setState({
pagination,
dataSource: dataSource.clone({
data: { city: 'hz' }
}),
});
componentDidMount() {
this.fetch();
},
render() {
return <div>
<Table columns={columns} dataSource={this.state.dataSource} pagination={this.state.pagination} />
<Button type="primary" onClick={this.refresh}>
加载初始数据
</Button>
&nbsp;
<Button onClick={this.changeAndRefresh}>
加载 city=hz 的数据
</Button>
</div>;
return (
<Table columns={columns}
dataSource={this.state.data}
pagination={this.state.pagination}
loading={this.state.loading}
onChange={this.handleTableChange} />
);
}
});

14
components/table/demo/local-data.md

@ -1,6 +1,7 @@
# 外界控制数据
- order: 11
- hidden: true
由父元素控制自身数据展示。
@ -44,7 +45,7 @@ const data2 = [{
age: 32,
address: '西湖区湖底公园2号'
}, {
key: '22',
key: '2',
name: '胡彦祖2',
age: 42,
address: '西湖区湖底公园2号'
@ -72,8 +73,17 @@ const App = React.createClass({
});
},
render() {
// 通过 rowSelection 对象表明需要行选择
const rowSelection = {
onSelect: function(record, selected, selectedRows) {
console.log(record, selected, selectedRows);
},
onSelectAll: function(selected, selectedRows) {
console.log(selected, selectedRows);
}
};
return <div>
<Table columns={columns} dataSource={this.state.data} />
<Table columns={columns} dataSource={this.state.data} rowSelection={rowSelection} />
<Button onClick={this.handleClick1}>加载本地数据1</Button>
&nbsp;
<Button onClick={this.handleClick2}>加载本地数据2</Button>

2
components/table/demo/locale.md

@ -51,8 +51,6 @@ const data = [{
}];
const locale = {
sortAscend: 'Sort ascending',
sortDescend: 'Sort descending',
filterTitle: 'Filter',
filterConfirm: 'Confirm',
filterReset: 'Reset'

2
components/table/demo/nopagination.md

@ -2,7 +2,7 @@
- order: 8
传入 pagination 为 false 即可。
传入 pagination 为 false 即可。此时表格将完整显示 dataSource 内的数据,不进行任何分页。
---

2
components/table/demo/paging.md

@ -35,7 +35,7 @@ for (let i = 0; i < 46; i++) {
const pagination = {
total: data.length,
current: 2,
current: 1,
showSizeChanger: true
};

2
components/table/demo/row-selection.md

@ -2,7 +2,7 @@
- order: 1
第一列是联动的选择框,rowSelection 中配置 `type="radio"` 可设为单选
第一列是联动的选择框。
---

8
components/table/filterDropdown.jsx

@ -71,18 +71,10 @@ let FilterMenu = React.createClass({
</Menu>
<div className="ant-table-filter-dropdown-btns">
<a className="ant-table-filter-dropdown-link confirm"
style={{
cursor: 'pointer',
pointerEvents: 'visible'
}}
onClick={this.handleConfirm}>
{locale.filterConfirm}
</a>
<a className="ant-table-filter-dropdown-link clear"
style={{
cursor: 'pointer',
pointerEvents: 'visible'
}}
onClick={this.handleClearFilters}>
{locale.filterReset}
</a>

181
components/table/index.jsx

@ -1,5 +1,4 @@
import React from 'react';
import reqwest from 'reqwest';
import Table from 'rc-table';
import Checkbox from '../checkbox';
import Radio from '../radio';
@ -13,49 +12,19 @@ import classNames from 'classnames';
function noop() {
}
function defaultResolve(data) {
return data || [];
}
const defaultLocale = {
filterTitle: '筛选',
filterConfirm: '确定',
filterReset: '重置'
};
class DataSource {
init(config) {
this.config = config;
this.url = config.url || '';
this.resolve = config.resolve || defaultResolve;
this.getParams = config.getParams || noop;
this.getPagination = config.getPagination || noop;
this.headers = config.headers || {};
this.data = config.data || {};
}
constructor(config) {
if (config) {
this.init(config);
}
}
clone(config = {}) {
return new DataSource(objectAssign({}, this.config, config));
}
}
let AntTable = React.createClass({
getInitialState() {
return {
//
selectedRowKeys: [],
// only for remote
data: [],
dataSource: this.props.dataSource,
filters: {},
selectionDirty: false,
loading: this.props.loading,
sortColumn: '',
sortOrder: '',
sorter: null,
@ -69,6 +38,7 @@ let AntTable = React.createClass({
getDefaultProps() {
return {
dataSource: [],
prefixCls: 'ant-table',
useFixedHeader: false,
rowSelection: null,
@ -82,7 +52,16 @@ let AntTable = React.createClass({
},
propTypes: {
dataSource: React.PropTypes.oneOfType([React.PropTypes.array, React.PropTypes.instanceOf(DataSource)])
dataSource: React.PropTypes.array,
prefixCls: React.PropTypes.string,
useFixedHeader: React.PropTypes.bool,
rowSelection: React.PropTypes.object,
className: React.PropTypes.string,
size: React.PropTypes.string,
loading: React.PropTypes.bool,
bordered: React.PropTypes.bool,
onChange: React.PropTypes.func,
locale: React.PropTypes.object,
},
getDefaultSelection() {
@ -112,42 +91,22 @@ let AntTable = React.createClass({
nextProps.dataSource !== this.props.dataSource) {
let selectedRowKeys = this.state.selectedRowKeys;
//
if (this.isLocalDataSource()) {
let currentPageRowKeys =
this.getLocalDataPaging(nextProps.dataSource).map(
(record, i) => this.getRecordKey(record, i)
);
selectedRowKeys = selectedRowKeys.filter((key) => {
return currentPageRowKeys.indexOf(key) >= 0;
});
}
let currentPageRowKeys =
this.getCurrentPageData(nextProps.dataSource).map(
(record, i) => this.getRecordKey(record, i)
);
selectedRowKeys = selectedRowKeys.filter((key) => {
return currentPageRowKeys.indexOf(key) >= 0;
});
this.setState({
selectionDirty: false,
selectedRowKeys,
dataSource: nextProps.dataSource,
loading: true
}, this.fetch);
}
if ('loading' in nextProps) {
this.setState({
loading: nextProps.loading
});
}
},
hasPagination(pagination) {
if (pagination === undefined) {
pagination = this.props.pagination;
}
return pagination !== false;
},
isLocalDataSource() {
return Array.isArray(this.state.dataSource);
},
getRemoteDataSource() {
return this.state.dataSource;
return this.props.pagination !== false;
},
toggleSortOrder(order, column) {
@ -167,7 +126,7 @@ let AntTable = React.createClass({
sortOrder = order;
}
}
if (this.isLocalDataSource()) {
if (typeof column.sorter === 'function') {
sorter = function () {
let result = column.sorter.apply(this, arguments);
if (sortOrder === 'ascend') {
@ -182,7 +141,7 @@ let AntTable = React.createClass({
sortColumn,
sorter
};
this.fetch(newState);
this.setState(newState);
this.props.onChange.apply(this, this.prepareParamsArguments(objectAssign({}, this.state, newState)));
},
@ -202,7 +161,7 @@ let AntTable = React.createClass({
selectionDirty: false,
filters
};
this.fetch(newState);
this.setState(newState);
this.props.onChange.apply(this, this.prepareParamsArguments(objectAssign({}, this.state, newState)));
},
@ -293,7 +252,7 @@ let AntTable = React.createClass({
selectionDirty: false,
pagination
};
this.fetch(newState);
this.setState(newState);
this.props.onChange.apply(this, this.prepareParamsArguments(objectAssign({}, this.state, newState)));
},
@ -391,10 +350,6 @@ let AntTable = React.createClass({
return columns;
},
getCurrentPageData() {
return this.isLocalDataSource() ? this.getLocalDataPaging() : this.state.data;
},
getColumnKey(column, index) {
return column.key || column.dataIndex || index;
},
@ -458,7 +413,7 @@ let AntTable = React.createClass({
let pagination = objectAssign(this.state.pagination, {
pageSize: pageSize
});
this.fetch({pagination});
this.setState({ pagination });
},
renderPagination() {
@ -470,16 +425,14 @@ let AntTable = React.createClass({
'ant-table-pagination': true,
'mini': this.props.size === 'middle' || this.props.size === 'small',
});
let total = this.state.pagination.total;
if (!total && this.isLocalDataSource()) {
total = this.getLocalData().length;
}
return (total > 0) ? <Pagination className={classString}
onChange={this.handlePageChange}
total={total}
pageSize={10}
onShowSizeChange={this.handleShowSizeChange}
{...this.state.pagination} /> : null;
let total = this.state.pagination.total || this.getLocalData().length;
return (total > 0) ?
<Pagination className={classString}
onChange={this.handlePageChange}
total={total}
pageSize={10}
onShowSizeChange={this.handleShowSizeChange}
{...this.state.pagination} /> : null;
},
prepareParamsArguments(state) {
@ -496,61 +449,13 @@ let AntTable = React.createClass({
return [pagination, filters, sorter];
},
fetch(newState) {
if (this.isLocalDataSource()) {
if (newState) {
this.setState(newState);
}
} else {
// remote 使 this.dataSource
let dataSource = this.getRemoteDataSource();
if (!dataSource) {
return null;
}
let state = objectAssign({}, this.state, newState);
if (newState || !this.state.loading) {
this.setState(objectAssign({
loading: true
}, newState));
}
let buildInParams = dataSource.getParams.apply(this, this.prepareParamsArguments(state)) || {};
return reqwest({
url: dataSource.url,
method: 'get',
data: objectAssign(buildInParams, dataSource.data),
headers: dataSource.headers,
type: 'json',
success: (result) => {
if (this.isMounted()) {
let pagination = objectAssign(
state.pagination,
dataSource.getPagination.call(this, result)
);
this.setState({
selectionDirty: false,
loading: false,
data: dataSource.resolve.call(this, result),
pagination: pagination
});
}
},
error: () => {
this.setState({
loading: false,
data: []
});
}
});
}
},
findColumn(myKey) {
return this.props.columns.filter((c) => {
return this.getColumnKey(c) === myKey;
})[0];
},
getLocalDataPaging(dataSource) {
getCurrentPageData(dataSource) {
let data = this.getLocalData(dataSource);
let current, pageSize;
let state = this.state;
@ -579,7 +484,7 @@ let AntTable = React.createClass({
getLocalData(dataSource) {
let state = this.state;
let data = dataSource || this.state.dataSource;
let data = dataSource || this.props.dataSource;
//
if (state.sortOrder && state.sorter) {
data = data.sort(state.sorter);
@ -595,22 +500,14 @@ let AntTable = React.createClass({
if (values.length === 0) {
return;
}
data = data.filter((record) => {
return values.some((v)=> {
return col.onFilter(v, record);
});
});
data = col.onFilter ? data.filter(record => {
return values.some(v => col.onFilter(v, record));
}) : data;
});
}
return data;
},
componentDidMount() {
if (!this.isLocalDataSource()) {
this.fetch();
}
},
render() {
let data = this.getCurrentPageData();
let columns = this.renderRowSelection();
@ -644,7 +541,7 @@ let AntTable = React.createClass({
expandIconAsCell={expandIconAsCell} />
{emptyText}
</div>;
if (this.state.loading) {
if (this.props.loading) {
// if there is no pagination or no data, the height of spin should decrease by half of pagination
let paginationPatchClass = (this.hasPagination() && data && data.length !== 0)
? 'ant-table-with-pagination'
@ -661,6 +558,4 @@ let AntTable = React.createClass({
}
});
AntTable.DataSource = DataSource;
export default AntTable;

55
components/table/index.md

@ -16,14 +16,10 @@
## 如何使用
Table 有两种模式,本地数据和远程数据模式。
**本地数据模式**是指数据一次性载入内存,纯前端进行分页、筛选、排序等功能。
通过指定表格的数据源 `dataSource` 为一个数据数组。
指定表格的数据源 `dataSource` 为一个数组。
```jsx
var dataSource = [{
const dataSource = [{
key: '1',
name: '胡彦斌',
age: 32,
@ -35,28 +31,22 @@ var dataSource = [{
address: '西湖区湖底公园1号'
}];
<Table dataSource={dataSource} />
```
**远程数据模式**是更常见的业务场景,是一次只从服务端读取一页的数据放在前端,执行筛选、排序、切换页码等操作时均向后台发送请求,后台返回当页的数据和相关分页信息。
> 远程数据模式后续可能删除,目前不推荐使用。
通过指定表格的数据源 `dataSource` 为一个 DataSource 的实例如下。
const columns = [{
title: '姓名',
dataIndex: 'name'
}, {
title: '年龄',
dataIndex: 'age'
}, {
title: '住址',
dataIndex: 'address'
}];
```jsx
var dataSource = new Table.DataSource({
url: '/api/users',
resolve: function(result) {
return result.data;
},
getPagination: function(result) {},
getParams: function(pagination, filters, sorter) {}
});
<Table dataSource={dataSource} />
<Table dataSource={dataSource} columns={columns} />
```
> 注:`dataSource` 在 `0.11.0` 版本后不再支持远程模式。
## API
### Table
@ -66,7 +56,7 @@ var dataSource = new Table.DataSource({
| rowSelection | 列表项是否可选择 | Object | | false |
| pagination | 分页器 | Object | 配置项参考 [pagination](/components/pagination),设为 false 时不显示分页 | |
| size | 正常或迷你类型 | String | `default` or `small`| default |
| dataSource | 数据源,可以为数组(本地模式)或一个数据源描述对象(远程模式) | Array or Object | | |
| dataSource | 数据数组 | Array | | |
| columns | 表格列的配置描述,具体项见下表 | Array | | 无 |
| rowKey | 表格列 key 的取值 | Function(recode, index):string | | record.key |
| expandIconAsCell | 设置展开 Icon 是否单独一列 | Boolean | | true |
@ -88,21 +78,10 @@ var dataSource = new Table.DataSource({
| filters | 表头的筛选菜单项 | Array | | |
| onFilter | 本地模式下,确定筛选的运行函数 | Function | | |
| filterMultiple | 是否多选 | Boolean | | true |
| sorter | 排序函数,本地模式下为一个函数,远程模式下为布尔值 | Function or Boolean | | 无 |
| sorter | 排序函数,本地排序使用一个函数,需要服务端排序可设为 true | Function or Boolean | | 无 |
| width | 列宽度 | String or Number | | 无 |
| className | 列的 className | String | | 无 |
### dataSource
远程数据源配置对象。
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------------|--------------------------|-----------------|---------------------|---------|
| url | 数据源地址 | String | | |
| resolve | 获得数据的解析函数,接收参数为远程数据返回的 result | Function | | |
| getPagination | 和后台接口返回的分页数据进行适配的函数,返回值会传给表格中的分页器 | Function | | |
| getParams | 和后台接口接收的参数进行适配,返回值会作为请求的参数发送 | Function | | 无 |
## 注意
按照 React 的[规范](http://facebook.github.io/react/docs/multiple-components.html#dynamic-children),所有的组件数组必须绑定 key。在 Table 中,默认将每列数据的 `key` 属性作为唯一的标识。

2
package.json

@ -65,7 +65,6 @@
"rc-upload": "~1.7.0",
"rc-util": "~3.0.1",
"react-slick": "~0.9.1",
"reqwest": "~2.0.5",
"semver": "~5.0.3",
"util-deprecate": "~1.0.1",
"velocity-animate": "~1.2.2",
@ -106,6 +105,7 @@
"react-dom": "~0.14.2",
"react-router": "~1.0.0",
"react-stateless-wrapper": "~1.0.2",
"reqwest": "~2.0.5",
"webpack": "^1.10.1",
"webpack-babel-jest": "^1.0.0",
"webpack-dev-middleware": "^1.2.0"

1
scripts/demo.js

@ -34,6 +34,7 @@ window.React = React;
window.ReactDOM = ReactDOM;
window['object-assign'] = require('object-assign');
window['classnames'] = require('classnames');
window['reqwest'] = require('reqwest');
require('./importCss');
antd.DatePicker.locale = {

Loading…
Cancel
Save