I am facing an issue with a child component where the input field is obscured by a slotted element. The parent component provides the slotted click event element, along with a preview of the file(s) and the option to delete them. I am struggling to implement the select and preview functionality in the context of a child/parent relationship.
Below is the code I have come up with so far, but I am stuck and confused about the next steps.
The slot successfully triggers the event in the child component, but I encounter a "TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'." error when trying to render the files as currently written. What am I missing here?
If more information is needed, please feel free to ask! Thank you!
NOTE: I also need to ensure compatibility with V-model, but I am unsure how to achieve that at the moment.
UploadMediaFiles (Child Component)
<template>
<div class="upload-media-files">
<input
id="input-file"
type="file"
accept="*"
multiple
@change="addMedia"
class="_add-media-input"
ref="input"
/>
<label for="input-file">
<slot :openFileDialog="openFileDialog">
<img
src="https://www.clipartmax.com/png/middle/142-1422132_png-file-svg-upload-file-icon-png.png"
alt=""
/>
</slot>
</label>
</div>
</template>
<style lang="sass" scoped>
input
display: none
</style>
<script>
export default {
name: 'UploadMediaFiles',
props: {
multiple: { type: Boolean },
accept: { type: String },
},
data() {
return {
files: [],
}
},
computed: {},
methods: {
async addMedia(event) {
const files = event.target.files || event.dataTransfer.files
if (!files.length) return
console.log(`files → `, files)
this.files.push(files)
this.$emit('selected', this.files)
},
openFileDialog() {
this.$refs.input.click()
},
},
}
</script>
SelectAndPreviewFiles (Parent Component)
<template>
<div class="select-and-preview-files">
<div v-if="selectedFiles">
<div :key="index" v-for="(selectedFile, index) in selectedFiles">
<img :src="selectedFile" alt="" />
<button @click="deleteFile(index)">Delete</button>
</div>
</div>
<UploadMediaFiles @selected="(files) => selectFiles(files)" v-slot="{ openFileDialog }">
<button @click="openFileDialog">
<img
src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a1/Circle-icons-upload.svg/1200px-Circle-icons-upload.svg.png"
alt=""
/>
</button>
</UploadMediaFiles>
</div>
</template>
<style lang="sass" scoped>
img
width: 20%
margin: auto
display: block
margin-bottom: 10px
</style>
<script>
import UploadMediaFiles from '../atoms/UploadMediaFiles.vue'
export default {
name: 'SelectAndPreviewFiles',
components: {
UploadMediaFiles,
},
props: {},
data() {
return {
selectedFiles: [],
}
},
computed: {},
methods: {
selectFiles(files) {
this.selectedFiles.push(files)
this.previewImage(files)
},
previewImage(files) {
var vm = this
for (var index = 0; index < files.length; index++) {
var reader = new FileReader()
reader.onload = function (event) {
const imageUrl = event.target.result
vm.files.push(imageUrl)
}
reader.readAsDataURL(files[index])
}
},
deleteFile(index) {
this.selectedFiles.splice(index, 1)
},
},
}
</script>
CodePen without the parent-child relationship https://codepen.io/LovelyAndy/pen/gOmYGKO?editors=0001