🐝 el-cascader 组件清除内容报错

1,098 阅读3分钟

Hello 各位前端的小伙伴,你们好!


今天分享一篇关于 el-cascder(级联选择器) 组件的报错

关于 cascader 组件报错;Error in callback for watcher 'options': 'TypeError: Cannot read properties of null (reading 'level')'

报错信息

"[Vue warn]: Error in callback for watcher 'options': 'TypeError: Cannot read properties of null (reading 'level')'

cascader-01.png


需求:

cascader-02.png

名称为 select 组件;组织为 cascader 组件;均有可清空属性 clearable

目前的情况时,我用户选择 名称,然后拿到相关 名称 ID;然后请求 组织 数据;然后在给 组织 组件 传过去;改变 cascader 的 options;目前全部都是正常的;组织 和 名称 单独点击清空;无报错;直到 名称和组织 的组件上都选中数据,然后点击 名称的 清除(clearable),(组件 cascader 的数据 依赖于 select 数据;当 select 清空 cascader 数据也得清空);就产生了以上报错;


原因

出现在syncActivePath函数中解构 activePath,此时的 activePath为上一次的选中的那么在getNodeByValue函数中获取到的是null,即 expandNodes的 nodes为 [null],那么在handleExpand中就出现null.level

我理解的意思就是,当用户选择了 cascader 组价中的数据时,会被打上高亮效果;选择的数据被储存在activePath中;当我切换依赖数据时,cascader 组件的 options 会被清空;然后 父组件 根据依赖的数据再次请求 cascader 的数据,并传递给 cascader 组件的options; 再次触发 cascader 组件,但是上次选中的 activePath没有被清空,所以他在 本次数据的 options 中 找上次的高亮的数据;没有找到;所以会有以上报错;


解决方案:

  • 让 cascader 组件 重新渲染(不推荐组件重新渲染,消耗性能)

    • key 方式

    • v-if 方式

      并且结合我的需求会发现 加 key 的方式重新渲染 cascader 组件;会渲染两次;当父组件 开始请求数据时 ,会将 cascader 的 options 置空,options 发生改变,cascader 组件会重新渲染;父组件请求到数据并传递给 cascader 组件的 options 时,又重新渲染一次;这个过程用户可以清楚的看到

     <template>
       <sec-cascader
         :key="casacderKey"
         ref="cascader"
         v-model="value"
         class="cascader"
         placeholder="请选择"
         :options="options"
         clearable
         :show-all-levels="true"
         filterable
         :props="{ expandTrigger: 'click', multiple: false, checkStrictly: true }"
         @change="handleChange"
       ></sec-cascader>
     </template>
     <script>
     export default {
       name: 'Cascader',
       props: {
         options: {
           type: Array,
           default: () => []
         },
         editValue: {
           type: Array,
           default: () => []
         }
       },
       data() {
         return {
           value: [],
           cascaderKey:1
         }
       },
       watch: {
         editValue: {
           handler() {
             if (this.editValue) this.value = [...this.editValue]
             return
           },
           immediate: true
         },
         opyions: {
           handler() {
            this.cascaderKey ++ 
           }
         }
       },
       beforeDestroy() {
         this.value = []
       },
       methods: {
         /**
          * @description: 清空用户选择的当前的数据 并且清空当前高亮的项
          */
         clearV() {
           this.$refs.cascader.$refs.panel.clearCheckedNodes()
         },
         /**
          * @description: 用户选择 组织 change 函数
          * @return {object[] || object}
          */
         handleChange() {
           this.$emit('cascaderHandler', () => {
             if (this.$refs.cascader.getCheckedNodes()[0] === undefined) {
               return { value: '', label: '' }
             } else {
               return this.$refs.cascader.getCheckedNodes()[0]
             }
           })
         }
       }
     }
     </script>
    

  • 重置 activePath

    • ref拿到Cascader组件

       this.$refs.cascaderRef.panel.activePath = []
       this.$refs.cascaderRef.handleClear()
      

我这使用的方法是,名称 被清空 或者发生 改变时 通过 ref 调用子组件的 clearV 方法;

 <template>
   <sec-cascader
     ref="cascader"
     v-model="value"
     class="cascader"
     placeholder="请选择"
     :options="options"
     clearable
     :show-all-levels="true"
     filterable
     :props="{ expandTrigger: 'click', multiple: false, checkStrictly: true }"
     @change="handleChange"
   ></sec-cascader>
 </template>
 <script>
 export default {
   name: 'Cascader',
   props: {
     options: {
       type: Array,
       default: () => []
     },
     editValue: {
       type: Array,
       default: () => []
     }
   },
   data() {
     return {
       value: [],
     }
   },
   watch: {
     editValue: {
       handler() {
         if (this.editValue) this.value = [...this.editValue]
         return
       },
       immediate: true
     }
   },
   beforeDestroy() {
     this.value = []
   },
   methods: {
     /**
      * @description: 清空用户选择的当前的数据 并且清空当前高亮的项
      * @return {*}
      */
     clearV() {
       // 清空当前高亮的项
       this.$refs.cascader.panel.activePath = []
       this.$refs.cascader.handleClear()
       this.$refs.cascader.$refs.panel.clearCheckedNodes()
     },
     /**
      * @description: 用户选择 组织 change 函数
      * @return {object[]}
      */
     handleChange() {
       this.$emit('cascaderHandler', () => {
         if (this.$refs.cascader.getCheckedNodes()[0] === undefined) {
           return { value: '', label: '' }
         } else {
           return this.$refs.cascader.getCheckedNodes()[0]
         }
       })
     }
   }
 }
 </script>

父组件:

 <script>
     export default {
         watch:{
             "名称":{
                   async handler(newV) {
                     // 清空 组织 数据
                     await this.$nextTick(() => this.$refs['myCascader'].clearV())
                     if (newV !== '' || newV !== null){
                         // 请求 组织 数据
                     }
                   }
             }
         }
     }
 </script>

目前 用这种方式解决了;没有再报错;如果有类似错误的 小伙伴 也可以试试哦;也可参考以下 大佬的文章;


上述是我自己的理解;
源文章(更详细):记一次 element ui Cascader 级联选择器报错 - 掘金 (juejin.cn)


本文章只是做个关于 el-cascader 组件的报错记录;仅供参考;

🎉 以上 代码 或者 文档 有不对的地方,还请各位大佬指正;🧐

拜拜~

lb04.jpg