最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 模仿elementUI风格封装一个表单组件

    正文概述 掘金(Laman?)   2021-01-10   773

    本文目标

    • 学会使用elementUI的表单组件
    • 组件的传值方式
    • 封装input组件、带校验的form组件

    模仿elementUI风格封装一个表单组件

    先看看elementUI的表单组件是怎么用的

    1、 el-form:接收两个参数:model接收外部传的表单的数据,rules接收校验规则

    2、el-form-item“这个组件主要展示label和显示校验的错误信息

    3、el-input:双向绑定

    <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
      <el-form-item label="活动名称" prop="name">
        <el-input v-model="ruleForm.name"></el-input>
      </el-form-item>
    </el-form>
    

    封装input组件

    1、自定义组件要使用v-model,则必须要:value / @input

    2、触发input事件的时候,同时派发校验事件,告诉父组件去校验

    3、这里用到了v-bind="$attrs": 将调用组件时的组件标签上绑定的非props的特性(class和style除外)向下传递。在子组件中应当添加inheritAttrs: false(避免父作用域的不被认作props的特性绑定应用在子组件的根元素上)。注意: $attrs存储的是props之外的部分

    4、与attrs类似的还有:v-on="$listeners":将父组件标签上的自定义事件向下传递其子组件可以直接通过emit(eventName)的方式调用

    <template>
      <div class="m-input">
        <input :value="value" @input="onInput" v-bind="$attrs">
      </div>
    </template>
    
    <script>
        export default {
            inheritAttrs: false, // 避免顶层容器继承属性
            props: {
                value: {
                    type: String,
                    default: ''
                }
                // 因为用了$attrs 所以placeholder不需要这个props就不需要写了
            },
            methods: {
                onInput(e) {
                    // 通知父组件数值变化
                    this.$emit('input', e.target.value);
                    // 通知FormItem校验
                    this.$parent.$emit('validate');
                }
            },
        }
    </script>
    

    封装form-item组件

    • 展示label和校验错误的信息
    • 留插槽,里面放input
    • 对单个项进行校验
    <template>
      <div class="m-form-item">
        <i class="warn-icon" v-if="isNecessary&&label">*</i>
        <label v-if="label">
          <span>{{label}}</span>
        </label>
        <slot></slot>
        <p class="warning-msg">{{warningMsg}}</p>
      </div>
    </template>
    

    这里的校验需要用到一个校验库async-validator,具体用法见 github地址

    // 安装
    npm i async-validator
    // 用法
    import Schema from 'async-validator';
    const descriptor = {
      name(rule, value, callback, source, options) {
        const errors = [];
        if (!/^[a-z0-9]+$/.test(value)) {
          errors.push(new Error(
            util.format('%s must be lowercase alphanumeric characters', rule.field),
          ));
        }
        return errors;
      },
    };
    const validator = new Schema(descriptor);
    validator.validate({ name: 'Firstname' }, (errors, fields) => {
      if (errors) {
        return handleErrors(errors, fields);
      }
      // validation passed
    });
    

    js部分要做的是接受当前项的数据,以及当前项的校验规则,最后进行校验。但是form-item接收的参数只有两个:labelprop都是字符串,拿不到form表单的数据。 由于可以嵌套多层表单,因此不能只用父子传值,把数据传进来。这时候就涉及vue组件传值、通信的几种方式;

    组件传值、通信

    父=>子

    • 属性props
    • 引用refs:this.$refs.form.mobile='123'
    • children: this.$children[0].mobile='123'

    子=>父

    • 自定义事件:$emit('name',123)/@name

    兄弟通信

    通过同一个父亲组件搭桥 this.$parent.on('name',123)/this.$parent.emit('name')

    爷孙之间

    祖父辈通过provide提供数据

    provide() {
       return {formData: 123}
    }
    

    子孙辈通过inject注入数据 inject['formData']

    任意两个组件

    回到form-item获取到最外层父组件form的数据,因此需要form组件provide当前实例给孙子组件,那么form-item就可以注入整个form表单的实例

    import Schema from "async-validator"
    
    data() {
        return {
          isNecessary: false, // 是否必填
          warningMsg: '' // 错误的校验信息
        }
      },
      inject: ['from'], // form表单提供的数据
      props: {
        label: String,
        prop: String
      }
    

    数据以及规则都已经拿到,下面就是进行校验了!下面按照async-validator文档的用法写校验方法

    methods: {
        onValidate() {
          console.log(this.prop) //当前item的规则名称
          console.log(this.form) //整个form的实例:{model:外部接收的数据, rules:外部接收的规则}
          const descriptor = { // 这里拿到规则描述
            [this.prop]: this.from.rules[this.prop]
          }
          const validator = new Schema(descriptor);
          return validator.validate({ [this.prop]: this.from.model[this.prop] }, (errors) => {
            if (errors) {
              console.log(errors);
              this.warningMsg = errors[0].message
            } else {
            // 校验通过则清除错误消息
              this.warningMsg = ''
            }
          })
        }
      }
    

    最后监听校验事件,并且执行校验方法

     mounted() {
        if (JSON.stringify(this.from.rules) !== '{}') {
          this.isNecessary = true
        }
        this.$on('validate', () => {
          this.onValidate()
        })
      },
    

    封装form组件

    <template>
      <div>
        <slot></slot>
      </div>
    </template>
    

    js部分就是要处理两件事:

    • 接收外部数据和校验规则数组
    • 对规则进行全局校验
    provide() {
        return {
          'from': this // 直接传整个实例过去
        }
      },
    props: {
        model: {
          type: Object,
          required: true
        },
        rules: Object
      }
    

    下面是拿到全部form-item的校验方法return出来的结果;注意: async-validator return的校验结果是一个Promise。

    这里只需要拿出全部的校验任务,并且执行就可以

     methods: {
        validate(cb) {
          const tasks = this.$children
            .filter(item => item.prop)
            .map(item => item.onValidate());
          //   所有任务必须全部成功才算校验通过
          console.log(tasks); // tasks是一个promise的数组
    // Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值
          Promise.all(tasks)
            .then(() => cb(true))
            .catch(() => cb(false));
        }
      }
    

    这样一个基本的表单就完成,用法也与elementUI基本一致

    当提交表单的时候只需要通过this.$refs调用formvalidate()方法,就可以对整个表单校验

    模仿elementUI风格封装一个表单组件

    <template>
      <div class="home">
        <m-form :model="formData" :rules="rules" ref="form">
          <m-form-item label="手机号" prop="phone">
           <m-input type="text" v-model="formData.phone" placeholder="输入手机号" />
          </m-form-item>
          <m-form-item label="密码" prop="password">
            <m-input type="password" v-model="formData.password" />
          </m-form-item>
          <m-form-item>
            <div @click="submit">提交</div>
          </m-form-item>
        </m-form>
      </div>
    </template>
    
    <script>
    import mForm from '@/components/mForm/index.vue'
    import mFormItem from '@/components/mForm/Item.vue'
    import mInput from '@/components/mInput/index.vue'
    export default {
      name: 'Home',
      components: {
        mForm,
        mFormItem,
        mInput
      },
      data() {
        return {
          formData: {
            phone: '',
            password: ''
          },
          rules: {
            phone: [
              { required: true, message: '请输入手机号' }
            ],
            password: [
              { required: true, message: '请输入密码' }
            ]
          }
        }
      },
      methods: {
        submit() {
          this.$refs.form.validate((valid) => {
            console.log(valid);
            if (valid) {
              alert('submit!');
            } else {
              alert('error!');
              return false;
            }
          });
        }
      },
    }
    </script>
    
    

    起源地下载网 » 模仿elementUI风格封装一个表单组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元