Vue 项目中使用 Element UI 之el-form 组件实践

1,356 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

🏀 安装

首先用vue-cli新建个项目.

vue create use_elementui

官网提供了npmCDN两种在项目中引入Element的方式,我们使用第一种,npm安装:

npm install element-ui --save

关于使用组件,可以引入整个Element,也可以按需引入,表单中涉及的组件还是比较多的,方便起见,我们就使用完整引入,需要注意的就是样式文件需要单独引入

// main.js
import Vue from "vue";
import App from "./App.vue";
import ElementUI from "element-ui"; 
import "element-ui/lib/theme-chalk/index.css"; // 单独引入样式

Vue.config.productionTip = false;
Vue.use(ElementUI);

new Vue({
  render: (h) => h(App),
}).$mount("#app");

⚽ 使用Form组件

components文件夹下新建Form.vue文件,专门用于放置el-form相关的代码,在HelloWorld.vue中使用Form.vue。 我们从使用官网提供的表单验证组件开始,完整代码由于比较长就附在了文章末尾,最终表单组件的显示如下:

image.png

Element UI中的一个<el-form>元素就是一个表单,它可以包含多个控件,比如输入框、选择器、单选框、多选框等,每个控件也称为表单项,即<el-form-item>

表单表单项都有一系列的属性和方法,详细可以对照官网查看,这里就不一一演示了,我打算从功能出发来梳理表单组件中的核心逻辑,在这个过程中用到哪些属性/方法,就展开介绍一下,其它属性/方法可以对照官网自己练习用法。

⚽ 收集数据

拿上面的案例来说,Form组件通过表单项收集的数据都通过v-model存放在ruleForm对象中,以<el-input>元素为例,下面代码只保留我们关注的部分:

<template>
<!-- 只保留关注的代码 -->
    <div>
      <el-form :model="ruleForm">
          <el-form-item>
            <el-input v-model="ruleForm.name"></el-input>
          </el-form>
    <div>
<template>
<script>
    data(){
      return {
          ruleForm: {
            name: "",
          },
      }
    }
</script>

name的值为<el-input v-model="ruleForm.name"></el-input>输入框的默认值,这里设的是空值,name: "",如果我们将它设为name: "This is name",可以看到此时对应的活动名称输入框会显示出相应的默认值,对其它表单项的内容来说,也是同样的道理。

image.png

需要注意的一点是v-model是赋在表单控件上的,比如上面的<el-input>,而不是表单项上,表单中使用的是model属性,这点不要混肴了。事实上,如果不涉及校验,表单中的model属性在收集数据时是可以不要的。

⚽ 校验数据

Form组件的数据校验涉及的属性为rules,相对复杂一些。

从校验方式来看,我们可以选择默认的校验方式,也可以使用validator函数来自定义校验方法,两者遵循的都是async-validator这一校验规则。

从校验对象来看,表单表单项的内容都可以被校验,<el-form>上的rules优先级小于<el-item-form>上的rules规则验证,也就是说,当两者同时存在时,校验规则遵循的是<el-item-form>上的rules

下面我们来实践一下:

<template>
<!-- 只保留关注的代码 -->
    <div>
        <el-form ref="ruleForm" :model="ruleForm" :rules="rules"> 
            <el-form-item label="活动名称" prop="name">  // prop的值必须与rules中的字段对应
                <el-input v-model="form.name"></el-input> 
            </el-form-item>
        </el-form>
    <div>
<template>
<script>
data(){
  return {
   rules: {
    name: [
      {
        required: true,    // 该字段是否必填
        message: "请输入活动名称", // 没填写时的错误提示语
        trigger: "blur", // 在什么时候触发验证
      },
      {
        min: 3, // 字段最小长度
        max: 5, // 字段最大长度
        message: "长度在 3 到 5 个字符", // 不满足规则时的错误提示语
        trigger: "blur", // 在什么时候触发验证
      }
    ]
   }
  }
}
</script>

可以看到:rule作用在el-form上,并且绑定了一个变量rules。接着我们就在rules中编写活动名称name的验证规则。prop属性的值就是form中定义的name字段,只有这样rules中定义的对name的校验规则才能和对应的表单关联起来,否则验证规则是不会生效的。

image.png

这里需要注意的是,表单el-form上绑定的form数据中的字段、prop设置的值以及rules绑定的rules数据中的字段,这三个必须都要一致,否则校验会出现问题。

为了演示自定义校验规则的使用,这里增加了一个表单项:

<el-form-item label="年龄" prop="age" required>
    <el-input v-model.number="ruleForm.age"></el-input>
</el-form-item>

校验规则如下:

  data() {
    const checkAge = (rule, value, callback) => {
      if (!value) {
        return callback(new Error("年龄不能为空"));
      }
      setTimeout(() => {
        if (!Number.isInteger(value)) {
          callback(new Error("请输入数字值"));
        } else {
          if (value < 18) {
            callback(new Error("必须年满18岁"));
          } else {
            callback();
          }
        }
      }, 1000);
    };
    return {
        age: [{ validator: checkAge, trigger: "blur" }],
    }
  }

image.png

上面例子中展示了输入的年龄必须大于18岁的验证规则,添加了一个checkAge校验方法,它的通用语法规则是:

validate( source , [options], callback )
  • source 是需要校验的属性和值,必传参数。
  • options 是描述处理验证对象的选项。
  • callback 校验完成之后的回调函数。

该方法返回的是 Promise 对象,所以有:

  • then() 成功回调
  • catch(({ errors, fields })=>{}) 失败回调

进一步的了解可以看一下async-validator这个表单异步校验库的官方文档 。这里我们需要注意的是检验方法必须写在data()return的外层,否则校验无法生效。

⚽ 表单提交与重置

  methods: {
    // 提交表单
    submitForm(formName) {
      this.$refs[formName].validate((valid, failedInfo) => {
        if (valid) {
          alert("submit!");
        } else {
          console.log("error submit caused by:");
          console.log(failedInfo); 
          return false;
        }
      });
    },
    // 重置表单
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },

表单方法validate是对整个表单进行校验的方法,参数为一个回调函数。 该回调函数会在校验结束后被调用,并传入两个参数:是否校验成功未通过校验的字段。若不传入回调函数,则会返回一个promise

上面validate方法的逻辑是:点击提交按钮时,如果表单校验通过,就弹出提示框;如果校验未通过,就打印出错误信息,可以看到错误信息会告诉我们是哪些表单项未通过校验:

image.png

resetFields方法则是对整个表单进行重置,将所有字段值重置为初始值并且移除校验结果,这里需要注意的是在使用resetFileds时,必须给表单项el-form-item设置prop值,否则无法进行重置。

🏀 完整代码

// HelloWorld.vue
<template>
  <div class="hello">
    <Form />
  </div>
</template>

<script>
import Form from "./Form.vue";
export default {
  name: "HelloWorld",
  components: {
    Form,
  },
};
</script>

// Form.vue
<template>
  <div>
    <h1>El-Form</h1>
    <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-item label="活动区域" prop="region">
        <el-select v-model="ruleForm.region" placeholder="请选择活动区域">
          <el-option label="区域一" value="shanghai"></el-option>
          <el-option label="区域二" value="beijing"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="活动时间" required>
        <el-col :span="11">
          <el-form-item prop="date1">
            <el-date-picker
              type="date"
              placeholder="选择日期"
              v-model="ruleForm.date1"
              style="width: 100%"
            ></el-date-picker>
          </el-form-item>
        </el-col>
        <el-col class="line" :span="2">-</el-col>
        <el-col :span="11">
          <el-form-item prop="date2">
            <el-time-picker
              placeholder="选择时间"
              v-model="ruleForm.date2"
              style="width: 100%"
            ></el-time-picker>
          </el-form-item>
        </el-col>
      </el-form-item>
      <el-form-item label="即时配送" prop="delivery">
        <el-switch v-model="ruleForm.delivery"></el-switch>
      </el-form-item>
      <el-form-item label="活动性质" prop="type">
        <el-checkbox-group v-model="ruleForm.type">
          <el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox>
          <el-checkbox label="地推活动" name="type"></el-checkbox>
          <el-checkbox label="线下主题活动" name="type"></el-checkbox>
          <el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>
        </el-checkbox-group>
      </el-form-item>
      <el-form-item label="特殊资源" prop="resource">
        <el-radio-group v-model="ruleForm.resource">
          <el-radio label="线上品牌商赞助"></el-radio>
          <el-radio label="线下场地免费"></el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="活动形式" prop="desc">
        <el-input type="textarea" v-model="ruleForm.desc"></el-input>
      </el-form-item>
      <!-- 自定义校验 -->
      <el-form-item label="年龄" prop="age" required>
        <el-input v-model.number="ruleForm.age"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')"
          >立即创建</el-button
        >
        <el-button @click="resetForm('ruleForm')">重置</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    // 自定义校验规则
    const checkAge = (rule, value, callback) => {
      if (!value) {
        return callback(new Error("年龄不能为空"));
      }
      setTimeout(() => {
        if (!Number.isInteger(value)) {
          callback(new Error("请输入数字值"));
        } else {
          if (value < 18) {
            callback(new Error("必须年满18岁"));
          } else {
            callback();
          }
        }
      }, 1000);
    };
    return {
      // 通过表单收集到的数据
      ruleForm: {
        name: "", // 活动名称 <el-input>
        region: "", // 活动区域 <el-select>
        date1: "", // 活动时间-日期 <el-time-picker>
        date2: "", // 活动时间-时间 <el-time-picker>
        delivery: false, // 即时配送 <el-switch>
        type: [], // 活动性质 <el-checkbox-group>
        resource: "", // 特殊资源 <el-radio-group>
        desc: "", // 活动形式 <el-input>
        age: "", // 年龄 <el-input>
      },
      // 表单检验规则:async-validator
      rules: {
        name: [
          {
            required: true,
            message: "请输入活动名称",
            trigger: "blur",
          },
          {
            min: 3,
            max: 5,
            message: "长度在 3 到 5 个字符",
            trigger: "blur",
          },
        ],
        region: [
          {
            required: true,
            message: "请选择活动区域",
            trigger: "change",
          },
        ],
        date1: [
          {
            type: "date",
            required: true,
            message: "请选择日期",
            trigger: "change",
          },
        ],
        date2: [
          {
            type: "date",
            required: true,
            message: "请选择时间",
            trigger: "change",
          },
        ],
        type: [
          {
            type: "array",
            required: true,
            message: "请至少选择一个活动性质",
            trigger: "change",
          },
        ],
        resource: [
          {
            required: true,
            message: "请选择活动资源",
            trigger: "change",
          },
        ],
        desc: [
          {
            required: true,
            message: "请填写活动形式",
            trigger: "blur",
          },
        ],
        age: [{ validator: checkAge, trigger: "blur" }],
      },
    };
  },
  methods: {
    // 提交表单,<el-button>
    submitForm(formName) {
      this.$refs[formName].validate((valid, failedInfo) => {
        if (valid) {
          alert("submit!");
        } else {
          console.log("error submit caused by:");
          console.log(failedInfo);
          return false;
        }
      });
    },
    // 重置表单,<el-button>
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
  },
};
</script>