I have a file input blade component that uses alpine js for uploading the image file to Livewire while using the Livewire file upload events for user feedback.
I am trying to create the file input something like filepond using alpine js
Everything works as long as I use the file-input component once.
<div>
<x-file-input wire:model="icon" label="Browser"></x-file-input>
</div>
But if I use it twice in the same file like below, the files are getting all mixed up
<div>
<x-file-input wire:model="icon" label="Browse"></x-file-input>
</div>
<div class="mt-10">
<x-file-input wire:model="coverImage" label="Browse some other file"></x-file-input>
</div>
How do I separate these two components so that they don't clash with each other?
File Input component
<label
x-data="fileUploader()"
for="icon"
class="bg-gray-500 rounded-md w-full min-h-10 flex items-center justify-center text-white font-semibold overflow-hidden"
>
<span x-show="!isUploading && !isUploaded">{{ $label }}</span>
<div x-show="isUploading || isUploaded" class="bg-black w-full flex justify-center relative">
<div class="absolute left-0 top-0 mx-3 mt-2 flex flex-col">
<span class="text-white text-xs" x-text="name"></span>
<span class="text-white text-xs" x-text="size"></span>
</div>
<div class="absolute flex items-start mt-2 mx-3 right-0 top-0">
<div class="inline-flex mr-3">
<p class="leading-none">
<span class="text-white text-xs" x-text="statusMessage"></span>
<span x-show="isUploading" x-cloak class="text-white text-xs" x-text="progress"></span>
</p>
</div>
<div class="inline-flex items-center">
<svg x-show="isUploading" x-cloak class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<button type="button" x-show="isUploaded" x-on:click="removeUpload" x-cloak class="bg-opacity-25 bg-white rounded-full shadow-inner" style="padding: 5px">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
<img wire:ignore class="h-48 object-cover" :src="previewSource">
</div>
<input x-ref="input" x-on:change="uploadImage" class="hidden" type="file" id="icon">
</label>
<script>
function fileUploader() {
return {
file: @entangle($attributes->wire('model')),
uploadedFilename: '',
previewSource: '',
statusMessage: '',
isUploading: false,
isUploaded: false,
progress: 0,
name: '',
size: '',
uploadImage(event) {
const fileList = event.target.files
this.previewSource = URL.createObjectURL(fileList[0]);
this.statusMessage = 'Uploading';
this.name = fileList[0].name;
this.bytesForHuman(fileList[0].size);
this.isUploading = true
@this.upload('{{ $attributes['wire:model'] }}', fileList[0], (uploadedFilename) => {
this.uploadedFilename = uploadedFilename
this.isUploading = false
this.isUploaded = true
this.statusMessage = 'Upload complete'
event.target.value = null
}, () => {
// on error
}, (event) => {
this.progress = event.detail.progress
})
},
removeUpload() {
this.previewSource = ''
@this.removeUpload('{{ $attributes['wire:model'] }}', this.uploadedFilename, () => {
this.isUploaded = false
this.uploadedFilename = ''
this.progress = 0
this.name = ''
this.size = ''
this.statusMessage = ''
})
},
bytesForHuman(bytes) {
let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
let i = 0
for (i; bytes > 1024; i++) {
bytes /= 1024;
}
this.size = bytes.toFixed(1) + ' ' + units[i]
}
}
}
</script>