美文网首页
5. 表格组件

5. 表格组件

作者: 萘小蒽 | 来源:发表于2020-09-07 10:43 被阅读0次

1. 构造数据

var headers = [
        "Book", "Author", "Language", "Published", "Sales"
      ];
      
var data = [
        ["The Lord of the Rings", "J. R. R. Tolkien", "English", "1954–1955", "150 million"], 
        ["Le Petit Prince (The Little Prince)", "Antoine de Saint-Exupéry", "French", "1943", "140 million"], 
        ["Harry Potter and the Philosopher's Stone", "J. K. Rowling", "English", "1997", "107 million"], 
        ["And Then There Were None", "Agatha Christie", "English", "1939", "100 million"], 
        ["Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754–1791", "100 million"], 
        ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"], 
        ["She: A History of Adventure", "H. Rider Haggard", "English", "1887", "100 million"],
      ];

2. 表头循环headers

  var Excel = React.createClass({
        render: function () {
            return (
                React.DOM.table(null,
                    React.DOM.thead(null,
                        React.DOM.tr(null,
                            this.props.headers.map(function (title, index) {
                                return React.DOM.th(null, title)
                            })))
                )
            )
        }
    })
    ReactDOM.render(
        React.createElement(Excel,{
            headers:headers,
            initialData: data
        }),
        document.getElementById("app")
    )

在传递节点给组件时,既可以传递一个单独的数组参数,也可以把每个子节点作为独立的参数传递。上面map方法返回的就是子节点组成的数组。


3. 优化调试

当我们没加key值的时候,调试中会抛出:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using<tr>.

在开发中这样并不知道具体的报错组件,因为<tr>元素可能在很多地方用到,我们需要在组件中添加一条displayName的属性:

var Excel = React.createClass({
        displayName:'Excel'
    })

这样报错就很具体了:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method ofExcel.

displayName属性每次都要写觉得麻烦?在jsx中将会被自动产生赋值。

4. key的作用
上面的报错已经知道发生在Excel的render()函数中,我们加上key:

this.props.headers.map(function (title,index) {
                                return React.DOM.th({ key: index }, title)
                            })

key属性只需要在该数组中保持唯一,不需要在整个react应用中唯一。

5. 添加表格内容
由于上面的data是二维的,因此需要双重循环:

data.map(function(row){
    return(
     React.DOM.tr(null,
       row.map(function(cell){
          return React.DOM.td(null,cell)
         })
       )
  );
})

需要考虑的问题:data变量数据哪里来?随后数据可能会发生变化,由于是表格用户可能会对表格进行排序,编辑等操作。


组件的state会发生变化,因此我们使用this.state.data保持跟踪数据变化,使用this.props.initialData进行组件初始化

6. 表格排序
在点击表头时,根据点击的所在行进行排序。

先在表头添加点击事件属性。

React.DOM.thead({ onClick: this._sort },
                        ....
),

_sort 方法

_sort: function (e) {
            var column  = e.target.cellIndex;
            var data = this.state.data.slice(); //由于数据深层是基本属性,浅拷贝可用,拷贝的数据不影响state。
            data.sort(function(a, b) {
                console.log(a,b)
            return a[column] > b[column] ? 1 : -1;
          });
          this.setState({
            data: data,
          });
        },

不要尝试去直接改变state,请使用setState()进行操作。

7. 表格排序的视觉提示
给表头添加一个箭头符号。为了跟踪新的状态,需要添加两个新属性。

  • this.state.sortby - 当前被选择的索引值。
  • this.state.descending - 降序和升序的标记。
getInitialState:function(){
  return {
   data:this.props.initialData,
   sortby:null,
   descending:false
   }
}

在_sort函数中,可以指定采用哪个方法进行排序。

_sort: function (e) {
            var column = e.target.cellIndex;
            var descending = this.state.sortby === column && !this.state.descending;
            var data = this.state.data.slice(); //由于数据深层是基本属性,浅拷贝可用,拷贝的数据不影响state。
            data.sort(function (a, b) {
                return descending
                    ? (a[column] < b[column] ? 1 : -1)
                    : (a[column] > b[column] ? 1 : -1);
            });
            this.setState({
                data: data,
                sortby: column,
                descending: descending,
            });
        },
render: function () {
            return (
            React.DOM.table(null,
              React.DOM.thead({onClick: this._sort},
                React.DOM.tr(null,
                  this.props.headers.map(function(title, idx) {
                    if (this.state.sortby === idx) {
                      title += this.state.descending ? ' \u2191' : ' \u2193'
                    }
                    return React.DOM.th({key: idx}, title);
                  }, this) //map方法的第二个参数将作为回调方法的内部this指向,如果不传,this则为全局对象
                )
              ),
              React.DOM.tbody(null,
                this.state.data.map(function(row, idx) {
                  return (
                    React.DOM.tr({key: idx},
                      row.map(function(cell, idx) {
                        return React.DOM.td({key: idx}, cell);
                      })
                    )
                  );
                })
              )
            )
          );
        }
排序完成

8. 表格可编辑数据
功能如下:

  • 双击一个单元格,excel找出点击的单元格,并把表格内容从简单的文本变为一个填充了原内容的输入框。
  • 编辑内容
  • 回车键后,输入框消失,原内容被修改。
    var headers = [
        "Book", "Author", "Language", "Published", "Sales"
    ];

    var data = [
        ["The Lord of the Rings", "J. R. R. Tolkien", "English", "1954–1955", "150 million"],
        ["Le Petit Prince (The Little Prince)", "Antoine de Saint-Exupéry", "French", "1943", "140 million"],
        ["Harry Potter and the Philosopher's Stone", "J. K. Rowling", "English", "1997", "107 million"],
        ["And Then There Were None", "Agatha Christie", "English", "1939", "100 million"],
        ["Dream of the Red Chamber", "Cao Xueqin", "Chinese", "1754–1791", "100 million"],
        ["The Hobbit", "J. R. R. Tolkien", "English", "1937", "100 million"],
        ["She: A History of Adventure", "H. Rider Haggard", "English", "1887", "100 million"],
    ];
    var Excel = React.createClass({
        displayName: 'Excel',
        getInitialState: function () {
            return {
                data: this.props.initialData,
                sortby: null,
                descending: false,
                edit: null
            };
        },
        _sort: function (e) {
            var column = e.target.cellIndex;
            var descending = this.state.sortby === column && !this.state.descending;
            var data = this.state.data.slice(); //由于数据深层是基本属性,浅拷贝可用,拷贝的数据不影响state。
            data.sort(function (a, b) {
                return descending
                    ? (a[column] < b[column] ? 1 : -1)
                    : (a[column] > b[column] ? 1 : -1);
            });
            this.setState({
                data: data,
                sortby: column,
                descending: descending,
            });
        },
        _dblclick: function (e) {
            this.setState({
                edit: {
                    cell: e.target.cellIndex,
                    row: parseInt(e.target.dataset.row, 10)
                }
            })
        },
        _submit: function (e) {
            e.preventDefault();
            var input = e.target.firstChild;
            var data = this.state.data.slice();
            data[this.state.edit.row][this.state.edit.cell] = input.value;
            this.setState({
                edit: null,
                data: data,
            });
        },
        componentWillMount() {

        },
        render: function () {
            return (
                React.DOM.table(null,
                    React.DOM.thead({ onClick: this._sort },
                        React.DOM.tr(null,
                            this.props.headers.map(function (title, idx) {
                                if (this.state.sortby === idx) {
                                    title += this.state.descending ? ' \u2191' : ' \u2193'
                                }
                                return React.DOM.th({ key: idx }, title);
                            }, this)
                        )
                    ),
                    React.DOM.tbody({ onDoubleClick: this._dblclick },
                        this.state.data.map(function (row, rowidx) {
                            return (
                                React.DOM.tr({
                                    key: rowidx,
                                },
                                    row.map(function (cell, cellidx) {
                                        var content = cell;
                                        var edit = this.state.edit;
                                        if (edit && edit.row == rowidx && edit.cell == cellidx) {
                                            content = React.DOM.form({ onSubmit: this._submit },
                                                React.DOM.input(
                                                    {
                                                        type: 'text',
                                                        defaultValue: cell
                                                    }
                                                )
                                            )
                                        }
                                        return React.DOM.td({ key: cellidx, 'data-row': rowidx }, content);
                                    }, this)
                                )
                            );
                        }, this)
                    )
                )
            );
        }
    })
    ReactDOM.render(
        React.createElement(Excel, {
            headers: headers,
            initialData: data
        }),
        document.getElementById("app")
    )

如上,单元格只有一个输入框。

相关文章