© 2026 Hedgehog Software, LLC

TwitterGitHubDiscord
More
CommunitiesDocsAboutTermsPrivacy
Search
Star
Setup for Free
FilamentF
Filament•3y ago•
4 replies
Dreary Diana

live() deletes my cropper instance, custom field

I made a form field that incorporates Cropper.js to select part of an image and give back the coordinates to four other input form fields. This works perfectly so far, but after the very first update to the state, the Cropper instance seems to completely disappear... When removing live(), this doesn't happen, but obviously the other four form fields are no longer being updated.

Does anyone know what the issue is here? Thank you!

image 1: without live()
image 2: with live(), cropper instance dissappeared

Field:
 ImageCropper::make('coordinates')
->image(fn (CreateMapping $livewire) => Storage::disk('public')->url($livewire->getMapType()->preview))
->columnSpan(3)
->default('0, 0, 0, 0')
->afterStateUpdated(function(Set $set, string $state) 
  {
    [$x, $y, $width, $height] = explode(', ',$state);
    $set('x', $x);
    $set('y', $y);
    $set('width', $width);
    $set('height', $height);
  })
->live()
 ImageCropper::make('coordinates')
->image(fn (CreateMapping $livewire) => Storage::disk('public')->url($livewire->getMapType()->preview))
->columnSpan(3)
->default('0, 0, 0, 0')
->afterStateUpdated(function(Set $set, string $state) 
  {
    [$x, $y, $width, $height] = explode(', ',$state);
    $set('x', $x);
    $set('y', $y);
    $set('width', $width);
    $set('height', $height);
  })
->live()


Class:
<?php

namespace App\Forms\Components;

use Filament\Forms\Components\Field;
use Filament\Forms\Get;
use Illuminate\Support\Facades\Storage;

class ImageCropper extends Field
{
    protected string $view = 'forms.components.image-cropper';

    public $image;


    public function image(string|\Closure|null $image) :static
    {
        $this->image = $image;
        return $this;
    }

    public function getImageUrl(): ?string
    {
        if($result = $this->evaluate($this->image))
        {
            return $result;
        }
        return null;
    }
}
<?php

namespace App\Forms\Components;

use Filament\Forms\Components\Field;
use Filament\Forms\Get;
use Illuminate\Support\Facades\Storage;

class ImageCropper extends Field
{
    protected string $view = 'forms.components.image-cropper';

    public $image;


    public function image(string|\Closure|null $image) :static
    {
        $this->image = $image;
        return $this;
    }

    public function getImageUrl(): ?string
    {
        if($result = $this->evaluate($this->image))
        {
            return $result;
        }
        return null;
    }
}


Blade:
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">
    <div ax-load
        ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('image-cropper-cropperjs') }}"
        x-data="imageCropper({
        state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }},
        })">
        <!-- Interact with the `state` property in Alpine.js -->

        <input x-ref="arrayState" class="text-black" x-model='state'/>

        <img id="image" src="{{ $getImageUrl() }}" alt="Picture">

    </div>
</x-dynamic-component>
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">
    <div ax-load
        ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('image-cropper-cropperjs') }}"
        x-data="imageCropper({
        state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$getStatePath()}')") }},
        })">
        <!-- Interact with the `state` property in Alpine.js -->

        <input x-ref="arrayState" class="text-black" x-model='state'/>

        <img id="image" src="{{ $getImageUrl() }}" alt="Picture">

    </div>
</x-dynamic-component>


Script:
import Cropper from 'cropperjs';

export default function imageCropper({
    state,
}) {
    let component = null;
    let cropper = null;
    let image = null;
    let canvas = null;
    let selection = null;
    return {
        state,

        init: function () {
            component = this;
            cropper = new Cropper('#image');
            canvas = cropper.getCropperCanvas();
            image = cropper.getCropperImage();
            selection = cropper.getCropperSelection();
            selection.zoomable = false;
            image.scalable = false;


            canvas.classList.add("aspect-square");

            // Function to handle Cropper.js event
            function handleSelectionChange(event) {
                component.state = selection.x + ', ' + selection.y + ', ' + selection.width + ', ' + selection.height;
            }

            selection.addEventListener('change', handleSelectionChange);

            console.log(state);
            console.log(cropper);
            console.log(canvas);
            console.log(selection);
            console.log('done!');

        },
    }
}
import Cropper from 'cropperjs';

export default function imageCropper({
    state,
}) {
    let component = null;
    let cropper = null;
    let image = null;
    let canvas = null;
    let selection = null;
    return {
        state,

        init: function () {
            component = this;
            cropper = new Cropper('#image');
            canvas = cropper.getCropperCanvas();
            image = cropper.getCropperImage();
            selection = cropper.getCropperSelection();
            selection.zoomable = false;
            image.scalable = false;


            canvas.classList.add("aspect-square");

            // Function to handle Cropper.js event
            function handleSelectionChange(event) {
                component.state = selection.x + ', ' + selection.y + ', ' + selection.width + ', ' + selection.height;
            }

            selection.addEventListener('change', handleSelectionChange);

            console.log(state);
            console.log(cropper);
            console.log(canvas);
            console.log(selection);
            console.log('done!');

        },
    }
}
image.png
image.png
Solution
Okay, adding
wire:key="{{rand()}}"
wire:key="{{rand()}}"
to the x-dynamic-component seems to work in reinitializing the script! Now I just need to set it to the correct position 😄
Jump to solution
Filament banner
FilamentJoin
A powerful open source UI framework for Laravel • Build and ship admin panels & apps fast with Livewire
20,307Members
Resources
Was this page helpful?

Similar Threads

Recent Announcements

Similar Threads

custom field trigger live
FilamentFFilament / ❓┊help
3y ago
how to live update custom form field
FilamentFFilament / ❓┊help
14mo ago
custom field live(onBlur: true) firing immediately
FilamentFFilament / ❓┊help
2y ago
Custom live field not updated in a custom action
FilamentFFilament / ❓┊help
13mo ago