1、背景
最近遇到一个需求,在drawer抽屉里使用elementui的级联选择器,需要级联动态加载,保存之后打开抽屉还要回显选中的值。一般情况下如果不是动态加载的情况,那么回显是很好解决的,把选中节点的value传给cascader的v-model绑定的值就可以了。但是现在是动态加载的,传值进去也不好使,这是什么原因呢,同时如果想要动态加载时支持搜索功能应该怎么做呢?打开cascader的源码看一看。
2、动态加载时无法回显的原因
首先,多选时cascader显示了tag,可以看见v-for循环了presentTags,再看图2,mounted里面执行了computePresentContent方法,而这个方法里面又执行了computePresentTags方法,再看图4,computePresentTags函数里面有一行代码“const checkedNodes = this.getCheckedNodes(leafOnly);”,而往下看逻辑checkedNodes是否有值决定了presentTags是否有值,所以我们来看一下getCheckedNodes方法。
图1
图2
图3
图4
到这里可以看见,getCheckedNodes实际上是调用了panel里面的getCheckedNodes方法,这也就说明了为什么动态加载时无法回显了:因为需要回显已选中值的话必然会调用panel的方法,而动态加载时此时数据都还没有,还等着用户去点节点然后加载出子节点,子节点有了才有panel。比如图6,一开始只有选项1这个节点,需要点击选项1后才会加载第二层的panel,如果你上次选中了选项2,之后想回显这个节点的话就必须初始化cascader组件的时候就把第二层节点这个panel渲染出来,而因为动态加载的原因,需要等待用户点击第一层节点才会有第二层的panel,所以相互矛盾,此时this.panel.getCheckedNodes方法就不存在,因为panel此时不存在。
图5
图6
3、动态加载时搜索
在非动态加载的情况下,cascader的搜索功能是可以正常使用的,但是在动态加载时也会搜索不到,看图7这个列表是搜索时渲染出来的列表,可以看见v-for循环了suggestions,那我们就看看suggestions如何得到的。看图8的getSuggestions函数,suggestions是通过this.panel.getFlattedNodes方法得来的,和上面同样的问题,动态加载时数据都还没有,也就没有panel,所以这个方法肯定不会返回值。其实严格来说这个也不叫搜索了,源码里面只是根据已有节点来过滤出输入的值,所以叫做过滤应该更合适。
图7
图8
4、解决方法
4.1 初始化时调接口渲染panel
遇到这个问题后我一开始想到了这种方法,即每次初始化时拿到value值后都去调接口获取节点数据,然后panel就会被渲染出来了。但是也要看具体的业务,如果存在同时选几十个不同层级的不同节点,那么初始化时就会调几十次接口,这肯定是不现实的,只有在“动态加载单选”或者动态加载且确定只可能选两三个的情况下比较适用。关于这方面的实现推荐去看社区里一个LazyCascader(点我前往)的库,作者用cascader-panel组件做了二次开发,回显的实现逻辑就是基于这个逻辑去实现的,同时作者还做了支持搜索的功能。
4.2 改交互
上面提到的lazyCascader库也是作者改了交互才实现的,改交互的方法有多种,但如果要实现回显,一定要记住一个重要原则,下面图9中的红色框和绿色框一定要解耦,只有这样才能成功实现回显。说一下我的做法,因为当时时间紧急,所以我的做法是,把cascader源码里面的el-tags(即图1那段)注释掉,然后自己在引用的cascader组件上面加一个div框来显示选中的值,即在红色框上面再加一个div框,这样每次进入drawer抽屉里面,把value值直接赋给上面的div框,这样就解耦了,就根本不关心此时cascader组件里面有有没有panel。搜索功能的话我是通过和后端商量,在before-filter方法里调后端接口,让后端返回搜索值对应的节点以及所有直接父节点组成的一个树结构给我,然后把返回的数据塞到options里面,这样就能显示出一条该值,然后visible-change为false时再把数据变回去,即每次关闭panel后下次再打开都只展示第一层级的panel,当然要看你的产品接不接受。但是如果时间多的话可以好好用用lazyCascader这个库的组件,在此基础上再根基自己的业务场景来修改,效果会更好。
图9