首页
前端开发
为什么当我在 vue 组件上输入另一个输入时,输入文件的值丢失了?
为什么当我在 vue 组件上输入另一个输入时,输入文件的值丢失了?
[英] Why the value of input file missing when I input the another input on the vue component?
本文介绍了为什么当我在 vue 组件上输入另一个输入时,输入文件的值丢失了?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我有两个组件
我的第一个组件(父组件)是这样的:
<div>...<form-input id="name" name="name" v-model="name">Name</form-input><form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">出生日期</form-input><form-input id="avatar" name="avatar" type="file" v-on:triggerChange="onFileChange($event)">头像</form-input><form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">手机号码</form-input>...
</模板><脚本>导出默认{数据() {返回 {名称:空,出生日期:空,手机号码:空}},方法: {onFileChange(e) {让自己=这个this.validate(e.target.files[0]).then(功能(资源){让文件 = e.target.files,阅读器 = 新的 FileReader()//如果有任何值如果(文件.长度){self.removeErrorMessageUpload()self.files = 文件 [0]reader.onload = (e) =>{self.updateProfileAvatar(e.target.result)}reader.readAsDataURL(文件[0])}}).catch(函数(){//在图像无效的情况下做一些事情self.displayErrorMessageUpload()})},验证(图像){让自己=这个返回新的承诺(功能(解决,拒绝){//验证文件类型if (!self.allowableTypes.includes(image.name.split(".").pop().toLowerCase())) {拒绝()}//验证文件大小if (image.size > self.maximumSize) {拒绝()}//验证图像分辨率让 img = new Image()img.src = window.URL.createObjectURL(图片)img.onload = 函数(){让宽度 = img.naturalWidth,高度 = img.naturalHeightwindow.URL.revokeObjectURL(img.src)如果(宽度!= 100&&高度!= 100){拒绝()}别的 {解决()}}})},}}
从父组件调用子组件(表单输入组件)
我的子组件是输入类型文本、输入类型日期、输入类型文件和输入类型编号.我将它们全部合并为 1 个组件
子组件像这样:
<div class="form-group"><label :for="id" class="col-sm-3 control-label"><slot></slot></label><div class="col-sm-9"><input :type="type" :name="name" :id="id" class="form-control" :value="value" v-on:change="applySelected($event)" @input="$emit('input', $event.target.value)">
</模板><脚本>导出默认{名称:表单输入",道具: {'id':字符串,'名称':字符串,'是必须的': {类型:布尔型,默认值:真},'类型': {类型:字符串,默认() {if(this.type == 'number')返回数字"返回文本"}},'价值': {类型:[字符串,数字]}},方法: {applySelected(e) {this.$emit('triggerChange', e)}}}
因为我合并成 1 个 komponent,我遇到了一个新问题
如果我输入输入类型文件,文件的值会显示在输入类型文件中
但是如果我在输入类型文本中输入,输入类型文件的值丢失
为什么缺少输入类型文件的值?
演示:
Vue.component('form-input', {模板:#form-input-tpl",名称:表单输入",道具: {'id':字符串,'名称':字符串,'isRequired': {type: Boolean, default: true},'type': { type: String, default () {if (this.type == 'number') {return 'number'} else {return 'text'}}},'值':{ 类型:[字符串,数字]}},方法: {applySelected(e) { this.$emit('triggerChange', e) }}});新的 Vue({el: '#app',数据: {名称:空,出生日期:空,手机号码:空},方法: {onFileChange(e) {//...}}})
<script src="https://unpkg.com/vue"></script><模板id="form-input-tpl"><div class="form-group"><label :for="id" class="col-sm-3 control-label"><slot></slot></label><div class="col-sm-9"><input :type="type" :name="name" :id="id" class="form-control" :value="value" v-on:change="applySelected($event)" @input="$emit('input', $event.target.value)">
</模板><div id="应用程序"><h3>选择一个文件,然后输入名称.文件将被重置.</h3><div><form-input id="name" name="name" v-model="name">Name</form-input><form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">出生日期</form-input><form-input id="avatar" name="avatar" type="file" v-on:triggerChange="onFileChange($event)">头像</form-input><form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">手机号码</form-input>
解决方案
所以问题是:
<块引用>在 <form-input type="file">
中选择一个文件后,如果您在 <form-input type="type">
,
擦除.这是为什么?
发生这种情况是因为当您编辑 <form-input type="text">
时,Vue 会重新绘制"组件.
当它重绘<form-input type="file">
时,它会回到Nothing selected",因为它是一个新的<input type=文件">
.
解决方案:保留文件的值 作为 Kaiido 点在 评论 ,在最新 版本的浏览器,你可以设置
以标准方式.
这就是下面的代码所做的.它监视 value
属性(当父使用 v-model
并将其值设置为 .files
属性时出现的 .files
> .
我们必须使用两个
(和v-if
/v-else
)因为当它是一个<input type="file">
,:value
属性可以设置,事件处理器应该不同(@change="$emit('input', $event.target.files)"
) 并且我们想要保留一个 ref
以便我们可以设置 files
.
完整的工作演示如下 .
Vue.component('form-input', {模板:#form-input-tpl",名称:表单输入",道具: {'id':字符串,'名称':字符串,'isRequired': {type: Boolean, default: true},'type': {type: String, default: 'text'},'值':{类型:[字符串,数字,文件列表,数据传输]}},安装(){//在创建时设置文件或在父值更改时更新this.$watch('value', () => {if (this.type === "file") { this.$refs.inputFile.files = this.value;}}, { 立即:真});}});新的 Vue({el: '#app',数据: {名称:空,出生日期:空,手机号码:空,文件:空}})
<script src="https://unpkg.com/vue"></script><模板id="form-input-tpl"><div class="form-group"><label :for="id" class="col-sm-3 control-label"><slot></slot></label><div class="col-sm-9"><input v-if="type !== 'file'" :type="type" :name="name" :id="id" class="form-control" :value="value" @input="$emit('input', $event.target.value)"><input v-else :type="type" :name="name" :id="id" class="form-control" @change="$emit('input', $event.target.files)"ref="输入文件">
</模板><div id="应用程序"><div><form-input id="name" name="name" v-model="name">Name</form-input><form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">出生日期</form-input><form-input id="avatar" name="avatar" type="file" v-model="files">头像</form-input><form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">手机号码</form-input>
使用您的 file-change
事件和 validate
函数:
Vue.component('form-input', {模板:#form-input-tpl",名称:表单输入",道具: {'id':字符串,'名称':字符串,'isRequired': {type: Boolean, default: true},'type': {type: String, default: 'text'},'值':{类型:[字符串,数字,文件列表,数据传输]}},安装(){//在创建时设置文件或在父值更改时更新this.$watch('value', () => {if (this.type === "file") { this.$refs.inputFile.files = this.value;}}, { 立即:真});}});新的 Vue({el: '#app',数据: {名称:空,出生日期:空,手机号码:空,filesVModel:空,允许类型:['jpg', 'jpeg', 'png'],最大尺寸:1000,文件:空},方法: {onFileChange(e) {console.log('onfilechange!');让自己=这个this.validate(e.target.files[0]).then(功能(资源){让文件 = e.target.files,阅读器 = 新的 FileReader()//如果有任何值如果(文件.长度){self.removeErrorMessageUpload()self.files = 文件 [0]reader.onload = (e) =>{self.updateProfileAvatar(e.target.result)}reader.readAsDataURL(文件[0])}}).catch(函数(错误){//在图像无效的情况下做一些事情self.displayErrorMessageUpload(err)})},验证(图像){让自己=这个返回新的承诺(功能(解决,拒绝){//验证文件类型if (!self.allowableTypes.includes(image.name.split(".").pop().toLowerCase())) {拒绝("类型" + image.name.split(".").pop().toLowerCase() + " 是不允许的.")}//验证文件大小if (image.size > self.maximumSize) {拒绝(图像大小" + image.size + "大于允许的"+ self.maximumSize + .")}//验证图像分辨率让 img = new Image()img.src = window.URL.createObjectURL(图片)img.onload = 函数(){让宽度 = img.naturalWidth,高度 = img.naturalHeightwindow.URL.revokeObjectURL(img.src)如果(宽度!= 100&&高度!= 100){拒绝("宽度和高度是"+宽度+"和"+高度+"而不是100")} 别的 {解决()}}})},displayErrorMessageUpload(msg) {console.log('displayErrorMessageUpload', msg);},removeErrorMessageUpload() {console.log('removeErrorMessageUpload');},更新配置文件头像(结果){console.log('updateProfileAvatar', result);}}})
<script src="https://unpkg.com/vue"></script><模板id="form-input-tpl"><div class="form-group"><label :for="id" class="col-sm-3 control-label"><slot></slot></label><div class="col-sm-9"><input v-if="type !== 'file'" :type="type" :name="name" :id="id" class="form-control" :value="value" @input="$emit('input', $event.target.value)"><input v-else :type="type" :name="name" :id="id" class="form-control" @change="$emit('input', $event.target.files)"ref="inputFile" v-on:change="$emit('file-change', $event)">
</模板><div id="应用程序"><div><form-input id="name" name="name" v-model="name">Name</form-input><form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">出生日期</form-input><form-input id="avatar" name="avatar" type="file" v-model="filesVModel" @file-change="onFileChange">头像</form-input><form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">手机号码</form-input>
I have two component
My first component (parent component) like this :
<template>
<div>
...
<form-input id="name" name="name" v-model="name">Name</form-input>
<form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">Date of Birth</form-input>
<form-input id="avatar" name="avatar" type="file" v-on:triggerChange="onFileChange($event)">Avatar</form-input>
<form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">Mobile Number</form-input>
...
</div>
</template>
<script>
export default {
data() {
return {
name: null,
birthDate: null,
mobileNumber: null
}
},
methods: {
onFileChange(e) {
let self = this
this.validate(e.target.files[0])
.then(function(res) {
let files = e.target.files,
reader = new FileReader()
// if any values
if (files.length) {
self.removeErrorMessageUpload()
self.files = files[0]
reader.onload = (e) => {
self.updateProfileAvatar(e.target.result)
}
reader.readAsDataURL(files[0])
}
})
.catch(function() {
// do something in the case where the image is not valid
self.displayErrorMessageUpload()
})
},
validate(image) {
let self = this
return new Promise(function(resolve, reject) {
// validation file type
if (!self.allowableTypes.includes(image.name.split(".").pop().toLowerCase())) {
reject()
}
// validation file size
if (image.size > self.maximumSize) {
reject()
}
// validation image resolution
let img = new Image()
img.src = window.URL.createObjectURL(image)
img.onload = function() {
let width = img.naturalWidth,
height = img.naturalHeight
window.URL.revokeObjectURL(img.src)
if (width != 100 && height != 100) {
reject()
}
else {
resolve()
}
}
})
},
}
}
</script>
From the parent component, it will call child component (form input component)
My child component are input type text, input type date, input type file and input type number. I combine all of them into 1 component
The child component like this :
<template>
<div class="form-group">
<label :for="id" class="col-sm-3 control-label"><slot></slot></label>
<div class="col-sm-9">
<input :type="type" :name="name" :id="id" class="form-control" :value="value" v-on:change="applySelected($event)" @input="$emit('input', $event.target.value)">
</div>
</div>
</template>
<script>
export default {
name: "form-input",
props: {
'id': String,
'name': String,
'isRequired': {
type: Boolean,
default: true
},
'type': {
type: String,
default() {
if(this.type == 'number')
return 'number'
return 'text'
}
},
'value': {
type: [String, Number]
}
},
methods: {
applySelected(e) {
this.$emit('triggerChange', e)
}
}
}
</script>
Because I merge into 1 komponent, I get a new problem
If I input the input type file, the value of file will show in the input type file
But if I input in the input type text, the value of input type file missing
Why the value of input type file missing?
Demo:
Vue.component('form-input', {
template: "#form-input-tpl",
name: "form-input",
props: {
'id': String,
'name': String,
'isRequired': {type: Boolean, default: true},
'type': { type: String, default () {if (this.type == 'number') {return 'number'} else {return 'text'}}},
'value': { type: [String, Number] }
},
methods: {
applySelected(e) { this.$emit('triggerChange', e) }
}
});
new Vue({
el: '#app',
data: {
name: null,
birthDate: null,
mobileNumber: null
},
methods: {
onFileChange(e) {
// ...
}
}
})
<script src="https://unpkg.com/vue"></script>
<template id="form-input-tpl">
<div class="form-group">
<label :for="id" class="col-sm-3 control-label"><slot></slot></label>
<div class="col-sm-9">
<input :type="type" :name="name" :id="id" class="form-control" :value="value" v-on:change="applySelected($event)" @input="$emit('input', $event.target.value)">
</div>
</div>
</template>
<div id="app">
<h3>Select a file, then type a name. The file will be reset.</h3>
<div>
<form-input id="name" name="name" v-model="name">Name</form-input>
<form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">Date of Birth</form-input>
<form-input id="avatar" name="avatar" type="file" v-on:triggerChange="onFileChange($event)">Avatar</form-input>
<form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">Mobile Number</form-input>
</div>
</div>
解决方案
So the problem is:
After you have chosen a file in the <form-input type="file">
, if you type something in the <form-input type="type">
, the <form-input type="file">
erases. Why is that?
This happens because when you edit <form-input type="text">
, Vue will "repaints" the components.
And when it repaints the <form-input type="file">
, it will go back to "Nothing selected" because it is a new <input type="file">
.
Solution: Keeping the files' values
As Kaiido points in the comments , in latest versions of browsers, you can set the files of a <input type="file">
in a standard way.
So this is what the code below does. It watches for the value
property (that comes when the parent uses v-model
and sets its value to the .files
property of the <input type="file">
.
We have to use two <input>
(with v-if
/v-else
) because when it is a <input type="file">
, the :value
property can be set, the event handler should be different (@change="$emit('input', $event.target.files)"
) and we want to keep a ref
so we can set the files
.
Full working demo below .
Vue.component('form-input', {
template: "#form-input-tpl",
name: "form-input",
props: {
'id': String,
'name': String,
'isRequired': {type: Boolean, default: true},
'type': {type: String, default: 'text'},
'value': {type: [String, Number, FileList, DataTransfer]}
},
mounted() {
// set files upon creation or update if parent's value changes
this.$watch('value', () => {
if (this.type === "file") { this.$refs.inputFile.files = this.value; }
}, { immediate: true });
}
});
new Vue({
el: '#app',
data: {
name: null,
birthDate: null,
mobileNumber: null,
files: null
}
})
<script src="https://unpkg.com/vue"></script>
<template id="form-input-tpl">
<div class="form-group">
<label :for="id" class="col-sm-3 control-label"><slot></slot></label>
<div class="col-sm-9">
<input v-if="type !== 'file'" :type="type" :name="name" :id="id" class="form-control" :value="value" @input="$emit('input', $event.target.value)">
<input v-else :type="type" :name="name" :id="id" class="form-control" @change="$emit('input', $event.target.files)" ref="inputFile">
</div>
</div>
</template>
<div id="app">
<div>
<form-input id="name" name="name" v-model="name">Name</form-input>
<form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">Date of Birth</form-input>
<form-input id="avatar" name="avatar" type="file" v-model="files">Avatar</form-input>
<form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">Mobile Number</form-input>
</div>
</div>
Using your file-change
event and validate
function:
Vue.component('form-input', {
template: "#form-input-tpl",
name: "form-input",
props: {
'id': String,
'name': String,
'isRequired': {type: Boolean, default: true},
'type': {type: String, default: 'text'},
'value': {type: [String, Number, FileList, DataTransfer]}
},
mounted() {
// set files upon creation or update if parent's value changes
this.$watch('value', () => {
if (this.type === "file") { this.$refs.inputFile.files = this.value; }
}, { immediate: true });
}
});
new Vue({
el: '#app',
data: {
name: null,
birthDate: null,
mobileNumber: null,
filesVModel: null,
allowableTypes: ['jpg', 'jpeg', 'png'],
maximumSize: 1000,
files: null
},
methods: {
onFileChange(e) {
console.log('onfilechange!');
let self = this
this.validate(e.target.files[0])
.then(function(res) {
let files = e.target.files,
reader = new FileReader()
// if any values
if (files.length) {
self.removeErrorMessageUpload()
self.files = files[0]
reader.onload = (e) => {
self.updateProfileAvatar(e.target.result)
}
reader.readAsDataURL(files[0])
}
})
.catch(function(err) {
// do something in the case where the image is not valid
self.displayErrorMessageUpload(err)
})
},
validate(image) {
let self = this
return new Promise(function(resolve, reject) {
// validation file type
if (!self.allowableTypes.includes(image.name.split(".").pop().toLowerCase())) {
reject("Type " + image.name.split(".").pop().toLowerCase() + " is not allowed.")
}
// validation file size
if (image.size > self.maximumSize) {
reject("Image size " + image.size + " is larger than allowed " + self.maximumSize + ".")
}
// validation image resolution
let img = new Image()
img.src = window.URL.createObjectURL(image)
img.onload = function() {
let width = img.naturalWidth,
height = img.naturalHeight
window.URL.revokeObjectURL(img.src)
if (width != 100 && height != 100) {
reject("Width and height are " + width + " and " + height + " and not both 100")
} else {
resolve()
}
}
})
},
displayErrorMessageUpload(msg) {
console.log('displayErrorMessageUpload', msg);
},
removeErrorMessageUpload() {
console.log('removeErrorMessageUpload');
},
updateProfileAvatar(result) {
console.log('updateProfileAvatar', result);
}
}
})
<script src="https://unpkg.com/vue"></script>
<template id="form-input-tpl">
<div class="form-group">
<label :for="id" class="col-sm-3 control-label"><slot></slot></label>
<div class="col-sm-9">
<input v-if="type !== 'file'" :type="type" :name="name" :id="id" class="form-control" :value="value" @input="$emit('input', $event.target.value)">
<input v-else :type="type" :name="name" :id="id" class="form-control" @change="$emit('input', $event.target.files)" ref="inputFile" v-on:change="$emit('file-change', $event)">
</div>
</div>
</template>
<div id="app">
<div>
<form-input id="name" name="name" v-model="name">Name</form-input>
<form-input id="birth-date" name="birth_date" type="date" v-model="birthDate">Date of Birth</form-input>
<form-input id="avatar" name="avatar" type="file" v-model="filesVModel" @file-change="onFileChange">Avatar</form-input>
<form-input id="mobile-number" name="mobile_number" type="number" v-model="mobileNumber">Mobile Number</form-input>
</div>
</div>
这篇关于为什么当我在 vue 组件上输入另一个输入时,输入文件的值丢失了?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文