封装一个能够过滤空格的input组件

3,375 阅读2分钟

今天过系统的时候发现,表单提交的时候写的必填校验,用户只需要敲一个空格就通过了。这就导致在统计数据的时候,出现了异常情况。除此之外还有可能用户在拷贝内容到输入框时,会带上很多无用的空格,那么如何自动来处理空格这种情况呢?

首先说一下我们系统使用的是ant-design-vue1.x 的版本,表单使用a-form

解决方案

我大概想到了以下三种解决方案:

  1. 使用a-form提供的自定义校验规则,当用户输入的内容首尾有空格的时候,校验不通过。
  2. 监听a-input输入框的change事件,去掉首尾的空格然后重新赋值。
  3. 封装一个首尾不能输入空格的组件

最后我们采用的是第三种方案,因为在已经快完成的系统里无论是使用第一种还是第二种,工作量都是巨大的。

封装一个和a-input具有相同api的组件,再进行标签的批量替换,这是相对容易得多的方案了。

三种方案具体实现

  1. 自定义校验规则

    使用正则验证首尾是否有空格

      validateBlank(rule, value, callback){
          if(/(^\s+)|(\s+$)/.test(value)){
              callback("输入内容首尾不能存在空格!");
          }else{
              callback();
          }
      }
    
  2. 去掉首尾的空格然后重新赋值

    template 代码

    <a-form-item label="测试">
       <a-input
           @change="handleInputChange"
            v-decorator="['test', { rules: [{ required: true, message: '请输入内容' }] }]"
            >
        </a-input>
    </a-form-item>
    

    handleInputChange方法

    handleInputChange(e) {
         let value = e.target.value.trim();
         this.$nextTick(() => {
             this.form.setFieldsValue({
                 test: value,
             });
         });
    },
    

    需要注意的是这里重新设置值的代码必须要在$nextTick里,保证在原值修改之后再设置新值。

  3. 封装一个首尾不能输入空格的组件x-input

    • 属性传递

      使用v-bind="$attrs"加上inheritAttrs:falsex-input上的参数全部传递到a-input

    • 事件传递

      因为组件内部重新处理change事件,所以传递的时候要过滤掉change,不然我们自己的change事件不会生效。

      <a-input
          v-on="_listeners"
        >
        </a-input>
      
      computed: {
            _listeners() {
              let $listeners = this.$listeners;
              return Object.keys($listeners)
                .filter(key => key !== "change")
                .reduce((res, key) => {
                  res[key] = $listeners[key];
                  return res;
                }, {});
            },
          },
      
    • 插槽传递

      <slot v-for="(_,name) in $slots" :name="name" :slot="name"></slot>

      原理是在x-input标签中写了的插槽全部在组件中声明一遍,再传递到a-input中,由a-input负责处理。

    • change事件处理

      <a-input @change="handleFilterValue">
       </a-input>
      
       props: {
            filterHandler: PropTypes.func.def(val => val.trim()),
       },
      
       methods: {
           handleFilterValue(e) {
               let value = this.filterHandler(e.target.value);
               this.$emit("change", value);
               this.$forceUpdate();
           },
       },
      

      定义一个默认是trim的过滤函数filterHandler,如果不需要trim的话,则使用传递一个处理函数:filterHandler=val=>val即可。

      值得一提的是:这里使用到了$forceUpdate来强制刷新,因为一直输入空格的话,值被trim处理之后,组件的value属性一直不变,但是a-input显示的值却一直在增加空格。有兴趣的同学可以测试一下。

    • v-model支持

       model: {
           prop: "value",
           event: "change",
       },
      

      完整代码如下:

      <template>
        <div>
          <a-input v-bind="$attrs"
                   v-on="_listeners"
      			 @change="handleFilterValue">
            <slot v-for="(_,name) in $slots" :name="name" :slot="name"></slot>
          </a-input>
        </div>
      </template>
      
      <script>
        import PropTypes from "ant-design-vue/lib/_util/vue-types";
      
        export default {
          name: "XInput",
          inheritAttrs: false,
          model: {
            prop: "value",
            event: "change",
          },
          computed: {
            _listeners() {
              let $listeners = this.$listeners;
              return Object.keys($listeners)
                .filter(key => key !== "change")
                .reduce((res, key) => {
                  res[key] = $listeners[key];
                  return res;
                }, {});
            },
          },
          props: {
            filterHandler: PropTypes.func.def(val => val.trim()),
          },
          methods: {
            handleFilterValue(e) {
              let value = this.filterHandler(e.target.value);
              this.$emit("change", value);
              this.$forceUpdate();
            },
          },
        };
      </script>
      

      本以为组件封装到这就结束了,但是放到a-form里去使用才发现还存在问题。

      如果只是单纯的使用v-model绑定值,是没有问题的。

      但是在a-form里,表单是使用v-decorator绑定值,使用过的小伙伴应该明白,表单里的值都是受form进行控制的。如果组件里不显式声明一个value属性,表单里的值完全不受控。完整代码如下:

      <template>
        <a-input
          :value="value"
          v-bind="$attrs"
          v-on="_listeners"
          @change="handleFilterValue"
        >
          <slot v-for="(_,name) in $slots" :name="name" :slot="name"></slot>
        </a-input>
      </template>
      
      <script>
        import PropTypes from "ant-design-vue/es/_util/vue-types";
      
        export default {
          name: "XInput",
          inheritAttrs: false,
          props: {
            //默认是会将值trim掉的,如果不需要则val=>val即可
            filterHandler: PropTypes.func.def(val => val.trim()),
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
          },
          model: {
            prop: "value",
            event: "change",
          },
          computed: {
            _listeners() {
              let $listeners = this.$listeners;
              return Object.keys($listeners)
                .filter(key => key !== "change")
                .reduce((res, key) => {
                  res[key] = $listeners[key];
                  return res;
                }, {});
            },
          },
          methods: {
            handleFilterValue(e) {
              // 如果传入了过滤函数,则使用过滤函数,否则使用trim进行过滤
              let emitValue = this.filterHandler(e.target.value);
              this.$emit("change", emitValue);
              this.$forceUpdate();
            },
          },
        };
      </script>
      

总结

虽然整个组件封装的代码确实不多,但是里面涉及到属性传递、事件传递时过滤掉change、插槽传递、以及a-form表单组件封装的注意事项。传入不同的filterHandler,可以实现特殊字符过滤、大小写转换等功能。

能力有限,不足之处,望多多指正。