昨天使用FineUIGrid写了一个购物车,主要是改变数量和单价计算总价的功能,纯前端。在实现时遇到了坑,解决后使用了闭包的结构优化,这里推演下应用的过程
1. 代码概览
首先购物车需要两个列,数量和单价,可以编辑,这里使用了 RendererFunction
进行绘制,绘制函数就是返回个 input
标签
购物车的项需要新增和删除,这里对应两个方法 adddata
和 removedata
,在修改数据后需要计算合计价格:updateTotal
,所以有代码如下,这里抽象一下,细节不重要
/**绘制输入数量*/
const renderNumber = function(v, { rowId, rowValue }, m, g){
return `<input id="Number_${rowId}" value="${v}" class="number" style="width:98%;" type="text">`;
}
/**
* 新增方法
* @param {[MsINVMB]} values 新增的数据数组
*/
function adddata(values){
F.ui.Grid1.addNewRecords(data, true);
//更新总价
updateTotal();
}
/**
* 删除方法
* @param {ID} rowId 删除的ID
*/
function removedata(rowId){
F.ui.Grid1.deleteRow(rowId, true);
//更新总价
updateTotal();
}
/**
* 更新总价
*/
function updateTotal(){
//计算总价
let res = GetSummary();
//更新总价
F.ui.Grid1.setSummaryData(res);
}
2. 问题
熟悉FineUI前端的能感觉到坑来了,调用过 addNewRecords
或 deleteRow
的同学都知道,这两个方法会重绘Grid,即触发 renderNumber
函数,造成输入的值会被重置,比如我在某行的数量输入了2,然后新增了一行,执行 addNewRecords
,然后输入的2没了,成默认项了,这时总价也不对了;这里不讨论为什么会触发绘制,对外可能是逆天的,但是对内是自洽的;
最直接的解决方法就是在 新增 或 删除 行之前先得到录入的值,执行新增后再给赋上,然后计算总价;所以多了两个方法 GetInputVal
和 SetInputVal
,改造如下;
/**
* 新增方法
* @param {[MsINVMB]} values 新增的数据数组
*/
function adddata(values){
let rawvalue = GetInputVal();
F.ui.Grid1.addNewRecords(data, true);
SetInputVal(rawvalue);
//更新总价
updateTotal();
}
/**
* 删除方法
* @param {ID} rowId 删除的ID
*/
function removedata(rowId){
let rawvalue = GetInputVal();
F.ui.Grid1.deleteRow(rowId, true);
SetInputVal(rawvalue);
//更新总价
updateTotal();
}
3. 优化
这时让我别扭的地方来了,我最讨厌割裂,明显 GetInputVal
和 SetInputVal
是一对,在调用时中间间隔其他的逻辑,而且他俩本身不参与其他逻辑,SetInputVal
只接收 GetInputVal
返回的值,rawvalue
外露没有意义还有可能被篡改,所以我感觉他俩应该是一个函数,为什么方法我还要写两个,我能不能塞到一个函数里,并且中间还可以跨其他的逻辑;
/**得到值*/
function GetInputVal(){}
/**设置值*/
function SetInputVal(values){}
这个场景下,我想到了使用闭包的方式,有一个函数 RestoreInputVal
,如果要跨过其他逻辑,这个函数返回的应该是 SetInputVal
的实现而不是调用,它应该这么写
/**还原值*/
function RestoreInputVal(){
const GetInputVal = ()=>...;
const SetInputVal = (values)=>...;
let rawvalue = GetInputVal();
return ()=>{
SetInputVal(rawvalue);
};
}
用的时候像这样
/**
* 新增方法
* @param {[MsINVMB]} values 新增的数据数组
*/
function adddata(values){
let restorval = RestoreInputVal();
F.ui.Grid1.addNewRecords(data, true);
restorval();
//更新总价
updateTotal();
}
/**
* 删除方法
* @param {ID} rowId 删除的ID
*/
function removedata(rowId){
let restorval = RestoreInputVal();
F.ui.Grid1.deleteRow(rowId, true);
restorval();
//更新总价
updateTotal();
}
这样就实现了 rawvalue
的闭包,以此作为一个典型的应用案例
其实要按照面向对象的思路应该是一个 class ,这里也不考虑继承扩展,所以就不深入了
class InputRestore {
#rawValue;
constructor() {
this.#rawValue = this.#getInputValue();
}
#getInputValue() {
// 原逻辑
}
#setInputValue(value) {
// 原逻辑
}
restore() {
this.#setInputValue(this.#rawValue);
}
}