关于表格数据变更的一种处理方式

讨论 已结 精帖 81 6423
岁月小偷
悬赏:500飞吻
表格数据变更,一般包括几个内容:新增、修改、删除、移动,开发中经常会面临的一个问题就是变更之后如何将数据同步到节点上,一直以来个人的建议还是利用表格重载,不管是url模式的还是data模式的实际都是需要重载,url重载自然会重新请求后台得到最新的数据,data模式一般就是对data的操作,之后重新以新的data去渲染出来。
但是,同时会考虑的是如何尽量减少请求,可能感受最深的就是update操作,为了要更新这一条记录而重载整个表格,请求一遍数据感觉划不来,那么一般来说就可以利用表格的tool事件中的obj.update这个方法去更新,不过具体使用中就会发现其诸多的不足的地方,这个帖子就是主要针对这些不足进行一个处理给出一个tablePlug.update的方法,然后进而衍生出add和remove和move,同时新增了更新统计行数据的方法;另外针对处女座星人开发的一个refresh,有小伙伴说加了智能重载了效果是好了,但是还是有问题,就是下面的page模块也会重新渲染,在加了定时刷新的时候要操作page操作不了,实际是可以操作的,只不过会失去焦点,应为page是会重构的,那么可以连page也不刷新只刷新数值吗?好吧~方法是想出来的,新增一个tablePlug.refresh(处女座星人必看)
一、update
正如上面说的obj.update(data)有诸多限制,优点上来说就是用最小的修改代价,实现了数据的更新,他就更新参数中的data中的键的数据,不会整个行更不会整个table的节点更新;缺陷是底层的实现逻辑有点问题:
1,是通过遍历data,更新缓存cache中对应的记录的key的value,然后根据cols的配置信息更新td的内容,但是如果是想要更新toolbar列的话就没戏,目前解析的只有templet的,所以如果想要更新toolbar的话基本就只能设置成templet,而且要给这个列添加一个field,才有理论上的可能;
2,toolbar列即使加了field改成templet也未必能更新过来,因为内部的实现逻辑是先判断原始的data是否有这个key,所以如果field命名是原始的data里面没有的,后面用obj.update也更新不进去的,这个是一个比较大的限制,因为拿我们项目来说,后台给我们的数据如果原始的记录里面没有这个key的值他不会给一个key: ''的,那么后面要想利用obj.update这个key就变得不可能,除非利用parseData在渲染之前对后台给的数据做一个人工的初始化把对应的key添加上,但是可想而知有多麻烦;
3,数据他是一个一个更新进去,然后更新一个值就更新对应的td,但是这个就存在另外一个风险了,就是遍历对象他是无序的,比如update{a: 1, b:2},如果a字段的cols中会用到b字段的值做一个处理再显示出来,那么如果遍历顺序是先更新a的值,然后就开始更新a的td的内容,这个时候cache中b的值还是旧的不是你要更新进去的2,等到更新了b字段了他又不能说检测到其他字段有使用了这个字段会去再次更新对方的内容,这就导致了a出来的结果还是错的;
4,更新了统计列的某一个值统计行的对应数据没有重新计算;

总结的来说就是,obj.update实现的还是太过理想化太过简单,一条记录从数据上来说每个key是独立的这个没什么问题,但是到页面显示就不然了,因为页面的内容它不一直是单个字段的简单值显示,还会进行一些特殊处理,所以需要一个templet来转化,来自定义,所以有可能一个td里面会用到多个字段这个很正常,工具列的按钮也会根据数据的状态去决定部分按钮是否显示等等。所以个人认为要更新这个数据不能是一个独立的小单元的更新,而是先update这一行的数据然后在update这一行,而不是遍历被update的key一个个更新,再往大了看,实际这个表的记录也是一个整体,也是不能说你改了这条记录其他的记录必定是不变的,不排除某个字段的td他会根据当前页面的同一个field做了什么处理现实,比如统计行,所以目前的思路就是直接将值先update到cache中,然后再调用table内部的渲染tr td的内容。
大致的代码:

前面是针对参数做了一些处理让参数更加灵活,最关键的是后半部分的更新cache的部分,还要一个最关键的renderData的方法:

他的作用就是将cache中的数据重新解析渲染一遍,同时针对是否是移动数据还有默认点击那一条记录的处理,但是核心是渲染cache,调用table.js内部的renderData。

使用场景:
1,知道当前编辑修改的是那一条记录,可以看看一个最常用的场景就是点击编辑弹出一个form然后修改提交,完成之后希望尽量不要重新请求接口更新到data和页面中去,
gif很不好录,自己使用测试的例子里面的编辑按钮测试效果即可
调用的更新数据的形式是:
tablePlug.update(表格实例的id, 当前tr的index, newData)

2,不知道当前的trIndex的情况下update某一条记录的话,必须有一个限制就是必须是有主键的表格,并且更新的数据中必须包含主键的字段,不然你也不知道更新的到底是哪条记录,
tablePlug.update('demo', {id: 10002, username: '贤心'});
3,一次性更新多条记录,这个参数trIndex就没有意义了,加了也没用,因为是更新多条记录,所以可以这么写
tablePlug.update('demo', [{id: 10002, username: '贤心'}, {id: 10006, username: '小偷', age: 18, sign: '大叔'}]);
这个测试页面可以看看头部toolbar中的“积分清零”还有“女性积分加100”这两个测试按钮以及背后的事件执行的方法

4,更加任性的,只要传入一个tableId,update会将当前按照cache中的数据给渲染一次,这个是非常实用的,比如如果你觉得我update中的逻辑针参数对cache的修改的逻辑不满意可以自己用自己觉得更好的方法去处理cache,最后执行一下tablePlug.update('demo')就好了,提供更高的自由度,和拓展的可能性。
可能有眼尖的小伙伴看了会提出一个问题:“实际你这个是会重新渲染整个表格的实体内容,包括tr th td和分页模块,如果我要改的是很小的有时候就是某一行的某一个field的值,这个动作是不是太大了”。个人的看法是如果一个东西效率很高但是出来的结果是错的,跟另外一个会消耗多一些资源,但是出来的结果是对的,这两个东西摆在面前给我选的话我会毫不犹豫选择后者。如果连结果都不对再快有什么用?但是后续会尽量的优化这块的逻辑让消耗的资源尽量的少。
二、addData
如果之前看过我那个新增临时数据区的帖子就知道目前已经有一个addTemp了,那跟addData的区别是什么?简单的说就是addTemp添加的是临时数据,addData添加的记录是已经请求接口完成返回的数据记录,本质上来说就是不一样的,所以不要混淆。
具体addData的代码:

对于添加记录我个人一直坚持的原则就是只有data模式才可以有优化处理的可能,如果是url,还是要直接reload最简单最严谨,因为表格,采用url之后,特别是在分页的情况下,有查询条件,有排序规则,这些得到的记录都是要满足的,但是如果你请求接口添加一条记录,你能给我准确的说他出现在当前表格的哪一个位置吗?也许新增的记录不能满足where所以是不应该出现的,即使满足了查询条件,那么排序呢?新增就是添加到最后?这不可能,新增的记录也可能在当前的排序规则下排到中间的某个位置上,所以这块不是说想不想处理的问题,是能不能处理的问题了,所以没必要做任何多余的动作,直接reload就好。
data模式的话,实际也是往data里面添加一些记录,然后也是再reload一下。
 // 添加单条记录:
tablePlug.addData ('demo', {id: newId, username: '甲'});
// 添加多条记录
tablePlug.addData ('demo', [{id: newId1, username: '乙'},{id: newId2, username: '丙'}]);
关于addData的有一个比较综合的例子可以看看利用table的data模式怎么跟流加载配合使用,弄成一个流加载的表格
https://sun_zoro.gitee.io/layuitableplug/testTableFlow
三、del
新增和删除实际个人建议还是reload比较稳妥,不管是url还是data模式都是,所以删除对应的处理方式也跟新增实际差不多,只不过删除麻烦一点的就是data模式要在原始的记录里面去删除指定的记录,而且有可能开启了复选的状态记忆删除了就要将关于他的状态给调整一下;还是为了使用更方便,参数同样做了处理,
1,删除指定的下标的数据,可以查看表格行的toolbar中的删除按钮的监听处理,但是注意,如果表格是url的模式,目前测试页面写的都是json文件,所以reload也不会有效果的,所以要测试请在data模式的测试,不用纠结这个,url的如果是实际的服务接口的话是后台返回数据,一般删除成功了后面查询是不会再出来的,除非后台接口有问题。

2,删除指定的一些记录,这个一般有两种形式,但是要求一样就是必须是有主键的表格
// id集合
tablePlug.del('demo', [1,2,3,4]);
// 对象数组
tablePlug.del('demo', [{id: 1, name: 'name1'}, {id:2}, {id:4}]);
根据得到哪种数据比较方便就用哪种形式,可以参考测试页面的批量删除的处理方式

四、move
这个处理基本跟update差不多,将数据在cache中调整位置,然后调用一下组件内部的renderData的方法让他重新渲染出来就好,

然后为了使用方便衍生出来一个上移跟下移的方法

效果

理论上利用一些拖拽事件或者其他的插件在事件中调用一下tablePlug.move('demo', form, to);就能够实现顺序的任意改变了;
限制:注意!这个只是针对数据移动,不会有单条数据记录的变动,如果原始的数据里面有点击了排序,那么移动之后默认是会去掉这个排序的状态了的,因为移动之后很可能就不能满足当前的排序规则了,所以建议在使用移动的时候不要跟sort搭配,如果有sort而且所谓的移动是会发起请求改变数据的,那么这个建议还是使用请求接口得到两个新的数据然后用update去更新他们的位置。
五、renderTotal
在记录更新之后,如果存在统计行有需要统计的列,那么值一般也要跟着变,另外一个更加重要的作用就是可以自定义统计规则,而不是自带的求和,可以自定一定计算的函数,或者可以直接类似templet一样的去自定义返回的内容,包括异步的去读取想要显示的数据,代码大概如下:

从实现代码可以看出就是给cols的字段配置新增一个totalFormat的设置,可以设置一个规则如果不设置的话就是sum(目前也只是添加了sum,其他的规则后面会加入或者自己加入,比如平均,最大最小不过个人觉得主要意义是可以自定义方法,这个才是实用常用的),也可以设置一个方法,如果不是异步的可以直接把结果返回,如果是需要异步的那么也可以在得到最终想要的结果的时候执行:tablePlug.renderTotal(tableId, field, res);比如下面的

平时实用的话不是都要自己去调用的,在插件内部已经在renderDone回调里面会去执行他了:

参数也是比较自由,不同的组合会有不同的效果,
// 触发更新某个表格的所有列的统计数据
renderTotal(tableId);
// 触发更新某个表格的某个字段的数据
renderTotal(tableId, fieldName);
// 更新某个表格的某个字段的统计数据为value
renderTotal(tableId, fieldName, totalValue);
六、refresh
之前做过一个智能reload的修改,即在执行table.reload的时候会根据传过去的option来判断只是重新请求数据还是需要重载一下,个人觉得效果可以了,不过对于有强迫症(有追求)的小伙伴来说,在一些场景下还是不够好,就是那些定时刷新的,表现就是一方面滚动条会回到top:0,left:0,还有其他的比如鼠标在操作分页组件的时候会觉得失去焦点,体验不好,好吧经过一番折腾,新增一个tablePlug.refresh来试一试能否满足要求。
先看效果:

事件背后做的事情:

表格config:

背后的实现思路,

修改table的Class.prototype.pullData支持refresh模式(代码较多不方便截图,有兴趣的可以自己阅读一下代码基本主要搜索refresh就基本知道大致思路了)
renderData的时候根据是否refresh去做一些细节的处理,还有一个限定就是返回的数据中关于总数应该是不变的,如果发生了改变,那么还是会renderData,会重新渲染page组件,还是那句话,在正确与体验好的选择的话还是要优先正确的结果;另外一个限制就是这种refresh的表格不建议再加什么按钮呀edit呀,因为它一直会在变,基本主要就是用来做一个单纯用来显示用的表格,比如一些经常变化的数据,访问人次,股票动态之类的。
使用:
// 启动表格demo的自动刷新功能,500毫秒一次
tablePlug.refresh('demo', 500);
// 取消表格demo的自动刷新
tablePlug.refresh('demo', false);
// 停止所有已开启自动刷新的表格的自动刷新
tablePlug.refresh(false);
回帖
  • PetStone
    2019-5-5
    6666666666666
    0 回复
  • [微笑]
    5 回复
  • @linknat 要多难也不会,就是要处理一下,改一下optimizeSelectOption.js的一些逻辑,新增一些选择器的事件监听,然后修改一下回调处理和一些其他的事件处理,但是如果从扩展性和后面升级方便性考虑的话我得想想如何处理更好
    4 回复
  • [good]
    2 回复
  • 高手啊,[good]
    1 回复
  • 渣渣晖
    2019-4-29
    牛逼阿[good]
    1 回复
  • @linknat 多选下拉的目前已经有一个很优秀的formSelect了,使用上来说跟select没太大的区别呀,你说的加入是什么意思呢?结合还是?
    @白小白兔 @沉默死海 @肥到模糊 @雷锋2班红领巾 @aaaxxx thanks[微笑]
    1 回复
  • @岁月神偷 [哈哈] 是的,结合起来
    目前 formSelect 在 table 中使用也会出现不显示的问题
    折腾了好久不知道怎么把 多选框 放在 layer 中显示
    1 回复
  • [good] [good] [good]
    1 回复
  • @linknat 关于formselects在表格中显示的调试了一下,发现不能跟layui的select处理完全一样,这个是内部的逻辑导致的,layui的事件处理啥的,是在render的时候加上的而且基本都是jquery对象之间的处理,但是formSelects它有一部分的处理是通过当前的节点然后去找到它对应的其他的有关系的dom节点,所以layui的select的即使将他原始的option节点拿到其他地方取,点击之后他还是能响应到原始的title和select中,但是formselects就不行了,因为拿出去之后他通过节点找到另外的节点的时候原始的逻辑就找不到了,所以得有别于layui的select的处理,将dl 跟title等整套的copy一份拿出去,然后layer关闭的时候再做一些处理将layer中的copy出来对象给覆盖回去原始的,这样子就能够做到想要的效果,如果觉得这样子可以的话我整理整理一下push上去
    1 回复