背景描述
最近在做国际化的功能,然后就发现各种问题,最开始是rules在国际化中不随着语言的变化而变化,为了解决这个问题开始踩坑……
错误日志
vue.runtime.esm.js:619 [Vue warn]: Error in nextTick: "TypeError: Cannot read properties of undefined (reading 'clearValidate')"
found in
---> <Device>
<Device> at src/views/flowinn/device/index.vue
<AppMain> at src/layout/components/AppMain.vue
<Layout> at src/layout/index.vue
<App> at src/App.vue
<Root>
warn @ vue.runtime.esm.js:619
vue.runtime.esm.js:1888 TypeError: Cannot read properties of undefined (reading 'clearValidate')
at VueComponent.eval (VM5890 deviceAdd.vue:136:32)
at Array.eval (vue.runtime.esm.js:1980:1)
at flushCallbacks (vue.runtime.esm.js:1906:1)
解决方法
把this.$nextTick换成this.$forceUpdate即可解决。
使用了forceUpdate,如果是在引入的页面中使用了该方法,记得resetForm,不然弹出框重新打开后,那个错误的message还是在提示。
-------------------------以下是rules不同情况下的使用方法-------------------------------
在下面的几种情况中,都只用到了nextTick和forceUpdate方法,并没有使用resetFields方法。经过验证resetFields方法有问题,有时候会导致部分的input无法输入,切记不要使用。在网上搜索了一下发现有使用setTimeout方法的,那些基本是做赋值用的,这里还是不适用,经过测试完全没有效果。
第一种情况
这种情况下不需要国际化,只是一个简单的form表单验证,而且不是引入的页面或组件,就是一个完整的单一页面。这种情况下直接在data中定义自己的rules就可以实现功能。
data() {
return {
inputDisabled: true,
title:"",
addErrorMessage:"",
addOpen: false,
// 分组选项
queryGroupOptions: [],
// 用户选项
queryUserOptions: [],
// 表单参数
addForm: {
id:null,
devId:"",
ownerId:"",
name:"",
devDesc:"",
longitude:"",
latitude:"",
groupId:"",
},
rules: {
devId: [
{ required: true, message: '请输入用户账号', trigger: 'blur' }
],
}
};
},
第二种情况
需要实现国际化功能,但是不是引入的页面或组件,就是一个完整的单一页面。这种情况下需要借助nextTick方法来实现。在这种情况下,如果使用第一种写法,就会出现一个问题,就是你切换了不同的语言,这时候输入框的不填写时候的错误消息还是默认的中文的,不会随着语言的切换而切换。
export default {
name: "Device",
data() {
return {
inputDisabled: true,
title:"",
addErrorMessage:"",
addOpen: false,
// 分组选项
queryGroupOptions: [],
// 用户选项
queryUserOptions: [],
// 表单参数
addForm: {
id:null,
devId:"",
ownerId:"",
name:"",
devDesc:"",
longitude:"",
latitude:"",
groupId:"",
},
};
},
computed: {
rules() {
let rules = {
devId: [
{ required: true, message: this.$i18n.t('userInput')+this.$i18n.t('indexTablePrimaryKey'), trigger: 'blur' }
],
};
// 引入的页面必须使用forceUpdate, 单一的页面中可以直接使用nextTick
this.$nextTick( () => {
this.$refs['addForm'].clearValidate();
});
return rules;
}
},
}
第三种情况
需要实现国际化功能,是引入的页面或组件。这种情况下需要借助forceUpdate方法来实现。
这种方法下特别要注意,功能虽然实现了,但是出现了另一个问题。就是弹出框打开后,鼠标离开输入框,错误消息提示,然后关闭弹出框,再次打开弹出框的时候,那个错误消息还在,这时候需要调用resetForm来解决。以下的代码中都有。
export default {
name: "Device",
data() {
return {
inputDisabled: true,
title:"",
addErrorMessage:"",
addOpen: false,
// 分组选项
queryGroupOptions: [],
// 用户选项
queryUserOptions: [],
// 表单参数
addForm: {
id:null,
devId:"",
ownerId:"",
name:"",
devDesc:"",
longitude:"",
latitude:"",
groupId:"",
},
};
},
computed: {
rules() {
let rules = {
devId: [
{ required: true, message: this.$i18n.t('userInput')+this.$i18n.t('indexTablePrimaryKey'), trigger: 'blur' }
],
};
// 引入的页面必须使用forceUpdate, 单一的页面中可以直接使用nextTick
this.$forceUpdate( () => {
this.$refs['addForm'].clearValidate();
});
return rules;
}
},
dialogAddOpenFun(chooseId){
this.addErrorMessage = "";
noPageListUser().then(response => {
this.queryUserOptions = response.rows;
});
listBsGroupNoPage().then(response => {
this.queryGroupOptions = response.rows;
});
if(chooseId == null){
this.inputDisabled = false;
this.title = this.$i18n.t('btnAdd');
this.addForm.id=null,
this.addForm.devId="";
this.addForm.ownerId="";
this.addForm.name="";
this.addForm.devDesc="";
this.addForm.longitude="";
this.addForm.latitude="";
this.addForm.groupId="";
// 特别重要
this.resetForm("addForm");
}else{
this.inputDisabled = true;
this.title = this.$i18n.t('btnEdit');
this.addForm.devId=chooseId;
getDevice(chooseId).then(response => {
this.addForm = response.data;
});
}
this.addOpen = true;
},
}
vue完整代码
<template>
<!-- 新增或编辑 -->
<el-dialog
:title="title"
:visible.sync="addOpen"
:close-on-click-modal="false"
width="660px" append-to-body>
<div class="deviceAddOutDiv">
<el-form ref="addForm" :model="addForm" :rules="rules" label-width="100px" @submit.native.prevent>
<el-row>
<el-col :span="24">
<div style="height: 30px; color: #f56c6c;text-align: center;">{{addErrorMessage}}</div>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('indexTablePrimaryKey')" prop="devId">
<el-input v-model="addForm.devId" :disabled="inputDisabled" :maxlength="60" auto-complete="off"
:placeholder="`${this.$t('userInput')}${this.$t('indexTablePrimaryKey')}`" style="width: 100%;" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('indexTableDeviceName')" prop="name">
<el-input v-model="addForm.name" :maxlength="120" auto-complete="off" :placeholder="`${this.$t('userInput')}${this.$t('indexTableDeviceName')}`" style="width: 100%;" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('deviceDescription')" prop="devDesc">
<el-input v-model="addForm.devDesc" :maxlength="120" auto-complete="off" :placeholder="`${this.$t('userInput')}${this.$t('deviceDescription')}`" style="width: 100%;" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('deviceGroup')" prop="groupId">
<el-select v-model="addForm.groupId" filterable clearable :placeholder="`${this.$t('userChoose')}${this.$t('deviceGroup')}`" style="width: 100%;">
<el-option
v-for="item in queryGroupOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item :label="$t('deviceCreate')" prop="ownerId">
<el-select v-model="addForm.ownerId" filterable clearable :placeholder="`${this.$t('userChoose')}${this.$t('deviceCreate')}`" style="width: 100%;">
<el-option
v-for="item in queryUserOptions"
:key="item.id"
:label="`${item.deptName} / ${item.account}`"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('deviceLongitude')" prop="longitude">
<el-input v-model="addForm.longitude" :maxlength="18" auto-complete="off" :placeholder="`${this.$t('userInput')}${this.$t('deviceLongitude')}`" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('deviceLatitude')" prop="latitude">
<el-input v-model="addForm.latitude" :maxlength="18" auto-complete="off" :placeholder="`${this.$t('userInput')}${this.$t('deviceLatitude')}`" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogAddCloseFun">{{$t("btnCancel")}}</el-button>
<el-button type="primary" @click="deviceAddSubmitForm">{{$t("btnConfirm")}}</el-button>
</div>
</el-dialog>
</template>
<script>
import { listBsGroupNoPage } from "@/api/flowinn/group";
import { noPageListUser } from "@/api/flowinn/user";
import { getDevice, addDevice, updateDevice } from "@/api/flowinn/device";
export default {
name: "Device",
data() {
return {
inputDisabled: true,
title:"",
addErrorMessage:"",
addOpen: false,
// 分组选项
queryGroupOptions: [],
// 用户选项
queryUserOptions: [],
// 表单参数
addForm: {
id:null,
devId:"",
ownerId:"",
name:"",
devDesc:"",
longitude:"",
latitude:"",
groupId:"",
},
};
},
computed: {
rules() {
let rules = {
devId: [
{ required: true, message: this.$i18n.t('userInput')+this.$i18n.t('indexTablePrimaryKey'), trigger: 'blur' }
],
};
this.$forceUpdate( () => {
this.$refs['addForm'].clearValidate();
});
return rules;
}
},
created() {
},
methods: {
dialogAddOpenFun(chooseId){
this.addErrorMessage = "";
noPageListUser().then(response => {
this.queryUserOptions = response.rows;
});
listBsGroupNoPage().then(response => {
this.queryGroupOptions = response.rows;
});
if(chooseId == null){
this.inputDisabled = false;
this.title = this.$i18n.t('btnAdd');
this.addForm.id=null,
this.addForm.devId="";
this.addForm.ownerId="";
this.addForm.name="";
this.addForm.devDesc="";
this.addForm.longitude="";
this.addForm.latitude="";
this.addForm.groupId="";
// 特别重要,不然弹出框的rules消息一直不消失
this.resetForm("addForm");
}else{
this.inputDisabled = true;
this.title = this.$i18n.t('btnEdit');
this.addForm.devId=chooseId;
getDevice(chooseId).then(response => {
this.addForm = response.data;
});
}
this.addOpen = true;
},
dialogAddCloseFun(){
this.addOpen = false;
},
deviceAddSubmitForm(){
this.$refs["addForm"].validate(valid => {
if (valid) {
if (this.addForm.id != null) {
updateDevice(this.addForm).then(response => {
if(response.code != 200){
this.addErrorMessage = this.$i18n.t('wordErrorStart')+response.msg;
}else{
this.$modal.msgSuccess(this.$i18n.t('messageUpdateSuccess'));
this.addOpen = false;
this.$parent.getList();
}
});
} else {
addDevice(this.addForm).then(response => {
if(response.code != 200){
this.addErrorMessage = this.$i18n.t('wordErrorStart')+response.msg;
}else{
this.$modal.msgSuccess(this.$i18n.t('messageAddSuccess'));
this.addOpen = false;
this.$parent.getList();
}
});
}
}
});
},
},
mounted() {
}
};
</script>