I have a Filepond component that works great for uploading images to a post.
However, I'm struggling with Filepond when needing to edit a post and its pre-existing images.
I want to load Filepond with the Post model's pre-existing images. The goal is to allow the user to upload, delete and reorder the images when editing the Post model, then update the database and the file system.
This is what I have so far:
<div
x-data=""
x-init="
// Plugins
FilePond.registerPlugin(FilePondPluginImagePreview);
FilePond.registerPlugin(FilePondPluginImageExifOrientation);
FilePond.registerPlugin(FilePondPluginFileValidateType);
FilePond.registerPlugin(FilePondPluginFileValidateSize);
FilePond.registerPlugin(FilePondPluginImageResize);
// Set options
FilePond.setOptions({
allowMultiple: true,
allowReorder: true,
itemInsertLocation: 'after',
server: {
process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
@this.upload('images', file, load, error, progress)
},
revert: (filename, load) => {
@this.removeUpload('images', filename, load)
},
},
// This allows me to reorder the images, essential resetting the $image variable in livewire component when ever th euser reorders the images in filepond
onreorderfiles(files, origin, target){
@this.set('images', null);
files.forEach(function(file) {
@this.upload('images', file.file);
});
},
});
// Create Filepond
const pond = FilePond.create($refs.input, {
acceptedFileTypes: ['image/png', 'image/jpeg'],
@if(optional($post)->images) // If we are editing a post and if that post has images
files: [
@foreach($post->images as $image)// Loop through each image for the post
{
// the server file reference
source: '{{ Storage::disk('post_images')->url($post->id . '/' . $image->filename.$image->extension) }}',
},
@endforeach
],
@endif
});
pond.on('addfile', (error, file) => {
if (error) {
console.log('Oh no');
return;
}
});
"
>
<div wire:ignore wire:key="images">
<input
id="image-upload"
type="file"
x-ref="input"
multiple
>
@error('images.*')
<p wire:key="error_images" class="mt-2 text-sm text-red-600" id="email-error">{{ $message }}</p>
@enderror
</div>
</div>
However this is causing all of the images to re-upload every time the user views the edit page.
There is a lot online on how to upload images using Filepond and Livewire, but not really much on how to edit.
Is there a better way to manage images, other than reuploading every time the user views the edit page?
I was able to handle pre-existing images on a model, specifically I was able to re-order the images.
For us to handle re-ordering of images we need a unique identifier coming from the server and with livewire's upload method it was handled for us magically.
@this.upload('images', file, load, error, progress)
This is great and is working with both processing or reverting uploads but breaks when we try to listen for filepond's onreorderfile
event. Filepond knows how to handle re-ordering on the DOM level but is missing an important information, which is the unique indentifier coming from the server
onreorderfiles(files, origin, target) {
console.log(file.serverId); // this will be null
}
Why is that? The first time we added our images either through the filepond's files array or addFile method we processed the images using livewire's @this.upload('images', file, load, error, progress)
. The load callback here is what returns the serverId
or the unique identifier we need so that Filepond can communicate it with the backend. The load callback from filepond is available on the server process object but not in onreorderfiles
event.
This isn't quite right but we do have a clue where to find the serverId. Diving the source code for livewire's livewire\livewire\js\UploadManager.js
we will find the upload method and it does return the tmpFilename:
this.component.on('upload:finished', (name, tmpFilenames) => this.markUploadFinished(name, tmpFilenames))
The issue is clear because onreorderfiles event we aren't specifying any callbacks to filepond's load() method and when the script reaches the server process object load
is undefined:
onreorderfiles(files, origin, target){
// omitted code
@this.upload('images', file.file);
// omitted code
}
The solution is to explicitly return a success callback no matter what.
Change the following code block:
server: {
process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
@this.upload('images', file, load, error, progress)
},
to this, remember that livewire upload method returns unique filename no matter what but we need to pass it to filepond and load method accepts a response as a parameter:
server: {
process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
@this.upload('images', file,
// this is the secret method people have been looking for
(uploadedFilename) => {
load(uploadedFilename);
}, error, progress)
},
Well, it is not secret per se since it is documented at Livewire file uploads.