Web前端之路Vue

Vue-component | 自定义Form组件

2020-03-16  本文已影响0人  鱼太咸丶

日常开发中积累了不少可能对一些开发有帮助的简单易用的组件,好记性不如烂笔头,想对过去的一些零零乱乱的东西做一些总结,反省自己的同时也便于思考一些更优的方法,提升下开发思维😉😉😉。
代码传送门(😃感觉有作用的的同学帮忙点下❤️️)

效果截图

先上效果图,自定义规则,违反规则马上显示提示。


Form

组件结构

参照elementUI的表单组件,组件的结构是Form > ... (FormItem > Form组件)

<j-form>    // Form
    <j-form-item>     //  FormItem
             <j-input></j-input>    // content
    </j-form-item>
    //  ...可以添加多个Item
</j-form>

核心代码

Form组件
最上级是Form组件,是进行全局验证的组件,并且接收prop(传递的数据模型和规则),并将自身注入到子级中。

<template>
  <div id="form">
    <slot></slot>
  </div>
</template>
<script>
export default {
  name: 'j-form',
  //  将自身注入
  provide () {
    return {
      form: this
    }
  },
  props: {
  //  数据模型
    model: {
      type: Object,
      required: true
    },
  //  校验规则
    rules: {
      type: Object
    }
  },
  methods: {
    //  表单全局校验方法,校验所有的item内容
    validate (callBack) {
      const tasks = this.$children.filter(item => item.prop).map(item => item.validate())
      // 所有任务都通过才算校验通过
      Promise.all(tasks).then(() => callBack(true)
      ).catch(() => callBack(false)
      )
    }
  }
}
</script>


FormItem组件
用item组件包裹表达内容组件,目的是为了进行单一校验,并产生错误的提示信息。这里使用的是async-validator第三方的校验库,需要额外装载。


<template>
  <div id="form-item">
    <label v-if="label" class="label">{{label}}</label>
    <slot></slot>
    <p v-if="errorMsg" class="error-msg">{{errorMsg}}</p>
  </div>
</template>
<script>
import Schema from 'async-validator'
export default {
  name: 'j-form-item',
  components: {},
  inject: ['form'],
  data () {
    return {
      errorMsg: ''
    }
  },
  props: {
    //  标题文本
    label: {
      type: String,
      default: '',
      required: true
    },
  //  数据字段名
    prop: {
      type: String
    }
  },
  mounted () {
    // 监听校验
    this.$on('validate', () => { this.validate() })
  },
  methods: {
    validate () {
      console.log('触发校验===')
      // async-validator的校验方法
      const value = this.form.model[this.prop]
      const rules = this.form.rules[this.prop]
      const desc = { [this.prop]: rules }
      const schema = new Schema(desc)
      // return的是校验结果的Promise
      return schema.validate({ [this.prop]: value }, errors => {
        if (errors) {
          console.log('error', errors[0].message)
          this.errorMsg = errors[0].message
        } else {
          console.log('error', errors)
          this.errorMsg = ''
        }
      })
    }
  }
}
</script>

Input组件
内容组件,在这里进行内容的监听,上到FormItem进行校验

<template>
  <div id="input">
    <input :value="value" @input="onInput" v-bind="$attrs" />
  </div>
</template>
<script>
export default {
  name: 'j-input',
  // 禁用继承的属性
  inheritAttrs: false,
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  methods: {
    onInput (e) {
      console.log(e.target.value)
      // 监听数值变化
      this.$emit('change', e.target.value)
      // 触发校验
      this.$parent.$emit('validate')
    }
  }
}
</script>

使用
使用组件就和elementUI一样进行处理即可

<template>
  <div id="home">
    <j-form ref="ruleForm" :model="model" :rules="rules">
      <j-form-item label="姓名" prop="name">
        <j-input v-model="model.name" placeholder="请输入姓名" />
      </j-form-item>
      <j-form-item label="年龄" prop="age">
        <j-input v-model="model.age" placeholder="请输入年龄" />
      </j-form-item>
    </j-form>
  </div>
</template>
<script>
export default {
  components: {},
  data () {
    return {
      model: {
        name: '',
        age: ''
      },
      rules: {
        name: [
          { required: true, message: '请填写姓名', trigger: 'change' },
          { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'change' }
        ],
        age: [
          { required: true, message: '请填写年龄', trigger: 'change' },
          { min: 1, max: 2, message: '长度在 1 到 2 个字符', trigger: 'change' }
        ]
      }
    }
  }

关键点


处理自定义组件,一定要对vue中的传值比较清晰了解,这里就不一一列举。在这里主要使用的一些技术包括:

技术 概述 备注
props传值 父级传子级 /
provide-inject 从上往下可以跨级传值 在代码里将自身传递下来
slot 插槽,将子组件插入父组件中 Input>FormIten>Form
inheritAttrs-$attrs 禁用继承的属性直接赋予给根标签,自定义继承 /
v-model 自定义双向传值 /
Promise.all() 同时完成所有的任务 /

后续会持续更新其他一些有用的组件提供参考...

上一篇 下一篇

猜你喜欢

热点阅读