我有两个组件
我的第一个组件(父组件)是这样的:
<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>
从父组件,它将调用子组件(表单输入组件)
我的子组件是输入类型文本,输入类型日期,输入类型文件和输入类型编号。我将所有这些组合成一个组件
这样的子组件:
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) {
// ...
}
}
})
因为我合并为1个组件,我遇到了一个新问题
如果输入输入类型文件,file的值将显示在输入类型文件
中但是如果我在输入类型文本中输入,则缺少输入类型文件的值
为什么缺少输入类型文件的值?
<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>
&#13;
$pull
&#13;
答案 0 :(得分:2)
所以问题是:
在
<form-input type="file">
中选择文件后,如果您在<form-input type="type">
中输入内容,则<form-input type="file">
会删除。那是为什么?
这是因为当您编辑<form-input type="text">
时,Vue将“重新绘制”组件。
当它重新绘制<form-input type="file">
时,它将返回“未选择任何内容”,因为它是新的<input type="file">
。
以Kaiido为comments,latest versions of browsers, you can set以标准方式指定<input type="file">
的文件。
所以这就是下面的代码所做的。它会监视value
属性(当父级使用v-model
时将其值设置为.files
的{{1}}属性。
我们必须使用两个<input type="file">
(<input>
/ v-if
),因为当它是v-else
时,可以设置<input type="file">
属性,事件处理程序应该不同(:value
),我们希望保留@change="$emit('input', $event.target.files)"
,以便我们设置ref
。
完整的工作演示。
files
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>
事件和file-change
功能:
validate
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);
}
}
})