vue3自定义数字输入指令

vue3自定义数字输入指令

日常的开发过程中可能需要限制用户输入的内容,比如只能输入数字和小数点,又或者是数字加-等等需求,不用废话直接上代码。

import { Directive, ref, DirectiveBinding, Ref, unref, App, nextTick } from 'vue';
import { ElMessage } from 'element-plus'
import { debounce } from 'lodash';

export type Obj = {
  maxLength?: number
  reg?: RegExp
}
let obj: Ref<Obj> = ref({})
const inputValue = ref()
let reg: RegExp | any = /[^\d]/g


// 警告提示信息
export function warnMsg(msgInfo: string, arg?: any) {
  ElMessage({
    type: 'warning',
    showClose: true,
    dangerouslyUseHTMLString: true,
    message: msgInfo,
    ...arg
  })
}
export function parseJson(jsonStr: string) {
  return JSON.parse(jsonStr, (k, v) => {
    try {
      // 将正则字符串转成正则对象
      if (eval(v) instanceof RegExp) {
        return eval(v);
      }
    } catch (e) {
      // nothing
    }
    return v;
  });
}
/**
 * json对象转json字符串
 * @param { Object } json json对象
 */
export function stringifyJson(json: { [key: string]: any }) {
  return JSON.stringify(json, (k, v) => {
    // 将正则对象转换为正则字符串
    if (v instanceof RegExp) {
      return v.toString();
    }

    return v;
  });
}
function tip() {
  if (reg.test(inputValue.value)) {
    warnMsg('请输入数字!')
  }
}
// 派发自定义事件
const trigger = (el: HTMLElement, type: any) => {
  const e = document.createEvent('HTMLEvents');
  e.initEvent(type, true, true);
  el.dispatchEvent(e);
}

export const onlyNumber: Directive = {
   mounted(el: any, binding: DirectiveBinding, vnode: any) {
    const input = el?.children[0];
    if (binding.arg) {
      try {
        obj.value = parseJson(binding.arg)
        if (unref(obj).maxLength) {
          input.maxLength = unref(obj)?.maxLength
        }
        if (unref(obj).reg) {
          reg = unref(obj)?.reg
        }
      } catch (error) {
      }
    }
    if (binding.value) {
      reg = binding.value;
    }
    input.oninput = function (e: any) {
      inputValue.value = this.value;
      this.value = this.value.replace(reg, '');
      if (unref(obj).maxLength) {
        if (this.value.length === unref(obj).maxLength) {
          warnMsg(`最大输入${unref(obj).maxLength}个字符`)
        }
      }
       // 问题代码 ---> input 改为 update:modelValue
      if (vnode.dirs.length) {
        vnode.dirs[0].instance.$emit('input', this.value)
      }
    };
    input.addEventListener('input', debounce(tip, 500))
  },
  beforeUnmount(el: any, binding: any, vnode: any) {
    const input = el;
    input.removeEventListener('input', tip)
  }
}

export default {
  install(app: App) {
    app.directive('onlyNumber', onlyNumber)
  }
}


正如上面代码所示原理很简单通过element-plus的输入框拿到dom,监听input事件,拿到输入的值之后,通过正则进行替换。所有的事情都已经准备完全了,我以为万事俱备了,正当我使用这个指令进行二次封装number输入框的时候,问题就出现了,在vue3中不能通过$emit来触发双向数据绑定。下面是我封装的number组件。

<template>
  <el-input v-bind="$attrs" v-onlyNumber:[arg] v-model="val">
    <template v-for="(index, name) in slots" :key="index + 'g'" #[name]>
      <slot :name="name"></slot>
    </template>
  </el-input>
</template>

<script setup lang="ts" name="OnlyNumberInput">
import { ref, computed, useSlots, PropType } from 'vue'
export type Arg = {
  maxLength?: number
  reg?: RegExp
}
const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  },
  argOptions: {
    type: Object as PropType<Arg>,
    default: () => {
      // maxLength: 5,
      // reg: /[^\d\,]/g
    }
  }
})
const emit = defineEmits(['update:modelValue'])
const arg = stringifyJson(props.argOptions)
const slots = useSlots()
const val = computed({
  get() {
    return props.modelValue
  },
  set(v) {
    emit('update:modelValue', v)
  }
})

/**
 * json对象转json字符串
 * @param { Object } json json对象
 */
function stringifyJson(json: { [key: string]: any }) {
  return JSON.stringify(json, (k, v) => {
    // 将正则对象转换为正则字符串
    if (v instanceof RegExp) {
      return v.toString();
    }

    return v;
  });
}


</script>

讲了这么多废话,到底会出现什么问题呢?

当我们正常输入值的时候如果不是正则匹配的数据就会被替换为空,当输入最后一个值的时候并不会替换为空虽然输入内没有显示,但值已经是为非正则匹配的值了,这不是我们想要的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

如何解决这个问题呢

  • 正常的解决方法就是实现element-plus中输入框的数据双向绑定,我想到通过自定义指令中的vnode拿到这个实例的 e m i t 进行触发 i n p u t 事件,在 v u e 2 中这是可行的,在 v u e 3 中并不能通过 ‘ emit进行触发input事件,在vue2中这是可行的,在vue3中并不能通过` emit进行触发input事件,在vue2中这是可行的,在vue3中并不能通过emit(‘input’,this.value)来触发 可以通过***$emits(“update:modelValue”,this.value)`*** 来触发[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述
  • 如果不了解这个update:modelValue的朋友[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述
  • vue2vue3
    <someComponets :name.sync="name"/><someComponets v-model:name="name"/>
    emits(“update:name”,value)emits(“udpate:name”,value)
    默认为 :value=‘’ @input=“”默认为 :modelValue=“” @input=“”
    组件只能允许一个v-model可以允许多个v-model绑定
  • 暂时就只能想到这么多了。

总结

世上无难事,只要肯放弃就一定能解决问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Vue自定义指令实现仅支持输入数字的方法可以参考以下步骤: 1. 首先,在Vue的实例中定义一个自定义指令,例如`v-only-number`。 2. 在自定义指令的`bind`函数中,为输入框元素绑定一个事件监听器,用于处理输入事件。 3. 在事件处理函数中,使用正则表达式检查输入的值是否为数字。如果不是数字,则阻止输入。 4. 在输入框元素中使用自定义指令`v-only-number`,这样就可以限制用户只能输入数字了。 以下是一个示例代码,演示如何使用自定义指令实现仅支持输入数字的功能: ```javascript Vue.directive('only-number', { bind: function(el) { el.addEventListener('input', function(e) { var value = e.target.value; var newValue = value.replace(/[^0-9]/g, ''); // 使用正则表达式过滤非数字字符 e.target.value = newValue; }); } }); ``` 然后,在Vue组件中使用自定义指令`v-only-number`即可实现仅支持输入数字的效果,如下所示: ```html <template> <div> <input v-only-number type="text" v-model="inputValue"> </div> </template> <script> export default { data() { return { inputValue: '' }; } }; </script> ``` 这样,用户在输入框中只能输入数字,其他类型的字符将会被过滤掉。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue自定义指令限制input只能输入数字和小数点](https://blog.csdn.net/weixin_44816309/article/details/124856930)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Vue自定义数字输入指令](https://blog.csdn.net/weixin_44714325/article/details/108453737)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [vue自定义指令实现仅支持输入数字和浮点型的示例](https://download.csdn.net/download/weixin_38626943/12933110)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值