I am using browser-image-compression library to compress the images before uploading. Now it gives the compressed Image and the compressed image is also stored in the storage folder.
But I want to display the uploaded attachment as soon as the image is uploaded. I tried to dd($this->uploaded_cost_attachments) in component and it gives response there but when I try to <div>@dump($uploaded_cost_attachments)</div> It is always empty.
**
Livewire Component:**
public $cost_attachments = [];
public $uploaded_cost_attachments = [];
public function updatedCostAttachments()
{
$this->validate([
'cost_attachments.*' => 'file|mimes:jpg,png,pdf,xls,xlsx',
]);
if ($this->cost_attachments) {
foreach ($this->cost_attachments as $file) {
$filePath = $file->store('cost_attachments', 'public');
$this->uploaded_cost_attachments[] = [
'file' => $filePath,
'id' => null,
];
}
}
}
Blade.php:
<div class="mt-2">
<div class="relative">
<input onchange="handleImageUpload(event);" type="file" multiple id="file-input" />
</div>
</div>
<div class="mt-2">
@foreach($uploaded_cost_attachments as $key => $file)
<span class="inline-flex items-center gap-x-1.5 rounded-md px-2 py-1 text-xs font-medium
text-gray-900 ring-1 ring-inset ring-gray-200">
<svg wire:click="removeCostAttachment({{ $key }})" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5 cursor-pointer">
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10
11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10
8.94 6.28 5.22z" />
</svg>
<a target="_blank" href="{{ asset('storage/'.$file['file']) }}">
...{{ substr($file['file'], -10) }}
</a>
</span>
@endforeach
</div>
<script>
// Image compression
async function handleImageUpload(event) {
const files = event.target.files;
const options = {
maxSizeMB: 2,
maxWidthOrHeight: 1920,
useWebWorker: true,
}
for (const file of files) {
const fileType = file.type;
if (fileType === "application/pdf") {
// Handle PDF upload without compression
console.log('PDF File:', file.name);
// Upload the PDF file directly without compression
@this.upload('cost_attachments', file, (uploadedFilename) => {
console.log('PDF uploaded:', uploadedFilename);
}, (error) => {
console.error('Upload error:', error);
}, (event) => {
console.log('Upload progress:', event.detail.progress);
});
} else if (fileType.startsWith('image/')) {
try {
// Compress images
const compressedFile = await imageCompression(file, options);
console.log('Compressed Image:', compressedFile.name);
console.log(`Compressed size: ${compressedFile.size / 1024 / 1024} MB`);
// Upload the compressed image
@this.upload('cost_attachments', compressedFile, (uploadedFilename) => {
console.log('Image uploaded:', uploadedFilename);
}, (error) => {
console.error('Upload error:', error);
}, (event) => {
console.log('Upload progress:', event.detail.progress);
});
} catch (error) {
console.error('Image compression error:', error);
}
} else {
console.error('Unsupported file type:', fileType);
}
}
}
</script>
I am using the input without wire:model, following this answer on github.
Finally I solved it by emiting an event from component when the files are uploaded and then when I listen the event in blade I update the uploaded_cost_attachments array with updated array I received in event.
public function costAttachments()
{
$this->validate([
'cost_attachments.*' => 'file|mimes:jpg,png,pdf,xls,xlsx',
]);
if ($this->cost_attachments) {
foreach ($this->cost_attachments as $file) {
$filePath = $file->store('cost_attachments', 'public');
$this->uploaded_cost_attachments[] = [
'file' => $filePath,
'id' => null,
];
}
$this->emit('fileUploaded', $this->uploaded_cost_attachments);
$this->cost_attachments = [];
}
}
and in blade:
document.addEventListener('livewire:load', function() {
Livewire.on('fileUploaded', function(array) {
@this.set('uploaded_cost_attachments', array);
});
});
by doing this now the array uploaded_cost_attachments get updated and display the attachments.