Как очистить input type="file" компонент в Vue
Как правильно очистить value инпута с типом file? На компонент BaseFileUpload я добавляю v-model="photo" но в консоли ошибка "InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string."
BaseFileUpload.vue
<template>
<div class="form-group">
<label v-if="label">{{ label }}</label>
<input
type="file"
class="form-control-file"
ref="fileupload"
:value="value"
@change="onChange($event.target.files)"
/>
</div>
</template>
<script>
export default {
name: 'BaseFileUpload',
props: {
label: {
type: String,
required: false,
default: '',
},
value: {
type: File
},
},
methods: {
onChange(files) {
this.$emit('change', files[0]);
},
},
};
</script>
Form.vue
<BaseFileUpload v-model="photo" @change="onChangeFileUpload($event)" />
<button @click="clear()">clear</button>
data() {
return {
photo: null,
};
},
methods: {
clear() {
this.photo = null;
}
}
Ответы (1 шт):
Приведенная ошибка
не удалось установить свойство 'value' для 'HTMLInputElement': этот элемент ввода принимает имя файла, которое может быть программно установлено только в пустую строку
уже фактически содержит ответ.
Если мы не можем программно установить для input type="file" значение value,
значит директива v-model для приведенной вами реализации не подходит, потому что, директива v-model как мы знаем работает по принципу:
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
и для нашего случая, это значит, что при любом изменении value у <input> срабатывает обработчик, которые выполняет, указанную выше, логику, т.е. присваивает новое значение. Вот здесь то Vue и пробрасывает ошибку, что не может присвоить новое значение, потому что данный тип поля ввода - type="file", как отмечено выше, не может быть программно изменен на отличное от пустой строки значение.
В интернете много дискуссий по этому поводу. Я приведу вам один из вариантов как пример решения вашей задачи. В код вставлены комментарии, думаю код не вызовет вопросов.
const BaseFileUpload = Vue.component('BaseFileUpload', {
props: {
label: {
type: String,
required: false,
default: '',
}
},
methods: {
onChange(e) {
// здесь же можно подключить логику обработки файла (загрузку и прочее)
// ...
// или отдаем родителю файлы
this.$emit('change', e.target.files);
// также можно сразу очистить поле ввода
// this.$refs.fileupload.value = null
},
},
template: `
<div>
<label v-if="label">{{ label }}</label>
<input
type="file"
class="form-control-file"
ref="fileupload"
multiple
@change="onChange($event)"
/>
</div>`
});
const app = new Vue({
el: "#app",
components: {
BaseFileUpload
},
data() {
return {
images: []
};
},
methods: {
onChangeFileUpload(data) {
this.images = [];
// Здесь получаем файлы
// и можем подключить логику обработки
// здесь изображения просто отрисовываются на экран
[...data].forEach((element) => {
this.createImage(element)
});
},
clear() {
// очищаем переменные
this.images = [];
// полю ввода файла присваиваем пустую строку, что и очищает его
this.$refs.upload.$refs.fileupload.value = null;
},
createImage(file) {
var image = new Image();
var reader = new FileReader();
reader.onload = (e) => {
this.images.push(e.target.result);
};
reader.readAsDataURL(file);
}
}
})
h3 {
margin: 0;
}
img {
max-height: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<h3>Vue - загрузка файлов</h3>
<base-file-upload ref="upload" label="Выберите изображение(я)" @change="onChangeFileUpload($event)">
</base-file-upload>
<div v-if="images.length > 0">
<hr>
<img v-for="image in images" :src="image" />
<hr>
<button @click="clear()">Очистить все</button>
</div>
</div>
Принцип такой:
убираем v-model, для очистки поля ввода используем $refs, в метод onChangeFileUpload подключаем логику обработки введенных файлов.