How to configure Filepond for edit blade?

I’m trying to configure Filepond with Livewire test project.

Creating blade works fine, Filepond uploads image successfully, but I can’t achieve Filepond working properly in edit blade.

Here’s what I’ve got (with this code images re-uploads every time once I’m in edit blade):

<div

  wire:ignore

  x-data

  x-init="

      FilePond.registerPlugin(FilePondPluginImagePreview);

      FilePond.create($refs.image,{        

        server: {

            process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {

                @this.upload('image', file, load, error, progress)

            },

            revert: (filename, load) => {

                @this.removeUpload('image', filename, load)

            },

        },

        files: [

            {

                source: '/uploads/{{ $image }}',

            }

        ]

    });

  "

>

  <input wire:model="image" type="file" x-ref="image">

</div>

Hello! Could you find a solution?
I have the same problem…

with this code images re-uploads every time once I’m in edit blade

Not entirely sure what you mean by this, possibly that it throws an error when you are editing but there is no file?

I had a little play after watching the FilePond example in the Livewire screencasts. This is what I came up with and seems to work, I used the same example as Caleb, where a User has an avatar but the principle is the same for whatever your use case might be.

Created myself an Uploader component using php artisan make:livewire Uploader.

I have added comments to everything that requires some explination.

app/Http/Livewire/Uploader.php

<?php

namespace App\Http\Livewire;

use App\Models\User;
use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;

class Uploader extends Component
{
    use WithFileUploads;

    /**
     * The full path to the uploaded avatar
     */
    public $avatar;

    /**
     * Variable to hold the temporary file whilst uploading
     */
    public $tmp;

    /**
     * Just setting up some stuff here
     *
     * @return void
     */
    public function mount()
    {
        // I am not using any auth scaffolding so, force a manual user login
        Auth::login(User::find(1));
        // Display the current avatar for the user
        $this->avatar = $this->avatarUrl();
    }

    /**
     * Save some stuff
     *
     * @return void
     */
    public function save()
    {
        // This is just a dummy holder for some data
        $attributes = [];

        // If a file has been uploaded in the FilePond input
        if (isset($this->tmp)) {
            // Upload it to the storage location
            $filename = $this->tmp->store('/', 'uploads');
            // Add the new avatar filename to the avatar user attribute
            $attributes['avatar'] = $filename;
        }

        // If updating the user was successful
        if (auth()->user()->update($attributes)) {
            // Get the new avatar for the user
            $this->avatar = $this->avatarUrl();
            // Clear the FilePond input
            $this->dispatchBrowserEvent('pondReset');
            // Fire an event to show success message
            $this->emitSelf('notify-saved');
        }
    }

    /**
     * If the user has an avatar defined, get if from storage,
     * otherwise use a gravatar
     *
     * @return void
     */
    public function avatarUrl()
    {
        return auth()->user()->avatar ? 
            Storage::disk('uploads')->url(auth()->user()->avatar) :
            'https://gravatar.com/avatar/' . md5(strtolower(trim(auth()->user()->email)));
    }

    public function render()
    {
        return view('livewire.uploader');
    }
}

resources/views/livewire/uploader.blade.php

<div>
    <div>
        @if ($tmp)
        {{-- Display the tmp image as a preview --}}
        <img src="{{ $tmp->temporaryUrl() }}" alt="Avatar">
        @else
        {{-- Display the user avatar --}}
        <img src="{{ $avatar }}" alt="Avatar">
        @endif
    </div>

    <form wire:submit.prevent="save">
        {{-- without image preview --}}
        <x-input.filepond wire:model="tmp" />

        {{-- with image preview --}}
        {{-- <x-input.filepond wire:model="tmp" image-preview /> --}}

        <span x-data="{ open: false }" x-init="
                @this.on('notify-saved', () => { if (open === false) setTimeout(() => { open = false }, 2500); open = true})
            " x-show.transition.out.duration.1000ms="open" style="display: none">
            {{ __('Saved') }}
        </span>

        <button type="submit">
            {{ __('Save') }}
        </button>
    </form>
</div>

resources/views/components/input/filepond.blade.php

@props([
    // this is so you can turn the image preview on and off
    'imagePreview' => false,
])

<div x-data="{}" 
    x-init="
        FilePond.setOptions({
            server: {
                process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
                    @this.upload('{{ $attributes["wire:model"] }}', file, load, error, progress)
                },
                revert: (uniqueFileId, load, error) => {
                    @this.removeUpload('{{ $attributes["wire:model"] }}', uniqueFileId, load)
                },
            },
        });
        @if($imagePreview)
        FilePond.registerPlugin(FilePondPluginImagePreview);
        @endif
        var Pond = FilePond.create($refs.input);
        // add an event listener to clear the FilePond input
        // once the upload has been saved
        this.addEventListener('pondReset', e => {
            Pond.removeFiles();
        });
        " 
    wire:ignore>

    <input type="file" x-ref="input" />
</div>

I just used local storage to get this example working, defined a custom disk in
config/filesystems.php:

'disks' => [

    'uploads' => [
        'driver' => 'local',
        'root' => storage_path('app/uploads'),
        'url' => env('APP_URL').'/uploads',
        'visibility' => 'public',
    ]

]

Add a symlink

    'links' => [
        public_path('storage') => storage_path('app/public'),
        public_path('uploads') => storage_path('app/uploads'),
    ],

Then run php artisan storage:link.

Regarding your code.
Why do you use $tmp variable? You can access a temporary uploaded image with ->temporaryUrl(). And you do so.
Should I use imgUrl, like avatarUrl? I have read about gravatar. It doesn’t suit ordinary image for posts. There is no reason to make imgUrl?
Regarding attributes and props. I tried to add $attributes and props, laravel gave me an error, that properties weren’t passed to a component. I saw attributes and props in Caleb’s videos, honestly, I can’t understand, if these variables are necessary. That’s why I was trying to make his course from the beginning. I corrected some issues, which didn’t work for my configuration of laravel and livewire, cuz I knew how to do it. Then met his way of registration, I wrote myself, then copied his code. It wasn’t work! I always use fortify to register/login users.

This is my first code here.

My component looks the same way now. If I use $filename to store the name of the image. I got a very strange path to the image in the db, like /private/var/tmp/phpsLUXa6. That’s why I hash the name in updateOrCreate method:

‘img’ => $this->img->hashName(),

Then, @if… does’t work for me, cuz i get tan error, when I add a new img creating a new post, and, when I edit the post. I know @if… should work(

Call to a member function temporaryUrl() on string (View: /Applications/MAMP/htdocs/lemma-auto/resources/views/admin/posts/create.blade.php)

Here is my blade view of the form to create/edit. The place regarding image:

<div class="mb-4" wire:ignore>
                            <label for="img" class="block text-gray-700 text-sm font-bold mb-2">
                                Фото:
                            </label>
{{--                            @if($img)--}}
{{--                                <img src="{{ $img->temporaryUrl() }}">--}}
{{--                            @else--}}
{{--                                <img src="{{ \Illuminate\Support\Facades\Storage::url('public/docs/' . $img) }}">--}}

{{--                            @endif--}}
                            <div
                                x-data
                                x-init="
                                    FilePond.registerPlugin(FilePondPluginImagePreview);
                                    FilePond.setOptions({
                                        server: {
                                            process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
                                                @this.upload('{{ 'img' }}', file, load, error, progress)
                                            },
                                            revert: (filename, load) => {
                                                @this.removeUpload('{{ 'img' }}', filename, load)
                                            },
                                        },
                                        files: [{
                                            source: 'public/docs/{{ $img }}',
                                            options:{
                                                type: 'local'
                                            }
                                        }]
                                    });
                                        FilePond.create($refs.input);
                                    "
                                >
                                    <input type="file" wire:model="img" class="filepond"  x-ref="input" required>

                                </div>
                            @error('img') <span class="text-red-500">{{ $message }}</span>@enderror
                        </div>

It worked fine, expect the edit method.

Oh I forgot. This is the video, how my code works.


If the quality of the video will be too bad, you can change this parameter in the settings is right corner in the bottom of the video, like on YouTube

I would like to get an image in a filepond field. That’s why I configure filepond here:

files: [{
source: ‘public/docs/{{ $img }}’,
options:{
type: ‘local’
}
}]

I can access image in the edit form so:

<img src="{{ \Illuminate\Support\Facades\Storage::url('public/docs/') . $post->img }}" />

As I said before, then I got an error, when I add a new image.
Sorry for so much code. It turned out this was a very difficult question.

Why do you use $tmp variable?

The use of a $tmp variable was a design choice. It was a simple and convenient way of keeping track of the existing image URL and the temporary image. It’s not required.

Should I use imgUrl, like avatarUrl?

Yes, you can use imgUrl() to obtain your image rather than avatarUrl(). In your imgUrl() function you can return the image path and then use some conditional to show the image if an image was returned.

Something along the lines of

public function imgUrl()
{
    return $this->img;
}

@if ($img)
<img src="Storage::disk('local')->url($img)" alt="post image">
@endif

Regarding attributes and props. I tried to add $attributes and props, laravel gave me an error, that properties weren’t passed to a component.

You only need to define custom key value pairs in the @props([]) directive. Take a look a the Blade Components Cookbook screencasts on Laracasts.

If I use $filename to store the name of the image. I got a very strange path to the image in the db, like /private/var/tmp/phpsLUXa6

That is to be expected, the filename will be a hash of the image.

Call to a member function temporaryUrl() on string (View: /Applications/MAMP/htdocs/lemma-auto/resources/views/admin/posts/create.blade.php)

This is because when you first load the component, your $img variable is a string, the path to the image previously uploaded. Once you upload a file it becomes a File Upload object.

Change your @if($img) to something like @if(method_exists($img, 'temporaryUrl')). There is possibly a nicer way of doing it but that works for me.

I see what you are trying to do, you want to load the image back into the FilePond input when you are editing a Post.

I had a tinker and came up with the following working example which will hopefully get you in the right direction. I also simplified it a little removing the $tmp variable and some other stuff.

<?php

namespace App\Http\Livewire;

use App\Models\User;
use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;

class Uploader extends Component
{
    use WithFileUploads;

    /**
     * The full path to the uploaded avatar
     */
    public $avatar;

    /**
     * Just setting up some stuff here
     *
     * @return void
     */
    public function mount()
    {
        // I am not using any auth scaffolding so, force a manual user login
        Auth::login(User::find(1));
        // Display the current avatar for the user
        $this->avatar = $this->avatarUrl();
    }

    /**
     * Save some stuff
     *
     * @return void
     */
    public function save()
    {
        // Add the new avatar filename to the avatar user attribute
        $attributes['avatar'] = $this->avatar->store('/', 'uploads');;

        // If updating the user was successful
        if (auth()->user()->update($attributes)) {
            // Get the new avatar for the user
            $this->avatar = $this->avatarUrl();

            // Clear the FilePond input after submission,
            // Uncomment this if you want to clear the FilePond input after upload
            // $this->dispatchBrowserEvent('pondReset');
            
            // Fire an event to show success message
            $this->emitSelf('notify-saved');
        }
    }

    /**
     * If the user has an avatar defined, get if from storage,
     * otherwise use a gravatar.
     *
     * @return void
     */
    public function avatarUrl()
    {
        return auth()->user()->avatar ? 
            Storage::disk('uploads')->url(auth()->user()->avatar) :
            'https://gravatar.com/avatar/' . md5(strtolower(trim(auth()->user()->email)));
    }

    public function render()
    {
        return view('livewire.uploader');
    }
}
<div>
    <div>
        @if (method_exists($avatar ,'temporaryUrl'))
        <img src="{{ $avatar->temporaryUrl() }}" alt="Avatar">
        @else
        <img src="{{ $avatar }}" alt="Avatar">
        @endif
    </div>

    <form wire:submit.prevent="save">
        <x-input.filepond wire:model="avatar" :avatar="$avatar" image-preview />

        <span x-data="{ open: false }" x-init="
                @this.on('notify-saved', () => { if (open === false) setTimeout(() => { open = false }, 2500); open = true})
            " x-show.transition.out.duration.1000ms="open" style="display: none">
            {{ __('Saved') }}
        </span>

        <button type="submit">
            {{ __('Save') }}
        </button>
    </form>
</div>
@props([
    'avatar' => '',
    // this is so you can turn the image preview on and off
    'imagePreview' => false,
])

<div x-data="{}" 
    x-init="
        FilePond.setOptions({
            server: {
                process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
                    @this.upload('{{ $attributes["wire:model"] }}', file, load, error, progress)
                },
                revert: (uniqueFileId, load, error) => {
                    @this.removeUpload('{{ $attributes["wire:model"] }}', uniqueFileId, load)
                },
                load: (source, load, error, progress, abort, headers) => {
                    var request = new Request(source);
                    fetch(request).then(function(response) {
                      response.blob().then(function(myBlob) {
                        load(myBlob)
                      });
                    });         
                },
            },
        });
        @if($imagePreview)
        FilePond.registerPlugin(FilePondPluginImagePreview);
        @endif
        var Pond = FilePond.create($refs.input, {
            files: [
                {
                    source: '{{ $avatar }}',
                    options: {
                        type: 'local',
                    },
                },
            ],
        });
        this.addEventListener('pondReset', e => {
            Pond.removeFiles();
        });
        " 
    wire:ignore>

    <input type="file" x-ref="input" />
</div>

I can get this! It’s already something in my edit form, thank you😀
I think, I didn’t return a path to the image, that’s why a hash name is shown in the filepond input. Unfortunately, Idk how to add a new function imgUrl

This work only to edit the post. So I can get a preview.

@if (method_exists($img ,'temporaryUrl'))
    <img src="{{ $img->temporaryUrl() }}">
@else
    <img src="{{ \Illuminate\Support\Facades\Storage::url('public/docs/' . $img) }}">
@endif

If I press the button to create a new post, I get this:


You see an image is uploading and uploading.
I have an idea how to avoid this. I can make two separate forms to create and update a post. But later, first I have to make filepond work as I wont it to.

I need an image into the filepond input. If I can get an image in the filepond input, that will be great. I think, the matter is in the configuration of the filepond… Here is what I could take from you:

>     FilePond.registerPlugin(FilePondPluginImagePreview);
>         FilePond.setOptions({
>             server: {
>                 process: (fieldName, file, metadata, load, error, progress, abort, transfer, options) => {
>                     @this.upload('{{ 'img' }}', file, load, error, progress)
>                 },
>                 revert: (filename, load) => {
>                     @this.removeUpload('{{ 'img' }}', filename, load)
>                 },
>                 load: (source, load, error, progress, abort, headers) => {
>                     var request = new Request(source);
>                     fetch(request).then(function(response) {
>                       response.blob().then(function(myBlob) {
>                         load(myBlob)
>                       });
>                     });
>                 },
>             },
>         });
>             var Pond = FilePond.create($refs.input, {
>                 files: [
>                     {
>                         source: '{{ $img }}',
>                         options: {
>                             type: 'local',
>                         },
>                     },
>                 ],
>             });
>         "

I am trying now to add imgUrl function. It is not a success. My livewire component is still the same.

In my head, it looks like filepond takes a name of the image from db instead of the path of the path to it. that’s why you have avatarUrl, yes?
I tried to write source: ‘public/docs/{{ $img }}’ or source: {{ \Illuminate\Support\Facades\Storage::url(‘public/docs/’) . $img }} in filepond files, it didn’t work.

Unfortunately, Idk how to add a new function imgUrl

On your Post model, add something like:

public function imgUrl()
{
    return $this->image ? Storage::url('public/docs/' . $this->img) : '';
}

Then you can use that in your component:

$post->imgUrl();

Alter the condition that shows the image in your blade file:

@if (method_exists($img ,'temporaryUrl'))
    <img src="{{ $img->temporaryUrl() }}">
@elseif ($post->imgUrl())
    <img src="{{ $post->imgUrl() }}">
@endif

That will show the temporary image when it has been uploaded, otherwise it will show an image if the $post->imgUrl() return value is truthy.

You will also want to update the files property on the FilePond create method so that if an image does not exist, it doesn’t attempt to load anything:

var Pond = FilePond.create($refs.input, {
    @if ($img)
    files: [
        {
            source: '{{ $img }}',
            options: {
                type: 'local',
            },
        },
    ],
    @endif
});

If you’re using a component for the FilePond input, you’ll want to pass $img as a prop:

<x-filepond :img="$post->imgUrl()" />

If it is not a component, just use $post->imgUrl() instead of $img.

You know today is a great day! I have bought another courses about livewire. @skywalker adviced me. Thank you, friend. I tried to follow them, but I couldn’t( damn again. But I have found those courses very good, I just didn’t notice something, cuz the downloaded code of the teacher works good. I will definitely return to them later, as to the official course of Caleb.

@Veleous, thank you, you have made me think. Finally I returned to my ordinary laravel controllers. And that’t it. Look!

I will return to our talking later, cuz I have to have some flexible solutions. Now I wanna have some rest. It’s 1am here. Guys, it was the hardest task on laravel. I know, there is some work with styles of images ahead. For today I am done)

I can share my code, but Idk, it is not about livewire

1 Like