修复el-autocomplete组件监听change返回值始终是输入框的值而非选中的“输入建议”值

修复el-autocomplete组件监听change返回值始终是输入框的值而非选中的“输入建议”值

  • 需求
  • 问题
  • 环境
  • 重现代码
  • 原因
  • 解决方案
    • 发现问题
    • 终极方案
  • 剧终

需求

用户是在输入框输入值后模糊搜索某旅客表姓名
输入框需要在失焦后校验输入值的姓名格式

问题

模拟用户点击模糊匹配的旅客姓名列表后
提示格式错误

环境

项目 Value
电脑 M1 MBA2020
系统 BigSur 11.6
node 15.9
element-ui 2.15.6

重现代码

<template>
  <div id="app">
    <el-autocomplete v-model="test" :fetch-suggestions="querySearch" @select="handleSelect" @change="handleChange" >el-autocomplete>
  div>
template>
export default {
  data () {
    return {
      test: '',
      restaurants: [{ "value": "三全鲜食(北新泾店)", "address": "长宁区新渔路144号" }]
    }
  },
  methods: {
    handleChange (val) {
      alert(val)
    },
    handleSelect(item) {
      console.log(item);
    },
    querySearch(queryString, cb) {
      var restaurants = this.restaurants;
      var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
      // 调用 callback 返回建议列表的数据
      cb(results);
    },
    createFilter(queryString) {
      return (restaurant) => {
        return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
      };
    }
  }
}

code基本照搬 官网 el-autocomplete组件demo

原因

找到el-autocomplete组件源码:node_modules/elementui-vue/node_modules/element-ui/packages/autocomplete/src/autocomplete.vue
查找触发chang事件代码

handleChange(value) {
  this.$emit('change', value);
},

查找调用handleChange函数的代码

... other code
<el-input
  ref="input"
  v-bind="[$props, $attrs]"
  @input="handleInput"
  @change="handleChange"
  @focus="handleFocus"
  @blur="handleBlur"
  @clear="handleClear"
  @keydown.up.native.prevent="highlight(highlightedIndex - 1)"
  @keydown.down.native.prevent="highlight(highlightedIndex + 1)"
  @keydown.enter.native="handleKeyEnter"
  @keydown.native.tab="close"
>
... other code

可以发现el-autocomplete组件直接监听子组件input的change事件并原封不动把值抛出来

解决方案

当绑定了指令的组件被插入到dom时,修改组件的handleChange事件
注意:该方法仅适用于element-ui 2.13.2+版本,请看最终方案

  • 获取autocomplete组件实例,并修改handleChange函数
  • 判断是否已经展开输入建议,否则直接触发change,是则接下面
  • 给autocomplete组件创建观察函数和自定义事件
    • 1: 用户选择了输入建议-监听到select事件,移除观察函数,触发change事件并返回正确的值
    • 2: 用户不选择输入建议并输入框失焦使输入建议组件关闭-观察到autocomplete组件里的suggestions子组件的showPopper变化,移除观察函数,触发change事件
<el-autocomplete v-get-selected-value ...>
el-autocomplete>
  directives: {
    'get-selected-value': {
      inserted: function () {
        const vnode = arguments[2].child
        vnode.handleChange = val => {
          if (vnode.$refs?.suggestions?.showPopper) {
            const unwatch = vnode.$watch(() => vnode.$refs.suggestions.showPopper, function () {
              unwatch()
              vnode.$emit('change', val)
            })
            vnode.$once('select', item => {
              unwatch()
              vnode.$emit('change', item[vnode.valueKey])
            })
          } else {
            vnode.$emit('change', val)
          }
        }
      }
    }
  }

发现问题

当我把自定义指令代码搬公司项目代码上发现,输入框每input一次就会执行修改的handleChange函数!T-T心累~~
当我再次确认demo的代码没问题后,发现两个项目的element-ui版本不一样。然后分别打开两个项目的autocomplete.vue源码进行对比,发现版本是2.13.0的代码中,handleChange函数直接绑定给input监听,这就解释通了。

<el-input
  ref="input"
  v-bind="[$props, $attrs]"
  @input="handleChange"
  ...
  >

补充文章:element-ui v2.13.2

Bug fixes
Autocomplete
Fix change event bug (#19200 by @sxzz)

终极方案

不动你的handleChange函数,我直接给input子组件加个change事件监听,上代码!

directives: {
    'get-selected-value': {
      inserted: function () {
        const vnode = arguments[2].child
        // 弃用:vnode.handleChange = val => {
        // 这里$off是为了移除高版本代码中新增的@change监听,否则直接加代码会有两个监听事件,触发两次change
        vnode?.$refs?.input && vnode.$refs.input.$off('change').$on('change', val => {
          if (vnode.$refs?.suggestions?.showPopper) {
            const unwatch = vnode.$watch(() => vnode.$refs.suggestions.showPopper, function () {
              unwatch()
              vnode.$emit('change', val)
            })
            vnode.$once('select', item => {
              unwatch()
              vnode.$emit('change', item[vnode.valueKey])
            })
          } else {
            vnode.$emit('change', val)
          }
        })
      }
    }
  },

剧终

当然如果发现代码仍有BUG,欢迎指出

你可能感兴趣的:(VUE开发,javascript,vue.js,elementui,javascript)