Vue.js-ElementUI表单

1 表单el-form

每个表单 el-form 由多个表单域 el-form-item 组成。默认情况,表单域是垂直分布的,可以设置 el-form inline=‘true’ 属性,使表单水平分布,当多个表单域el-form在一行显示不完时,会自动换行。

1.1 表单的模型对象model

el-form使用model属性来描述表单的数据模型对象,在指定数据对象后,其下的form-item能够使用该数据对象,比如引用数据对象的值,或数据验证等。

<el-form :model="dto">
  <el-form-item label="名字">
    <el-input v-model="dto.name"></el-input>
  </el-form-item>
</el-form>
<script>
    export default{
    data(){
      return {
        dto:{name:"张三"},
</script>

上面的代码:model="dto"指定了表单的模型对象,在el-form-item表单域中对模型对象的值做了双向绑定,需要注意的是表单域中v-model='dto.name'即使没有模型对象,也是能够正常运行的。但是,表单的模型对象在表单调用其验证方法validate(见验证规则一节)时是必须的。

1.2 表单域的标签

每个表单域都有一个标签(不是必选),用el-form-item的label属性指定,表单域的标签有位置和宽度两个属性。

  • label-position 表示标签的对齐方式,它有right(默认)|left|top三个取值,该属性是加在el-form元素上的,即只能对其下所有表单域进行统一的设置。
  • label-width 表示标签的宽度,只有当label-position为right或left时有效,label-width的值需要带单位,比如label-width='120px'。label-width可以加载el-form元素上,表示对其下的所有表单域的标签进行统一的设置,也可以加载el-form-item元素上,表示对单个表单域标签设置,el-form-item的优先级高于el-form的优先级。
  • label-suffix 标签的后缀,该属性在el-form标签中设置。也就是说,如果设置了该项,其下的所有设置了标签(label)的表单域,都会添加后缀。
  • <!-- 在表单元素上统一设置了表单域标签元素的位置和宽度 -->
    <el-form label-position='left' label-width='100px' :model="dto">
        <el-form-item label="名称">
        <el-input v-model="dto.name"></el-input>
      </el-form-item>
      <!-- 单独设置该表单域的标签宽度,优先级高于el-form上设置的宽度 -->
      <el-form-item label="组名称" label-width='120px'>
        <el-input v-model="dto.name"></el-input>
      </el-form-item>
    </el-form>
    

    表单域的label属性是字符串类型的,如果要用其他类型(如图片)或复杂的标签样式,显然label是不能满足的,element为我们提供标签具名插槽,具名插槽的名称为label。

    <el-form-item>
      <!--使用具名插槽设置label-->
      <template v-slot:lable>
        <img :src="logo" alt="" style="width:16px">
      </template>
      <el-input v-model="dto.name"></el-input>
    </el-form-item>
    

    具名插槽的优先级高于label属性,同时,使用具名插槽后label-suffix属性设置的标签后缀也会失效。

    1.3 表单域的布局

    element对其定义的表单元素做了默认的样式,比如:e-input默认是width:100%,element会在input元素的外面包裹一层div,其渲染后的样子如下:

    <div class="el-input">
      <input type="text" class="el-input__inner"/>
        el-input样式中width:100%
        el-input__inner样式中width:100%
    

    如上面的分析,如果要修改input的宽度,我们有以下几种方法:

  • 为表单元素el-input添加一个类,设置其width
  • 复写生成的div标签的el-input样式,设置其width
  • 表单元素el-input外面设置一个外壳,为这个外壳设置width
  • 我们为el-input组件添加的任何自定义样式,最后都会加在外层的div标签上,而webpack渲染出的最终页面,是先加载我们自定义的样式,后加载element的默认样式。所以,如果我们在自定义的样式中设置了宽度,会被默认样式覆盖掉,也就是说,我们在自定义样式中设置width值是不起作用的,如下:

    <template>
        <div id="app">
        <el-input class="width-200" />
    </template>
    <style>
      .width-200{
        width:200px;
    </style>
    

    渲染后的结构是

    <style type="text/css"> /*不会生效*/ .width-200 { width: 200px; </style> <style type="text/css"> /*element中的样式*/ </style> </head> <div class="width-200 el-input"> <input type="text" class="el-input__inner"/> </body>

    width-200(width:200px)和el-input(width:100%)中都有width属性,因为<div class="width-200 el-input">中el-input在width-200的后面,所以,width-200中的宽度被覆盖。

    我们可以利用css选择器优先级高低排列来处理这个问题,简单的说,我们将自定义的样式的权重设高,来解决无效的问题。选择器的权重用4个部分表示(0,0,0,0),分别表示(行内样式,ID选择器,类/属性/伪类,元素/伪元素),他们的优先级从右到左依次升高,同一个级别的的选择器有几个,该部分的数字就是几,比如:

    /* demo1 权重(0,0,2,1)*/
    .class-a .class-b div{
      color:red;
    /* demo2 权重(0,0,1,3) */
    .class-a div ul li{
      color:blue;
    

    上面的例子中,demo1的优先级高于demo2,因为在类这个级别demo1高于demo2,所以不管在元素级别demo2的值怎么高,优先级也是比不过demo1。注意:

  • 权重表示方法没有进位的说法
  • !important是最高级别,它高于所有的权重

    对于el-input我们可以在外面加一层id选择器,如#app .width-200{width:200px},这样width-200的优先级就高于el-input的优先级。

    第2个解决方案中,在样式引入时(html的head标签中),复写的el-input样式会在element的el-input的样式之前,所以,element的el-input的样式会层叠掉自定义的el-input的样式。可以在自定义的el-input样式的宽度上使用!important,强制使用自定义样式的宽度,不推荐这种方式。

    第3中方案比较好理解,在element中,可以使用栅格布局的方式来处理表单元素的位置。

    2 表单验证

    表单验证的对象是el-form中model属性指定的数据对象。

    21 验证规则

    element的验证规则使用async-validator,规则的部分属性如下:

    待验证对象的数据类型,取值范围:string(默认)|number|boolean|method|regexp|integer|float|array|object|enum|date|url|hex|email required 是否为必须 pattern 正则表达式 最小值,string和array类型对比其length属性,number类型对比起本身的值 最大值,参照值与min相似 长度,参照值与min相似,但是他是进准的表达,比如验证一个array对象的len是3,那么,这个array的长度就必须为3才能满足条件,如果是number类型,表示这个值必须与len中的值一样 当type:enum时,enum选项才有意义,它后接数组集合,验证数据是否在此集合中 fields 深度验证,当验证的type为object,可以验证其内部数据 message 错误消息,类型为字符串或函数 validator 自定验证函数,函数的参数为(rule,value,callback)

    在element中,将规则封装到一个规则对象中,规则对象的键(规则名)要与模型对象的键一致。键的值就是具体的规则,它可以是一个对象包含规则的属性和值,也可以是一个数组对象包含多条规则。

    规则对象与模型对象一样,由表单元素el-form引用,使用rules属性指定(模型对象由model属性指定)。有了对规则对象的引用,还需要在具体的表单域中指定使用那一条规则,在el-form-item表单域中使用prop指定规则名。

    <template>
      <div id="app">
          使用model指定模型对象,使用rules指定规则对象
          模型对象就是规则要验证的数据对象
        <el-form ref="validateForm" :model="dto" :rules="rules" label-width="80px">
          <!-- 在表单域中使用prop属性指定规则对象中的规则名 -->
          <el-form-item label="名字" prop="name">
            <el-input v-model="dto.name"></el-input>
          </el-form-item>
          <el-form-item label="组名字" prop="groupName">
            <el-input v-model="dto.groupName" />
          </el-form-item>
        </el-form>
    </template>
    <script>
    export default {
      data() {
        return {
          dto: { name: "张三", groupName: "管理组" },
          //规则对象定义,键为对象名,建议与数据对象的键相同,
          //值为规则,可以是单条规则,也可以是集合
          rules: {
            // trigger是element中自定义的属性,见触发验证一节
            name: { required: true, message: "名称为必填项", trigger: "blur" },
            groupName: [
              //gorupName有多条规则,不同的规则验证不同的方面
              { required: true, message: "组名为必填", trigger: "blur" },
              { min: 3, max: 16, message: "组名长度在3-16之间", trigger: "blur" },
              { pattern: /^\w+$/, message: "不能包含特殊字符", trigger: "blur" }
    </script>
    

    2.2 触发验证

    element在验证规则中加入了自动触发条件trggier属性,目前它的取值为blurchange(默认)两项,表示当表单元素失去焦点,或内容改变时触发该项验证。注意,它们也可以组合使用,如:trigger:['blur','change']

    我们也可以通过el-form的实例方法validate手动触发整个表单的验证,或者使用实例方法validateField指定部分字段做验证。这里提到了实例方法,也就是说我们要得到el-form的实例,一般使用vue的ref属性定义这个对象的引用名。

    validateForm() {
      //通过refs获取表单的引用
      let { form } = this.$refs;
      //调用表单的实例方法validate做全部验证
      //该方法的参数为一个回调函数
      //回调函数有两个可选参数,一个表示是否验证通过,第二个是未通过的字段集合
      form.validate((result,errorFields)=>{
        console.log(result);
        console.log(errObject);
    validateFields(){
      //通过refs获取表单的引用
      let { form } = this.$refs;
      //调用表单的实例方法validateField做指定字段的验证
      //该方法的有两个参数,第一个为要验证的字段,如果是当用string,多个用array类型
      //第二个参数为回调函数,该函数只有一个string类型的参数,表示错误信息
      form.validateField(['name','groupName'],error=>{
        console.log(error);
    

    注意:使用validateField方法验证多个字段时,每验证一次,都会调用回调函数一次,通过验证的字段会error=='',没通过验证的字段,返回对应规则的message错误消息字段。

    有时候需要对字段做动态的验证,可以为表单域的prop动态绑定规则。比如:有一个select选择器表示字段的输入类型,有文本、单选两种。当为文本时,可以填写文本的默认值(非必须),当为单选时,要填写单选的取值范围(必须)。

    字段类型: 文本:填写默认值,非必须 单选:填写选项,多个选项使用','分隔

    代码实现:

    <template>
      <div id="app">
        <el-form ref="form" :model="dto" :rules="rules" label-width="80px">
          <el-form-item label="输入类型">
            <el-select v-model="dto.type">
              <el-option
                v-for="(item,index) in inputType"
                :key="'inputType'+index"
                :value="item.value"
                :label="item.label"
              ></el-option>
            </el-select>
          </el-form-item>
            prop的值改变时,要重新渲染el-form-item元素,
            但是vue出于性能的考虑,会复用之前的el-form-item元素,
            添加key属性可以强制使用新的el-form-item元素
          <el-form-item :key="'key'+inputValueRule" label="字段值" :prop="inputValueRule">
            <el-input v-model="dto.inputValue"></el-input>
          </el-form-item>
        </el-form>
    </template>
    <script>
    const inputType = [{ value: 0, label: "文本" }, { value: 1, label: "单选" }];
    const rules = {
      inputValue: [
        { required: true, message: "字段值为必填", trigger: "blur" },
          //使用正则表达式做逗号验证
          pattern: /^([\u0391-\uFFE5\d\w,])*([\u0391-\uFFE5\d\w]+)$/g,
          message: "选项需要用','号隔开",
          trigger: "blur"
    export default {
      data() {
        return {
          inputType,
          rules,
          dto: {
            type: 0,
            inputValue: ""
      computed:{
        inputValueRule(){
          //当输入类型类单选时,返回规则指定的规则名称
          return this.dto.type==1?"inputValue":"";