——上礼拜踩的坑
1、关于为什么不直接操作DOM对象?
因为微信小程序里没有document对象。
2、为什么坑了这多时间?
因为之前看了个过期的帖子,完美避开了解决方案。
下面进入正文,需求是在微信小程序里构造一棵文件树。
3、解决思路
定义一个自定义组件,并在这个组件里递归自己。
4、自定义组件.json文件
{"component": true,"usingComponents": {"alexTree":"path/to/component"}
}
由于组件要引用自己,所以这里定义了组件自己。
“alexTree”是组件里引用自己时用的标签名,理论上是可以自定义的,只要在wxml文件j里对应起来就好。
5、组件的property定义
properties: {treeBody:{type: Array,value:[],observer: function (newVal, oldVal, changedPath) {}},treeConfig:{type:Object,value:{haveOpGroup:false,haveIconGroup:false,opGroup:{label:"",icon:"",onTap:function(node){console.log("opGroup tapped.");}},tapOnNode:function(node){console.log("tapOnNode");}},observer: function(newVal, oldVal, changedPath) {}},},
主要定义了两个属性,一个是数据tree-body,一个是配置tree-config。
数据结构本身当然也是递归的。tree-body是一个数组(根目录),其中的元素作为文件,一个文件可以是一个普通文件,也可以是一个目录文件,用元素的isDir属性来区分,如下:
{filename:<String>isDir:<Boolean>data:<Array>/<Object>
}
filename是文件名,当isDir为true时,data为子文件数组,当isDir为false时,data为与文件相关的自定义数据,例如文件在服务器的url等。
6、 wxml大致结构。
<view wx:for="{{treeBody}}"><!-- 这里为当前节点的构造逻辑 --><view class='treeNode' catchtap="tapOnNode" data-node='{{item}}'> <<<<<<vviieew>>>>>>>>><!-- balabala --><!-- balabala --><!-- balabala --><!-- 然后判断此节点是否展开,下面的逻辑里,如果这个节点为展开状态的目录,就递归此组件 --><!-- 注意alexTree为上面json文件里定义的 --><view class='childNodes'><alexTree wx:if="{{item.isDir && item.expand}}" tree-body="{{item.data}}" tree-config="{{treeConfig}}" data-filename="{{item.filename}}" bindnodechanged="_handleChildNodeChanged"/></view>
</view>
7、点击节点更改目录展开状态
tapOnNode:function(e){// 点击节点时,折叠或展开结点(目录)var tmpNode = e.currentTarget.dataset.node;var filename = tmpNode.filename;var nodes = this.data.treeBody;var node = this._findNodeByName(nodes,filename);if(!node){return;}if(node.isDir){if(node.expand){delete node.expand;}else{node["expand"] = 1;}}this._refreshTree(nodes);// 调用treeConfig.tapOnNodeif(this.data.treeConfig.tapOnNode){this.data.treeConfig.tapOnNode();}},
8、另一个问题
传递给子节点的数据为深复制之后的数组,所以改变子节点的数据,不会影响到父节点的数据。当父节点目录折叠时,重新展开后,子节点的状态无法保存。
解决方案:当子节点数据改变时,抛出一个自定义事件,父节点捕捉这个事件,同时更改自己的数据。
// 刷新树_refreshTree:function(nodes){this.setData({treeBody : nodes,});// 然后向父结点抛出一个nodeChanged事件var filename = this.dataset.filename;var myEventDetail = { filename: filename,nodes: nodes,} // detail对象,提供给事件监听函数var myEventOption = {} // 触发事件的选项this.triggerEvent('nodechanged', myEventDetail, myEventOption)},_handleChildNodeChanged:function(e){// 处理子结点抛出的nodeChanged事件var filename = e.detail.filename;var nodes = this.data.treeBody;for (var i = 0; i < nodes.length; i++) {if (nodes[i].filename == filename) {nodes[i].data = e.detail.nodes;}}this.setData({treeBody: nodes,});},