How to reinitialize a dropzone element - javascript

I want to use dropzone to display thumbnails of images that I already have on the server. I am creating a CMS for a property website. The server has images associated with Sites of properties. When the page loads, the init function of my dropzone displays the relevant image for a given site as a thumbnail in the dropzone.
There is a drop down box on the page which lets the user select another site. When they do this I want the dropbox object to basically do what's in the init function again. Make a call to the server and display the thumbnails that are associated with this different site.
I haven't been able to get a solution yet (obviously). Typically I will get something like, "dropzone still attached" with stuff I have tried. I can't seem to see anything in the docs that is useful.
Any help would be appreciated :)
//my dropzone object
var myDropzone = $('#featured-development-dropzone').dropzone({
url: '#Url.Action("SaveFeaturedDevelopmentImage","Homepage")',
maxFiles: 1,
addRemoveLinks: true,
init: function () {
var myDropzone = this;
$("select").on('click', function () {
myDropzone.removeAllFiles(true);
});
var siteID = $('#siteDropdown').find(':selected').val();
$.ajax({
url: '#Url.Action("GetFeaturedDevelopmentImage", "Homepage")',
data: { siteID: siteID },
type: 'GET',
success: function (data) {
console.log(data);
if (data.data != null) {
var mockFile = {
name: data.filename,
size: data.fileSize
};
// Call the default addedfile event handler
myDropzone.emit("addedfile", mockFile);
console.log(typeof (data));
// And optionally show the thumbnail of the file:
myDropzone.emit("thumbnail", mockFile, "data:image/png;base64," + _arrayBufferToBase64(data.data));
myDropzone.emit("complete", mockFile);
}
}
});
this.on("sending", function (file, xhr, formData) {
formData.append("SiteID", siteID);
formData.append("imageTypeID", 4);
console.log(formData);
});
}
});
var prevSiteID;
$("select").on('click', function () {
prevSiteID = this.value;
}).change(function () {
var newSIteID = this.value;
// potentially put code here that will reinitialize the dropbox and display images associated with different site.
console.log(prevSiteID);
console.log(newSIteID);
changeFeaturedDevelopment(prevSiteID, newSIteID);

I belive you need to recall that function again. Since when you are triying to attach a new image, the dropzone zone is already defined in the page load. Or you call the init again or try to reset it, so the dropzone can reattach another image.

Related

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

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

Dropzone JS Global Events

I might have this wrong but Dropzone JS seems to do things in a very odd way. http://www.dropzonejs.com/#events
This is the recommended method of adding an event
Dropzone.options.myAwesomeDropzone = {
init: function() {
this.on("addedfile", function(file) { alert("Added file."); });
}
};
This assumes you know what myAwesomeDropzone is. But if you create them programatically then you may not know what myAwesomeDropzone is. It could be anything, e.g. if you have three different Dropzones on the page based on some ID then the identifier won't be myAwesomeDropzone or even guessable.
It would be handy to be able to do
$(".dropzone").on("addedfile", function(file) {
alert("hello");
});
But it does not work. I just want to be able to attach a global event to all my dropzones.
If you have several dropzones in the same page, and you need for each one to have a different configuration, you have to initialize each one separately.
As I see you are using jQuery know that you can also initialize the dropzone elements using the Dropzone jQuery plugin already included in dropzone.
As example imagine that each dropzone accepts a different file type:
Dropzone.autoDiscover = false;
$("#dropzone1").dropzone({
url: 'urlForDropzone1.php',
acceptedFiles: 'application/pdf',
init: function(){
this.on("addedfile", function(file) {
alert("Added" + file.name + " on Dropzone 1.");
}),
this.on("success", function(file) {
alert(file.name " Uploaded from Dropzone 1")
})
}
});
$("#dropzone2").dropzone({
url: 'urlForDropzone2.php',
acceptedFiles: 'image/*,.jpeg,.jpg,.png,.gif',
init: function(){
this.on("addedfile", function(file) {
alert("Added" + file.name + " on Dropzone 2.");
}),
this.on("success", function(file) {
alert(file.name " Uploaded from Dropzone 2")
})
}
});
Notice that first you need to disable the autodiscover feature, then initialize each dropzone separately, the url are optional, don't need to include them if you already have them in the html.
You can append dropzone very similar as in your 2nd snippet. When you attach the configuration directly when initializing the dropzone.
new Dropzone(".dropzone", {
url: 'someUrl',
addedfile: function () {
alert('Hallo');
}
});
The below works, my issue was that I had dropzone.js included twice which made Dropzone.autoDiscover = false; be ignored.
Dropzone.autoDiscover = false;
$(".dropzone").on("addedfile", function(file) {
alert("hello");
});

jQuery: Uncaught Error: Syntax error, unrecognized expression: (bunch of photo caption text data)

I am attempting to add text from a jQuery array at a given point to my webpage. The page is set up so that when I click on a thumbnail, a larger image and caption data are to show on the page, however the caption text is not showing. $(caption[this.id]).text().appendTo('#images'); is where the error comes from, though if I do a simple DOM.write with the (caption[this.id]).text(), the words will show on the page.
// App variables
var filename = [];
var caption = [];
var id = [];
var counter = 0;
var thumbs = $([]);
var images = $([]);
//Make an AJAX call to load the XML file data
$.ajax({
url: 'gallery.xml',
dataType: 'xml',
success: function (data) {
$(data).find('gallery').each(function () {
// Gather the image filenames
$(data).find('image filename').each(function () {
counter++;
id.push(counter);
filename.push($(this).text());
});
//console.log(filename);
// Gather the image captions
$(data).find('image caption').each(function () {
caption.push($(this).text());
});
});
// Run the thumbnail loading procedure when the AJAX call has completed
loadThumbs();
loadImages();
},
error: function () {
// Failure alert if XML was not loaded
alert("XML file couldn't load...");
}
});
// Loop through filename list and create large image for each entry
var loadImages = function() {
$.each(filename, function (_, src) {
images = images.add(
$('<img />', {src: 'images/' + src, class: 'largeImage', height: '100px', width: '100px'})
);
});
// Add first image to default
$(images[0]).appendTo('#images')
};
// Do some shit when image is clicked...
$( document ).on( "click", "img", function() {
// Empty viewing DIV and create a new large-scale version of the image for viewing
$('#images').empty();
$(images[this.id]).appendTo('#images');
//THIS IS WHERE ERROR COMES FROM
// Add text to <span> to solve error?
$(caption[this.id]).text().appendTo('#images');
});
The error is specifically:
I can add all of the JS code if there is not enough here to understand. Thanks in advance.
You're passing the caption text to jQuery:
$(caption[this.id])
The library is trying to parse it as a CSS selector, and it can't so it throws the error.
You could do several things to make it work, and your suggestion in the comment is a good one:
$("<span/>", { text: caption[this.id] }).appendTo("#images");

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).

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