单元格编辑要考虑的因素:
- 数据模型本身应该是啥样的:
- 数据模型的数据传达到cell:略
- cell数据的显示
- 必须有个编辑属性 表示该单元是否可编辑:cell_editing_model
- 根据是否可编辑属性,决定如何呈现一个单元格
-
if(this.props.commCell_editing_mode==0){ return ( <td className="ttd" onClick={e => this.clicked(e)} onDoubleClick={e => this.doubleClicked(e)} className={["ttd", alignCss,cell_font_family_css,cell_font_size_css, cell_font_weight_css, cell_font_style_css,cell_text_decoration_css, cell_border_top_css,cell_border_right_css,cell_border_bottom_css,cell_border_left_css, cell_cell_filter_brightness_css].join(' ')} rowSpan={_rowSpan} colSpan={_colSpan} style={{backgroundColor: this.props.commcellBackgroundColor}} > {this.getCommData(this.props.commValue)} </td> ) }else{ return ( <td className="ttd" onClick={e => this.clicked(e)} onDoubleClick={e => this.doubleClicked(e)} className={["ttd", alignCss,cell_font_family_css,cell_font_size_css, cell_font_weight_css, cell_font_style_css,cell_text_decoration_css, cell_border_top_css,cell_border_right_css,cell_border_bottom_css,cell_border_left_css, cell_cell_filter_brightness_css].join(' ')} rowSpan={_rowSpan} colSpan={_colSpan} style={{backgroundColor: this.props.commcellBackgroundColor}} > {this.getCommDataInput(this.props.commValue)} </td> ) }
- 双击事件,要改变单元格对应的edit 及 select 这2个值,
- 单击改变了什么:改变单元格对应的select值。
事件体系
- onchange
- 回车键 除了改变 value_mode外,还应该改变 edit_mode,及选择模型吧
- onblur 当当前输入框失去焦点的时候,会调用和回车键一样的事件
- 点击其他单元格对当前输入框的影响 点击其他单元格的时候,本来只是改变edit_mode 及select_mode.但会先触发blur事件
输入框回车键,:更改mode_value、edit_mode、selected_mode,按回车键,才能真正修改模型值?
/**
* Handles changing a cell, stores the new data state and stores into
* local model
*/
handleChangedCell = (i,j, value) =>{
const history = this.state.history_record;
const current =history.slice(history.length - 1)
const table_model = current[0];
//let cellSelectStates_s=table_model.cell_addFilter_bright_model ;
//let _cell_editing_model=table_model.cell_editing_model;
table_model.value_model[i][j].text=value;
//改变编辑状态和选择状态
this.transformState_edit(table_model,i,j,0,1);
this.setState({
history_record: history.concat(table_model),
});
}
选择其他单元格
onchange:到目前为止,我对onchange事件的理解为:改变显示的值,如果不按回车键,不会去修改value_model
ps:
在react中是无法直接更改from表单元素的值的,必须通过setState()去响应用户的输入。例如想要更改input的value,则需要监听onChange()事件,然后通过event.target.value来获取用户的输入,再通过设置一个名为value的state,来告诉react重新渲染。
onChange(event) {
this.setState({value: event.target.value});
}
<input value={this.state.value} onChange={this.onChange}/>
单元格编辑,呈现的时候,应该带上单元格样式,可以考虑带上单元格的宽度、高度,我们先简化处理,让input的width height 为100%
cell_editing_model 也是个二维数组,cell_editing_model:[i][j]=0 或者 cell_editing_model:[i][j]=0
value_model 是值的对象数组,value_model_init[i][j]={text:"xx",type:"String"};
新追加这2个描述数据与可编辑性的值,要相应调整追加行、追加列、插入行、插入列、删除行、删除列的代码。
追加行的时候,也是往cell_editing_model、value_model 追加一行数据。采取深度clone方式应该更保险。
还有个要注意的问题:当存在编辑框的时候,原则上,应该不容许添加 删除 行列,因为 我们编辑框修改数据的时候,要根据行、列坐标值
单元格公式:
核心是 hot-formula-parser的应用。
1、单元格公式的解析必须放到table组件当中,因为公式解析必须了解数据全貌
2、构造函数中初始化这个解析器
3、公式解析前进行必要的检测
4、写解析方法
constructor(props) {
......
// Initialize the formula parser on demand
this.parser = new FormulaParser()
....}
.... // check
// When a formula contains a cell value, this event lets us
// hook and return an error value if necessary
//Fired while retrieving cell value by its label (eq: B3, B$3, B$3, $B$3).
this.parser.on('callCellValue', (cellCoord, done) => {
const x = cellCoord.column.index + 1
const y = cellCoord.row.index + 1
//const x = cellCoord.column.index
//const y = cellCoord.row.index
// Check that the cell is not self referencing
if (this.parser.cell.x === x && this.parser.cell.y === y) {
throw this.parser.Error(this.parser.ERROR_REF)
}
let _value_model=
this.state.history_record[this.state.history_record.length-1].value_model;
// if (!this.state.data[y] || !this.state.data[y][x]) {
// return done('')
// }
if (!_value_model[y] || !_value_model[y][x]) {
return done('')
}
// All fine
//let r=this.state.data[y][x];
let t1=this.state.history_record[this.state.history_record.length-1].value_model;
console.log(t1);
let t2=t1[y][x];
console.log(t2)
return done(t2)
})
// When a formula contains a range value, this event lets us
// hook and return an error value if necessary
this.parser.on('callRangeValue', (startCellCoord, endCellCoord, done) => {
const sx = startCellCoord.column.index + 1
const sy = startCellCoord.row.index + 1
const ex = endCellCoord.column.index + 1
const ey = endCellCoord.row.index + 1
const fragment = []
let _value_model=this.state.history_record[this.state.history_record.length-1].value_model;
for (let y = sy; y <= ey; y += 1) {
//const row = this.state.data[y]
const row = _value_model[y]
if (!row) {
continue
}
const colFragment = []
for (let x = sx; x <= ex; x += 1) {
let value = row[x]
if (!value) {
value = ''
}
if (value.slice(0, 1) === '=') {
const res = this.executeFormula({ x, y }, value.slice(1))
if (res.error) {
throw this.parser.Error(res.error)
}
value = res.result
}
colFragment.push(value)
}
fragment.push(colFragment)
}
if (fragment) {
done(fragment)
}
})
}
....
/**
* Executes the formula on the `value` usign the FormulaParser object
*/
executeFormula = (cell, value) => {
this.parser.cell = cell
console.log(value);
let res = this.parser.parse(value)
if (res.error != null) {
console.log(res.error);
return res // tip: returning `res.error` shows more details
}
if (res.result.toString() === '') {
return res
}
if (res.result.toString().slice(0, 1) === '=') {
// formula points to formula
res = this.executeFormula(cell, res.result.slice(1))
}
return res
}
5、cell 显示的时候,调用解析函数
/**
* Executes the formula calculation on the cell value
*/
determineDisplay = ({ x, y }, valueObject) => {
//if (valueObject.text.slice(0, 1) === '=') {
if (valueObject.slice(0, 1) === '=') {
//const res = this.props.executeFormula({ x, y }, valueObject.text.slice(1))
const res = this.props.executeFormula({ x, y }, valueObject.slice(1))
if (res.error !== null) {
return 'INVALID'
}
return res.result
}
//return valueObject.text
return valueObject;
}