0


Element UI各种使用问题汇总(Input、Form篇)

Input回车导致页面刷新的问题

Element UI为了遵守W3C规范特意设置的,就是当Form中只有一个Input的时候,Form把这个事件当成了是提交表单的操作,所以页面会刷新。

解决方法:

凡是<el-form>里面只有一个Input,就在<el-form>上附加一个事件:

@submit.native.prevent

,这样可以阻止默认提交,可以解决这个问题。

Input的各种输入约束办法

可以使用这几种约束方法:

  1. 正则替换,比较粗暴,慎用

一些场景下,比如编辑人员录入一篇文章,早就规定了标题不允许使用叹号,编辑也明白这个规定,他只是从别的地方复制标题,恰好标题带叹号,那么,正则替换可以帮助编辑直接把叹号去掉。比如:

@input="form.title = form.title.replace(/[!!]/g,'')"

这就要比提示“标题禁止出现叹号”要好。

同样的,约束一个输入框只允许输入数字和小数点,也可以用这种方法。

记住:

不要用这种方法去执行超出常识的约束和有负面作用的约束,因为不见得使用者会注意到程序的自动替换,或许会造成使用者困扰甚至造成事故。

  1. 长度约束,阻止输入,也比较粗暴,慎用
maxlength

minlength

这2个属性可以用来约束字符长度,可用于

type="text"

type="textarea"

,但是对于别的type它一般无能为力,比如改成

type="number"

就不起作用。

注意,

minlength

在Element UI中其实是无效的,至少经过我测试,

minlength
  • rules设为必填 +
    this.$refs["form"].validate()
    
    是无效的,无法报错。
minlength

其实在原生Form中才会有效。

长度控制可以设置比如某个网络游戏昵称(最多6位)……等。

事实上,我

maxlength

并不建议用于控制手机号输入,尽管国内手机号一定是11位,但是用rules控制会更好一些,因为总有人无意输入了12个数字,你粗暴的阻止最后一位的输入也可能会造成困扰。

真的想控制长度,最佳实践是使用rules,因为控制数值长度是不常见的约束,无论是用正则替换还是用阻止输入,都可能让使用者困扰或者不留意,最终形成事故。

记住:

同样的,不要用这种方法去执行超出常识的约束和有负面作用的约束,因为不见得使用者会注意到输入被截断,比如你设置

maxlength="10"

,一个使用者飞速地敲了11位字符,他根本没注意最后一个字符是没有成功输入的,也会造成使用者困扰甚至造成事故。

max

min

一般跟

step

联合使用,但是它不具有约束性,只要你愿意,你可以敲出高于max的数值,所以**尽量别用

max

min

**。

  1. 最妥帖的办法,使用rules

官方文档:https://element.eleme.cn/#/zh-CN/component/form

基于async-validator:https://github.com/yiminghe/async-validator

比如,手机号必须11位,写法是:

      formRules: {
        ownerPhone: [
          { required: true, message: "手机号不能为空", trigger: "blur" },
          { len: 11, message: "手机号应为11位", trigger: "blur" }
        ],
      }

一个输入行,有多个输入框,怎么分别验证,分别提示?

很简单,让<el-form-item>里面继续嵌套<el-form-item>即可,外层<el-form-item>只负责写label,不写prop,内层的<el-form-item>不写label,写prop。

验证提示会显示在每个内层<el-form-item>的下方。

如何移除某个输入框的验证结果?

比如有2个输入框,前者如果是后者的2倍则通过。

现在我前面填写了10,后面填写6,验证出错,这时候后者报红字错误。我发现是前者写错了,改成了12,这样符合了要求,但是后者的红字错误不会消失,怎么办?

只能手动清除某个输入框的报错,做法是:

  1. <el-form-item>ref,值可以是字段名。
  2. 在validator末尾写上this.$refs.xxx.clearValidate()即可。

明明写的是数字,为什么type: 'number'验证不通过?

首先,给

v-model

加上

.number

然后,服务器返回的字段,必须转换为数字再复制给form。

如何快捷生成表单的rules?

当一个表单快写完了,就该写

rules

,但是这玩意写起来很烦,内容大部分一样,但是键名都各有区别,比如:

      rules: {
        isOwner: [
          { required: true, message: "是否户主不能为空", trigger: "blur" }
        ],
        phoneNumber: [
          { required: true, message: "联系电话不能为空", trigger: "blur" },
          { len: 11, message: "手机号应为11位", trigger: "blur" }
        ],
      },

大部分都是

xxx不能为空

这类话。有没有快捷生成办法?

我本来打算写一个工具来专门生成,后来觉得没必要,因为没办法特别的智能,所以简单生成一下,然后再做加工就好了。

方案:

  1. 创建@/utils/genCode.js,写入:
module.exports = {
  rules: function (form) {
    let rules = {};
    for (let key in form) {
      if (Array.isArray(form[key])) {
        rules[key] = [{ required: true, message: "不能为空", trigger: "blur", type: 'array' }];
      } else {
        rules[key] = [{ required: true, message: "不能为空", trigger: "blur" }];
      }
    }
    console.log(JSON.stringify(rules, null, 2).replace(/"(.+?)":/g, '$1:').replace(/"/g, "'"));
  },
};
  1. 在main.js写入:
if (process.env.NODE_ENV === 'development') {
  Object.defineProperty(Vue.prototype, '$genCode', { value: require('@/utils/genCode') });
}

使用:

  1. 在提交按钮事件最顶部加上这2行代码:
      this.$genCode.rules(this.form);
      return;
  1. 在浏览器里把必填的字段都填上随意字符,不必填的字段如果你不想设rule,可以不填。
  2. 点击提交按钮,会打印出来一片代码,将代码贴到编辑器,然后进一步修改即可。
  3. 用完删掉this.$genCode.rules(this.form);return;这2行就完事。

如何快捷生成表单的'data.form'?

首先问自己懒不懒,如果懒,可以直接给

data.form

赋值为

{}

,只要Form中没有Checkbox-Group就行,因为Checkbox-Group初始化的时候就要读取

data.form

中的属性,而且必须是数组,所以

{}

这种空对象会报错。Element UI剩下的所有组件都没问题,哪怕是级联选择器这种value一定是数组的组件,也不会报错。

如果你比较勤快,希望代码正规一点,可以模仿上一道题的方法。

第一步,看Form里有没有Checkbox-Group,有的话将它的属性先写好,比如现在是:

form: {
  xxcheckboxList: [],
}

第二步,创建@/utils/genCode.js,写入:

module.exports = {
  form: function (form) {
    let data = {};
    for (let key in form) {
      if (Array.isArray(form[key])) {
        data[key] = [];
      } else if (typeof form[key] === 'string') {
        data[key] = '';
      } else if (typeof form[key] === 'boolean') {
        data[key] = form[key];
      }
    }
    console.log(JSON.stringify(data, null, 2).replace(/"(.+?)":/g, '$1:').replace(/"/g, "'"));
  },
};

第三步,在main.js写入代码,跟上题内容一样。

第四步,在提交按钮事件最顶部加上这2行代码:

      this.$genCode.rules(this.form);
      return;

第五步,必填不必填的字段一律填,然后点击提交按钮,剩下的不说了。用完别忘记注释掉。

弄懂Form的resetFields方法原理,并谨慎使用

resetFields方法原理

Form的resetFields方法的用法是

this.$refs[refName].resetFields();

,这个方法做的事就是将表单的某些数据(为什么说是“某些”请继续看)重置为初始值(即

data

加载的时候

form

的值),重置过程大致是:

  1. 先找Form身上设置的model,也就是表单数据对象,比如叫form
  2. 再找Form里的各个<el-form-item>,看它身上的prop,比如payName
      <el-form-item label="XX设置" prop="payName">
        <el-input
          v-model="form.payName"
        />
      </el-form-item>

然后将

payName

对应的

form.payName

设为data里的

form

初始化的时候的值,初始化的时候没有这个属性的话,则重置为

undefined

或者

[null]

。依次处理每个

<el-form-item>

。如果某个

<el-form-item>

没有

prop

,那么也不会有属性被重置。

看到了吗?

  1. 没有被prop的属性,resetFields就管不到,不会对其做任何操作。
  2. v-model="form.payName"resetFields完全没关系!

如果不用resetFields还能用什么方案?

可以重置为空对象:

this.form = {}

;或者重置为

this.form = {xx: []}

这种带属性的对象。

两种方案的对比如下:
resetFields方案手动重赋值方案依赖$ref依赖不依赖方案执行时机Form元素必须渲染才能执行,也就是说$refs必须能取到值可以在Dialog任何周期钩子里执行重赋值,也可以在触发弹出Dialog的函数里执行(通常用这种方案)必须有

prop="属性名"

必须不必

this.form

初始值只在

data

form

里设置在

form

和重赋值语句中都要设置
可见:resetFields方案的顾虑多,重赋值方案的顾虑少(但不是没有顾虑),只需要关注重赋值初始值即可,但假如初始值有属性,却无脑重赋值成了

{}

也会出事故。

慎用resetFields的场合

假设有个搜索查询表单,初始值是:

      queryParams: {
        pageNum: 1,
        pageSize: 10,
      },

有一个选填的Select,我选择了一个值,也就是

form.a

设为

1

,然后Select的change事件函数根据这个值计算出了另一个值

form.b

,比如是乘以100的关系,于是

form.b

得到

100

,然后,我点击了提交,服务器要的是

form.b

而不是

form.a

100

被成功提交,到目前没啥问题。

接着,我点击“重置”按钮,此时用

resetFields

来重置表单,其结果是:

  • form.a被重置为undefined,因为Select设置了prop="a"
  • form.b没任何变化,因为没有哪个prop设置为b,所以form.b此时还是100

现在,表面上看,表单都被重置,Select的值也是空,但是,

form.b

的值依然没有变,是

100

。紧接着,重置按钮进行下一步操作,也就是根据

queryParams

重新请求数据,此时

queryParams

是这样:

      queryParams: {
        pageNum: 1,
        pageSize: 10,
        b: 100,
      },

到此,问题出现:我本意是重置所有搜索条件,但是

b

并没有被重置,查询结果也一定是错的。

解决方案:

其实这事解决起来很容易,我列出3种方案,你可以选择一种你认为最无脑、最不容易出错的方案。

方案1:

queryParams

初始值加入

b: null

方案2:取消

change

事件监听,改为submit的时候才计算所有的

b

,但是要注意,不要给

queryParams.b

赋值,不然又成了无法resetFields,应该单设变量

b

,参数传

(...queryParams, b)

方案3:直接不用resetFields,改为手动重赋值为初始值。

我个人推荐方案1或方案3,理由是:

  1. 方案1是只要哪个属性没在prop就一律写到初始值,比较无脑,但可能会忘了写。
  2. 方案2将计算放到提交事件里,注意事项比较多,也要用到解构赋值,也不利于封装统一的提交函数。
  3. 方案3手动赋值,好处是初始值不用考虑b,重赋值也不用考虑b,重赋值无脑保持跟初始值一样即可。如果初始值是空对象,那就更好了,无脑重赋值为空对象就行了。

Select要给form的多个属性赋值,如何写代码?

我们用

:value=""

给Option指定输出的value值,但如果想一次性给form的多个属性赋值,该怎么办呢?比如又要传递userId,又要传递nickName,怎么办呢?(这里我假设你已经知道nickName有可能不会被重置的风险以及解决办法。)可以这样写

@change

        <el-form-item label="人员" prop="userId">
          <el-select v-model="form.userId" placeholder="请选择人员"
            clearable
            :style="{width: '100%'}"
            @change="form.nickName = personList.find(v=>$event===v.userId).nickName"
          >
            <el-option v-for="(item, index) in personList" :key="index"
              :label="item.nickName"
              :value="item.userId"
            ></el-option>
          </el-select>
        </el-form-item>

其中

$event

指向Select的值,也就是

form.userId

当然,如果后端除了

userId

nickName

,还要其他值,那么我建议写一个事件函数比较清晰。

Date-Picker或者Time-Picker加上is-range的话,值是数组,怎么拆成form的2个属性?

这要比上个问题好解决,值是数组的话,可以在

@change

里直接解构赋值,这样就拆成了form的2个属性。(这里我假设你已经知道startTime、endTime有可能不会被重置的风险以及解决办法。)

  <el-time-picker
    is-range
    v-model="form.range"
    range-separator="至"
    start-placeholder="开始时间"
    end-placeholder="结束时间"
    placeholder="选择时间范围"
    @change="[form.startTime, form.endTime] = $event"
  >
  </el-time-picker>

有个小疑问

那么,上一个问题(《Select要给form的多个属性赋值,如何写代码?》),为啥我们不把Select的值直接设为

item

,然后把

item

解构呢?

因为下面这种写法,在编辑器里是报错的,原因是

form.nickName

不能是这种表达式,必须只能是变量名。

    @change="{nickName: form.nickName} = $event" // 非法写法

还有人会问,写成

@change="form.nickName = $event.nickName"

不就行了么?

确实,写法是没错,但是用法有悖论。

一、value是

item

对象,意味着服务器可以收到这个对象,为什么还要拆分呢?

二、如果说,后端不喜欢处理对象,就喜欢拆分传送,那么,你不仅要写

form.nickName = $event.nickName

,你还要写

form.userId = $event.userId

,等于要写2行语句,这种情况下就应该专门定义方法了,并不省事,对不对?

三、当你不是新建表单,而是编辑表单的时候,你就要给Select的Option的value传递对象,以便Select能在UI上正确显示当前值。这时候,你就要找后端给你返这个现成的对象,或者自己拼出这个对象,不然,Select是不会显示当前值的。并不省事,对不对?

最后提醒一句,绑定对象类型的话,官方说:

如果 Select 的绑定值为对象类型,请务必指定 value-key 作为它的唯一性标识。

动态表单字段

所谓“动态表单”,有2种解释,一种是根据data动态生成输入框,一种是在模板写死代码,用

v-show

来控制输入框。今天说后一种。

v-show

来控制输入框的前提下,提交的时候应当判断到底提交哪些字段,有些字段就应该不提交。比如:

let params;
if (['0','1','2'].includes(this.form.deptType)) { // 部门类型为0/1/2时
  const { lng, lat, ...rest } = this.form; // 忽略经纬度
  params = rest;
} else if (this.form.deptType === '3') { // 部门类型为3时
  params = this.form; // 全要
} else if (this.form.deptType === '4') { // 部门类型为4时
  const { deptAddress, deptDesc, lng, lat, ...rest } = this.form; // 忽略地址、描述、经纬度
  params = rest;
}
// 接下来提交params即可。

动态修改rules

比如,某些前提下a字段不必填,另一些前提下a字段必填,怎么办?

  1. rules要把字段写全,即使有些字段的requiredfalse。总之按照默认状态设置truefalse,但字段一定要写进去。
  2. 在触发动态变化的事件里修改rules
@change="onChangeDeptType"
  1. 凡是可能变化的字段都要加上。比如:
    onChangeDeptType(value) {
      if (['0','1','2'].includes(this.form.deptType)) {
        this.rules.lat[0].required = false;
        this.rules.lng[0].required = false;
        this.rules.deptAddress[0].required = true;
        this.rules.deptDesc[0].required = true;
      } else if (this.form.deptType === '3') {
        this.rules.lat[0].required = true;
        this.rules.lng[0].required = true;
        this.rules.deptAddress[0].required = true;
        this.rules.deptDesc[0].required = true;
      } else if (this.form.deptType === '4') {
        this.rules.lat[0].required = false;
        this.rules.lng[0].required = false;
        this.rules.deptAddress[0].required = false;
        this.rules.deptDesc[0].required = false;
      }
    }

Input的问号注释

我们在别的UI框架中可能见过这样的问号注释,鼠标悬停能弹出气泡内容:

image.png

但官方手册并没有介绍这种用法,只是说,可以在Input里插入图标。今天我就来变相实现输入框的问号注释。

              <el-input v-model="form.nickName" placeholder="请输入昵称">
                <el-popover
                  slot="suffix"
                  placement="top"
                  title="标题"
                  width="200"
                  trigger="hover"
                  content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。">
                  <i slot="reference" class="el-icon-question color-000"></i>
                </el-popover>
              </el-input>
  1. 官方手册是插入了一个问号Icon,但是今天我们不但是要插入Icon,还要鼠标悬停能弹出气泡,所以我插入了一个Popover。
  2. Popover里面又插入了一个Icon,是问号图标。这样就OK了。
  3. 如果觉得图标颜色不明显,可以设个色值,比如color-000是我自定义的color: #000;,你可以随意改。

最后是这样子:

image.png

另外,你可能在有些网站看到的图标是叹号,我认为叹号和问号的区别在于:

  • 当用户很大概率不知道怎么填内容的时候,用问号。
  • 当用户确定知道怎么填,只是想看更多的注释的时候,甚至是起警告作用的时候,用叹号。

如何避免发送form的临时/私有属性?

比如Input组件的值是

form.a

,由它计算出一个

form.b

是服务器需要的值,但是服务器不需要

form.a

,你的一个做法是把a和b都传上去,但是可能后端程序员会不高兴,因为无用字段多了他会烦,对前端程序员也不太好,因为参数当然是越少越好。

你可以使用剩余操作符来赋值:

      const {a, ...restParams} = this.form;
      postXXX(restParams).then(response => {
        // ...
      })

这样的话,

form.a

就被排除了,也不会被上传了,并且不会影响

form

的数据。

**但是,这并不是最佳实践!最佳实践是将临时属性加一个私有标记,比如

_a

,也就是前置下划线,发送的时候用axios的拦截器统一拦截,过滤掉它。**

所以上例Input组件的值改为

form._a

,而

form.b

不动。

怎么过滤

form._a

,只上传

form.b

在axios的请求拦截器里过滤,凡是属性以

_

为首字符的,就不要发送。

service.interceptors.request.use(
  config => {
    if (config.params && Object.keys(config.params).some(v => v[0] === '_')) {
      let newParams = {};
      for (let key in config.params) {
        if (key[0] !== '_') {
          newParams[key] = config.params[key];
        }
      }
      config.params = newParams;
    }
    if (config.data && Object.keys(config.data).some(v => v[0] === '_')) {
      let newData = {};
      for (let key in config.data) {
        if (key[0] !== '_') {
          newData[key] = config.data[key];
        }
      }
      config.data = newData;
    }
    return config
  },
  error => {
    Promise.reject(error)
  }
)
标签: vue.js elementui 前端

本文转载自: https://blog.csdn.net/weixin_55639808/article/details/135130369
版权归原作者 【南汐】前端 所有, 如有侵权,请联系我们删除。

“Element UI各种使用问题汇总(Input、Form篇)”的评论:

还没有评论