view the generated source in the browser, check things like ids are unique
Multiple Instances of the same component in a page
Hello, I'm trying to create a livewire component in order to be able to crop and upload images. If I put mutliples instaces of the same component in my page, whenever I'm uploading an image, only the first instance in the page gets updated.
The livewire component: Cropper.php
<?php
namespace App\Http\Livewire;
use App\Models\Image;
use Livewire\Component;
class Croppie extends Component
{
public $width, $height, $viewmode, $post_to, $data, $image_id, $url, $item_id, $debug;
public function mount($width, $height, $item_id, $viewmode = 3, $post_to = 'crop-image-upload')
{
$this->viewmode = $viewmode;
$this->post_to = $post_to;
$this->item_id = $item_id;
$this->image_id = $item_id;
$this->debug = 'mounted' . $item_id;
}
public function save_image()
{
dd($this->item_id);
$this->debug = 'save_img ' . $this->item_id;
$folderPath = public_path('upload/');
$image_parts = explode(";base64,", $this->data);
$image_type_aux = explode("image/", $image_parts[0]);
$image_type = $image_type_aux[1];
$image_base64 = base64_decode($image_parts[1]);
$imageName = uniqid() . '.png';
$imageFullPath = $folderPath . $imageName;
file_put_contents($imageFullPath, $image_base64);
$saveFile = new Image;
$saveFile->name = $imageName;
$saveFile->save();
$this->image_id = $saveFile->id;
}
public function render()
{
return view('livewire.croppie');
}
}
The livewire component view: Cropper.blade.php
<div>
<div x-data="cropperData({ item_id: @js($item_id), width: @js($width), height: @js($height), viewmode: @js($viewmode) })" wire:key="{{$item_id}}">
<div class="croppie" x-cloak>
<div class="container text-left">
<meta name="_token" content="{{ csrf_token() }}">
<input type="file" name="image-{{$item_id}}" class="image" x-on:change.debounce="file_changed($event)"/>
<input type="text" name="" wire:model="image_id"/>
</div>
<div id="modal-{{$item_id}}" x-show="open" x-data="{ open: false }">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
<div class="fixed z-0 inset-0 overflow-y-auto">
<div class="flex items-end sm:items-center justify-center min-h-full text-center sm:p-0">
<div class="relative bg-white text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-lg sm:w-full">
<div class="img-container">
<div class="row">
<div class="col-md-8">
<img id="image-{{$item_id}}" src="">
</div>
<div class="col-md-4">
<div class="preview"></div>
</div>
<button type="button" class="btn btn-red" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-blue" x-on:click="crop_image">Crop</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{--styles--}}
<style>
[x-cloak] {
display: none !important;
}
img {
max-width: 100%;
}
.preview {
overflow: hidden;
width: @js($width)px;
height: @js($height)px;
border: 1px solid red;
}
</style>
</div>
</div>
@pushonce('scripts')
<script>
function cropperData(options) {
return {
cropper: null,
item_id: options.item_id,
width: options.width,
height: options.height,
viewmode: options.viewmode,
image_name: 'image-' + options.item_id,
modal_name: 'modal-' + options.item_id,
crop_name: 'crop-' + options.item_id,
get image() {
return document.getElementById(this.image_name);
},
get modal() {
return $('#' + this.modal_name)
},
get crop() {
return $('#' + this.crop_name)
},
done(url) {
this.image.src = url;
console.log(this.image);
this.activateCropper();
console.log('activated?');
},
activateCropper() {
console.log(this);
this.modal.show();
this.cropper = new Cropper(this.image, {
aspectRatio: this.width / this.height,
viewMode: this.viewmode,
preview: '.preview'
});
console.log(this.cropper);
},
deActivateCropper() {
this.cropper.destroy();
this.cropper = null;
this.modal.hide();
},
crop_image() {
let that = this;
let canvas = this.cropper.getCroppedCanvas({
width: this.width,
height: this.height,
});
canvas.toBlob(function (blob) {
let url = URL.createObjectURL(blob);
let reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function () {
let base64data = reader.result;
console.log(base64data);
that.deActivateCropper();
@this.debug = this.item_id;
@this.data = base64data;
@this.save_image();
}
});
},
file_changed(e) {
console.log('changed');
let files = e.target.files;
let reader, file, url;
if (files && files.length > 0) {
file = files[0];
if (URL) {
this.done(URL.createObjectURL(file));
} else if (FileReader) {
reader = new FileReader();
reader.onload = function (e) {
done(reader.result);
};
reader.readAsDataURL(file);
}
}
}
}
}
</script>
@endpushonce
And I call them like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Cropper</title>
<link rel="stylesheet" href="{{asset('css/app.css')}}">
@livewireStyles
</head>
<body class="antialiased">
<div class="container mx-auto mt-8">
<div>
<h2>This is a test</h2>
<div class="">
<livewire:croppie :width="300" :height="200" :item_id="1" :post_to="'save_img'" wire:key="ante" wire:id="222" />
</div>
-----
<div class="">
<livewire:croppie :width="300" :height="200" :item_id="2" :post_to="'save_img'" wire:key="nadoum" wire:id="12"/>
</div>
-----
</div>
</div>
<script src="{{asset('js/app.js')}}" defer></script>
@livewireScripts
@stack('scripts')
</body>
</html>
I'm calling the save_image function from js like this: @this.save_image(); I'm reading in the documentation that the @this directive compiles to the following string for JavaScript to interpret: "Livewire.find([component-id])", but I cannot find a way to set a compoent id or event get the component id from a livewire component.
Does anyone know what is happening?
Please or to participate in this conversation.