Laravel 5.2 & Dropzone.js - Remove (Delete) uploaded images - javascript

In routes.php I have the following routes:
Route::post('dropzone', ['as' => 'dropzone.upload', 'uses' => 'AdminPhotoController#dropzoneUpload']);
Route::post('dropzone/delete', ['as' => 'dropzone.delete', 'uses' => 'AdminPhotoController#dropzoneDelete']);
In AdminPhotoController.php I did in the following way:
public function dropzoneUpload(Request $request)
{
if($request->ajax()) { // hmm, do I really need this?
if ($request->hasFile('file') && $request->file('file')->isValid()) {
$image_file = $request->file('file');
$image_name = time() . '_' . $image_file->getClientOriginalName();
$destinationPath = 'images/photos';
if ($image_file->move($destinationPath, $image_name)) {
$photo = new Photo(['file_name' => $image_name, 'title' => 'No title', 'description' => 'No description']);
\Auth::user()->photos()->save($photo);
return \Response::json('success', 200);
} else {
return \Response::json('error', 400);
}
}
}
}
Finally, here are my HTML & JS:
<div class="dropzone" id="dropzoneFileUpload">
</div>
<script type="text/javascript">
Dropzone.autoDiscover = false; // to disable the auto discover behaviour of Dropzone (sami ga definisemo ispod)
var myDropzone = new Dropzone("div#dropzoneFileUpload", {
url: "{{ route('dropzone.upload') }}",
headers: {
'X-CSRF-TOKEN': '{!! csrf_token() !!}'
}
});
</script>
And it works, uploading files works. But I would like to have the remove links for deleting uploaded images. I was reading the official documentation on http://www.dropzonejs.com/ but I still don't understand how to do it. I see that there are:
removedfile event - Called whenever a file is removed from the list. You can listen to this and delete the file from your server if you want to;
and .removeFile(file) method - If you want to remove an added file from the dropzone, you can call .removeFile(file). This method also triggers the removedfile event.
So I started like this but I don't know how to do it, how to delete those images:
Dropzone.options.dropzoneFileUpload = { // div has id=dropzoneFileUpload?
addRemoveLinks: true, // but still link to delete is not displayed?
dictRemoveFile: 'Remove'
};
myDropzone.on("complete", function(file) {
myDropzone.removeFile(file); // What should I do here?
});
.
EDIT:
If I remove this code:
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("#my-dropzone", {
url: "{{ route('dropzone.upload') }}",
headers: {
'X-CSRF-TOKEN': '{!! csrf_token() !!}'
}
});
solution that that #Manuel Azar gave will work, Remove Links are now displayed (for each uploaded image). So, there is some problem with this code, something is missing.

Take a look at this answer to help you understand the dropzone events:
https://stackoverflow.com/a/19454507/4734404
Then you should add an action to your controller for your delete request to remove the image from DB and disk:
public function dropzoneRemove(Request $request)
{
if($request->ajax()) {
$photo = Photo::find($request->photoId); //Get image by id or desired parameters
if(File::exists($destinationPath.$photo->file_name)) //Check if file exists
File::delete($destinationPath.$photo->file_name) //Delete file from storage
$photo->delete() //Delete file record from DB
return response('Photo deleted', 200); //return success
}
}
I would recommend you to take a look at laravel's Storage facade, to keep your files well organized in your filesystem.
https://laravel.com/docs/5.2/filesystem
EDIT:
How to add a button to remove each file preview?
Starting with Dropzone version 3.5.0, there is an option that will handle all this for you: addRemoveLinks. This will add an Remove file element to the file preview that will remove the file, and it will change to Cancel upload if the file is currently being uploaded (this will trigger a confirmation dialog).
You can change those sentences with the dictRemoveFile, dictCancelUpload and dictCancelUploadConfirmation options.
If you still want to create the button yourself, you can do so like this:
<form action="/target-url" id="my-dropzone" class="dropzone"></form>
<script>
// myDropzone is the configuration for the element that has an id attribute
// with the value my-dropzone (or myDropzone)
Dropzone.options.myDropzone = {
init: function() {
this.on("addedfile", function(file) {
// Create the remove button
var removeButton = Dropzone.createElement("<button>Remove file</button>");
// Capture the Dropzone instance as closure.
var _this = this;
// Listen to the click event
removeButton.addEventListener("click", function(e) {
// Make sure the button click doesn't submit the form:
e.preventDefault();
e.stopPropagation();
// Remove the file preview.
_this.removeFile(file);
// If you want to the delete the file on the server as well,
// you can do the AJAX request here.
});
// Add the button to the file preview element.
file.previewElement.appendChild(removeButton);
});
}
};
</script>
From FAQ: https://github.com/enyo/dropzone/wiki/FAQ#how-to-add-a-button-to-remove-each-file-preview
More info here about customizing dropzone properties: http://www.dropzonejs.com/#layout
EDIT 2
The problem is here:
Dropzone will find all form elements with the class dropzone, automatically attach itself to it, and upload files dropped into it to the specified action attribute. http://www.dropzonejs.com/#usage
Alternatively you can create dropzones programmaticaly (even on non form elements) by instantiating the Dropzone class http://www.dropzonejs.com/#create-dropzones-programmatically
Dropzone.autoDiscover = false; // to disable the auto discover behaviour of Dropzone (sami ga definisemo ispod)
var myDropzone = new Dropzone("div#dropzoneFileUpload", {
I believe you have two instances of Dropzone because you are creating another Dropzone object. You should stick to the quick config and edit the options directly, and remove autoDiscover = false, instead of doing it programatically.
if your dropzone element id is 'my-awesome-dropzone':
<form action="/file-upload"class="dropzone" id="my-awesome-dropzone"></form>
Dropzone automatically will create a property with the camelized id name 'myAwesomeDropzone' and attach it to the current object.
So you set the Dropzone options by doing:
//myAwesomeDropzone = camelized version of ID = my-awesome-dropzone
Dropzone.options.myAwesomeDropzone = {
addRemoveLinks: true
}
I've made this plunker with minimun setup for you, just add your request config like you did before and it should work, i set the addRemoveLinks to true so you can see that they are working:
https://plnkr.co/edit/9850jCOpTwjSSxI1wS1K?p=info

Related

Multiple Dropzone in a single page

I'm using Dropzone without creating a dropzone form. It works great for me in this way.
But in this case I can not create another instance of Dropzone in my page.
var myDropzone1 = new Dropzone(
document.body,
{
url : "upload1"...
.
.
. some parameters
};
var myDropzone2 = new Dropzone(
document.body,
{
url : "upload'"...
.
.
. some parameters
};
When I do this, I'm getting the error Dropzone already attached.
It's possible, but you can't bind a second dropdzone on the same element, as you did. 2 Dropzones on one element makes no sense. 2x document.body in your solution atm. Try this...
HTML:
<form action="/file-upload" class="dropzone" id="a-form-element"></form>
<form action="/file-upload" class="dropzone" id="an-other-form-element"></form>
JavaScript:
var myDropzoneTheFirst = new Dropzone(
//id of drop zone element 1
'#a-form-element', {
url : "uploadUrl/1"
}
);
var myDropzoneTheSecond = new Dropzone(
//id of drop zone element 2
'#an-other-form-element', {
url : "uploadUrl/2"
}
);
I want to add something here because I have experienced problems with multiple dropzones on the same page.
In your init code you must remember to include var if putting a reference otherwise it isn't dealing with this instance of the dropzone rather trying to access/relate to the others.
Simple javascript but makes a big difference.
init: function(){
var dzClosure = this;
$('#Btn').on('click',function(e) {
dzClosure.processQueue();
});
Note: I'm not using Ajax in order to upload the image on server. For Ajax, One can use url. Attaching the doc DropzoneDoc. It's works like action.( Something like that. Check my solution you can get some idea to do it in your case.
When I working we dropzone, I faced a similar issue working with the Meteor framework.
In the case of Meteor Framework, dropzone is initialized in the below code. This will look different to you, it's the way to initialize in the Meteor framework.
In your case, you can find similarities when using the dropzone library.
Params are added as
params='{name: "Image1"}'
{{> dropzone url=getDropZoneImageUploadURL id='candidate-identity-photo-proof'
init=initFunction params='{name: "Image1"}'
acceptedMimeTypes= 'image/jpeg,image/png,image/jpg' maxFiles=1
success=uploadSuccessHandler maxFilesize=2 dictDefaultMessage= "Photo
ID Proof Photo"
previewsContainer='#upload-photo-id-holder'
previewTemplate=previewTemplateString}}
when the file is getting uploaded check "this" context as mentioned below code. You can use the params value to distinguish the image.
var initFunction = function () {
this.on("addedfile", function () {
if (this.files[1] != null) {
this.removeFile(this.files[0]);
}
});
this.on("sending", function (file, xhr, formData) {
console.log(this); // Here you can get the value
formData.append("type", "image");
});
this.on("error", function (fileInfo, errorMessage) {
var message = "ERROR";
showNotification("error", { message: message }, {});
});
};

Open File Selection dojox/Uploader programmatically

I need to select a file to upload with dojox/Uploader by clicking on a div not related to the widget.
I've tried using on.emit, but nothing happens (also click(), onclick() etc... on domNodes..)
This is my code:
var myUploader = new dojox.form.Uploader({
id : 'myUploader',
url : baseUrl + '/upload/form',
style : {
'overflow': 'hidden',
'position': 'relative',
'opacity' : 0
}
},"uploaderHolder");
myUploader.startup();
var importButtonNode = dom.byId("importDivButton");
on(importButtonNode,"click",function(evt) {
on.emit(myUploader.domNode, "click", {
bubbles: true,
cancelable: false
});
The widget must be hidden, so I can't press widget select button. I need open select file dialog by click other div so... how can I open the file browser programmatically to select a file?
Well, I find out a solution. I take a widgets inner node to call click, and attach a listener to the Uploader change event and complete event...
First, attach click event to a node.
var importButtonNode = dom.byId("myImportDiv");
on(importButtonNode,"click",function(evt) {
myUploader.domNode.childNodes[0].click();
});
Attach to Uploader change and complete events a handler
myUploader.on("change",function(evt){
if(evt[0].type != FileTypes.XSLX_FILE_TYPE){
alert("Error file type must be XLSX");
} else {
var formData = new Object();
formData.idProject = project.id;
myUploader.upload(formData);
}
});
myUploader.on("complete",function(evt){
alert("File Uploaded");
// do things
});
In my case I need send formdata without a form... so use de upload method. Also the file must be XLSX.
I hope this helps.
Regards

Add existing image files in Dropzone

I am using Dropzonejs to add image upload functionality in a Form, as I have various other fields in form so I have set autoProcessQueue to false and Processing it on click on Submit button of Form as shown below.
Dropzone.options.portfolioForm = {
url: "/user/portfolio/save",
previewsContainer: ".dropzone-previews",
uploadMultiple: true,
parallelUploads: 8,
autoProcessQueue: false,
autoDiscover: false,
addRemoveLinks: true,
maxFiles: 8,
init: function() {
var myDropzone = this;
this.element.querySelector("button[type=submit]").addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
}
}
This works fine and allows me to process all images sent when form is submitted. However, I also want to be able to see images already uploaded by user when he edits the form again. So I went through following post from Dropzone Wiki.
https://github.com/enyo/dropzone/wiki/FAQ#how-to-show-files-already-stored-on-server
Which populates dropzone-preview area with existing images but it does not send existing images with form submit this time. I guess this is because theses images are not added in queue but If it's so then how can update on server side, In case an existing image is removed by user?
Also, what would be the better approach, add already added images in queue again or just send information of removed file?
I spent a bit of time trying to add images again, but after battling with it for a while I ended up just sending information about the deleted images back to the server.
When populating dropzone with existing images I attach the image's url to the mockFile object. In the removedfile event I add a hidden input to the form if the file that is being removed is a prepopulated image (determined by testing whether the file that is passed into the event has a url property). I have included the relevant code below:
Dropzone.options.imageDropzone = {
paramName: 'NewImages',
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
maxFiles: 100,
init: function () {
var myDropzone = this;
//Populate any existing thumbnails
if (thumbnailUrls) {
for (var i = 0; i < thumbnailUrls.length; i++) {
var mockFile = {
name: "myimage.jpg",
size: 12345,
type: 'image/jpeg',
status: Dropzone.ADDED,
url: thumbnailUrls[i]
};
// Call the default addedfile event handler
myDropzone.emit("addedfile", mockFile);
// And optionally show the thumbnail of the file:
myDropzone.emit("thumbnail", mockFile, thumbnailUrls[i]);
myDropzone.files.push(mockFile);
}
}
this.on("removedfile", function (file) {
// Only files that have been programmatically added should
// have a url property.
if (file.url && file.url.trim().length > 0) {
$("<input type='hidden'>").attr({
id: 'DeletedImageUrls',
name: 'DeletedImageUrls'
}).val(file.url).appendTo('#image-form');
}
});
}
});
Server code (asp mvc controller):
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ViewModel viewModel)
{
if (ModelState.IsValid)
{
foreach (var url in viewModel.DeletedImageUrls)
{
// Code to remove the image
}
foreach (var image in viewModel.NewImages)
{
// Code to add the image
}
}
}
I hope that helps.
To extend on Teppic's answer, I found you needed to emit the complete event to remove the progress bar on the preview.
var file = {
name: value.name,
size: value.size,
status: Dropzone.ADDED,
accepted: true
};
myDropzone.emit("addedfile", file);
myDropzone.emit("thumbnail", file, value.path);
myDropzone.emit("complete", file);
myDropzone.files.push(file);
just use myDropzone.addFile(file)
from dropzone source code https://github.com/enyo/dropzone/blob/4e20bd4c508179997b4b172eb66e927f9c0c8564/dist/dropzone.js#L978
There's an official FAQ for that here
I solved this using the dropzone's built in "displayExistingFile" method.
in your init: function.
Create the mockfile
let mockFile = { name: file.title, size: file.size, dataURL:};
Call the displayExistingFile function
this.displayExistingFile(mockFile, , null, 'anonymous')
Instead of 'null' you can place a callback to respond to the thumbnail load event.
The 'anonymous' is for the cross origin property.
Originally I was doing this to programmatically upload a pre-existing file:
myDropzone.emit("addedfile", imageFile);
myDropzone.emit("thumbnail", imageFile, imageUrl);
myDropzone.files.push(file);
However, referencing this Dropzone Github Issue I found an easier way to directly upload:
myDropzone.uploadFiles([imageFile])
Unfortunately there are no references to this uploadFiles method in the Dropzone Documentation, so I figured I'd share some knowledge with all you Dropzone users.
Hope this helps someone
If you have the URL of the file, you can add the file with addFile.
fetch("url")
.then(res => res.blob())
.then((currentBlob) => {
const generateFile = new File([currentBlob], "filename.jpg", {
type: currentBlob.type,
});
myDropzone.addFile(generateFile);
});
On click of upload files just clear the files from dropzone.
All Files can be cleared using removeAllFiles() or specific file also you can delete by using removeFile(fileName).

Dropzone event handling not giving any life sign

i'm using dropzone to build an interface to an online storage api, for uploading files. I've to upload multiple files and to make an ajax call everytime a file is uploaded so that an input field of the hidden form below get added. It's not a big deal for itself, i just have to add a call to the init property of dropzone (i'm not creating a custo dropzone, i'm just using the default one). So at line 1581 i wrote:
Dropzone.options = {
init: function() {
this.on("addedfile", function(file) { alert("added file"); });
}
};
but when i add i file to the dropzone nothing happens.
I'm processing multiple files, so am I calling the wrong event? maybe should i call successmultiple? this is the dropzone tutorial. Any idea?
if you have a form, with an id="my-awesome-dropzone" like in the example
<form action="/file-upload"
class="dropzone"
id="my-awesome-dropzone"></form>
You have to create a config object in the same document, i.e. in the header
<script src="./path/to/dropzone.js"></script>
<script >
//"myAwesomeDropzone" is the camelized version of the HTML element's ID
Dropzone.options.myAwesomeDropzone = {
init: function() {
this.on("addedfile", function(file) { alert("Added file."); });
}
};
</script>
If instead of this, you are creating the dropzone by
var myDropzone = new Dropzone("form.myFormClass"); //or something like this
you have to add the options as the second parameter
var myDropzone = new Dropzone("form.myFormClass", {
init: function() {
this.on("addedfile", function(file) { alert("Added file."); });
}
});

Multiple dropzone.js - single page

Rather than having multiple file uploads on a single dropzone element - is it possible to have multiple dropzone elements on a single page?
It seems dropzone isn't even triggering after the select dialog when there are multiple elements,each with their own dropzone initialized
The typical way of using dropzone is by creating a form element with the class dropzone:
<form action="/file-upload"
class="dropzone"
id="my-awesome-dropzone"></form>
That's it. Dropzone will find all form elements with the class dropzone, automatically attach itself to it, and upload files dropped into it to the specified action attribute. You can then access the dropzone element like so:
// "myAwesomeDropzone" is the camelized version of the HTML element's ID
Dropzone.options.myAwesomeDropzone = {
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 2, // MB
accept: function(file, done) {
if (file.name == "justinbieber.jpg") {
done("Naha, you don't.");
}
else { done(); }
}
};
As far as i know , you can create your own dropzone , then it's possible to have multiple dropzone elements.
// Dropzone class:
var myDropzone = new Dropzone("div#myId", { url: "/file/post"});
// jQuery
$("div#myId").dropzone({ url: "/file/post" });
Yes, you can have an unlimited amount of dropzones on a single page.
Example:
<form action="/controller/action">
<div class="dropzoner" id="dropzone1"></div>
<div class="dropzoner" id="dropzone2"></div>
</form>
<script>
Dropzone.autoDiscover = false; // Very important
InitializeDropzones();
function InitializeDropzones() { // You only need to encapsulate this in a function if you are going to recall it later after an ajax post.
Array.prototype.slice.call(document.querySelectorAll('.dropzoner'))
.forEach(element => {
if (element.dropzone) {
element.dropzone.destroy();
} // This is only necessary if you are going to use ajax to reload dropzones. Without this, you will get a "Dropzone already exists" error.
var myDropzone = new Dropzone(element, {
url: "/controller/action",
headers: {
"headerproperty": value,
"headerproperty2": value2
},
});
})
}
</script>
Some notes that might save someone time when dealing with multiple dropzones:
After reloading any elements via ajax that have a dropzone attached to them, you will need to reinitialize that dropzone onto the element.
For instance:
myDropzone.on("success", function (response) {
toastr[show a toastr];
$("#ContainerThatHasDropzones").load("/controller/action/" + id, function() {
Dropzone.discover(); // This is very important. The dropzones will not work without this after reloading via ajax.
InitializeDropzones();
});

Categories