No delete the last file on dropzone.js - javascript

I am using the Dropzone JS plugin to upload and manage images on my server. Everything works great, but now I need to force the user to keep at least one image for the product. That means that if the users wants to change the images they need to upload a new one image before they delete the old one.
I can manage it on the server side, but I can't figure out a way to retain the thumbnail visible and notify the user they are not allowed to delete the last one. Just keeping the image on the server without telling the users whats going on will result on a bad UX.
Here is my code
$("div#myId").dropzone({
url: "/stores/sell_upload/",
maxFiles: 4,
acceptedFiles: "image/*",
addRemoveLinks: true,
renameFile: function(file) {
let newName = file.name.replace("." + file.name.split(".").pop(), "") + "-" + new Date().getTime() + "." + file.name.split(".").pop();
return newName;
},
removedfile: function(file) {
$.ajax({
type: "POST",
url: "/stores/sell_upload/",
data: {
name: file.name,
action: "delete"
}
});
var _ref;
return (_ref = file.previewElement) != null ? _ref.parentNode.removeChild(file.previewElement) : void 0;
}
});

So, Wouldn't it be easier if you validated the files in the action of the button to save? I mean, the dropzone area can be empty but you in your button action validate that it has to be at least one file in the area to be saved.

Related

reload page and stay in current page

I need to understand how I can change the data of a customer on page 2 without returning to page 1 when reloading the page.
In practice I need to stay on the current page, but currently when I change the data of a customer and save the card, if I am on page 3 it always returns me to page 1.
I tried window.location.reload().
Is there any way to avoid resetting the filters?
This is my JavaScript function:
function rinnova(id) {
var formData = new FormData();
var abbonamento = id.split("-");
var abb = $("#abb" + abbonamento[3]).val();
console.log(abb);
formData.append(
"id",
abbonamento[0] + "-" + abbonamento[1] + "-" + abbonamento[2]
);
formData.append("abb", abb);
$.ajax({
url: "include/rinnovaabbonamento.php",
data: formData,
type: "POST",
contentType: false,
processData: false,
beforeSend: function () {
return confirm("Vuoi rinnovare l'abbonamento?");
},
success: function (response) {
$(".box").html(
"<h3 class='text-center pb-3 pt-3'>Rinnovo confermato!</h3>"
);
document.location.reload();
}
});
}
This is my FooTable code:
if ($('.footable-editing').length) {
var
$modal = $('#footable-editor-modal'),
$editor = $('#footable-editor'),
$editorTitle = $('#footable-editor-title'),
ft = FooTable.init('.footable-editing', {
editing: {
enabled: true,
addRow: function() {
$modal.removeData('row');
$editor.find('#id').val("");
$editor.find('#account').show();
$editor.find('#categoria').val("1");
$editor.find('#costo').val("40.00");
$editor[0].reset();
$editorTitle.text('Add a new row');
$modal.modal('show');
},
editRow: function(row) {
var values = row.val();
$editor.find('#account').hide();
$editor.find('#id').val(values.id);
$editor.find('#nome').val(values.nome);
$editor.find('#cognome').val(values.cognome);
$editor.find('#data').val(values.data);
$editor.find('#luogonascita').val(values.luogonascita);
$editor.find('#residenza').val(values.residenza);
$editor.find('#codicefiscale').val(values.codicefiscale);
$editor.find('#scadenza').val(values.scadenza);
$editor.find('#costo').val(values.costo);
$editor.find('#note').val(values.note);
$editor.find('#attivo').val(values.attivo);
var cat = values.categoria.split(".");
var idcat = cat[0];
if (values.categoria == "Nessuna categoria") {
$("#nessunacategoria").attr('selected', 'selected');
} else {
$("#cat" + idcat).attr('selected', 'selected');
}
$modal.data('row', row);
$editorTitle.text('Edit - ' + values.nome + ' Data');
$modal.modal('show');
},
deleteRow: function(row) {
if (confirm('Sei sicuro di voler cancellare?')) {
var values = row.val();
var formData = new FormData();
formData.append('id', values.id);
formData.append('elimina', "1");
$.ajax({
url: "include/register_account.php",
data: formData,
type: "POST",
contentType: false,
processData: false,
success: function(response) {
alert(response);
}
});
}
}
}
}),
uid = 10;
};
you have to populate the html page with updated content after the ajax call instead of calling window.reload or document.reload
It's hard to know the details without seeing more of the relevant code, but it sounds like -- from your server's point of view -- there is only ever one page being loaded. When the user sees "page 2", the browser has merely changed the appearance of the page (probably, but not necessarily, including an AJAX request to exchange some information with the server) without actually loading an enire new page. Then, when you ask the server to reload the page, you logically get the original state of the page, which looks like "page 1".
If the address in the URL bar changes whenever the user's view changes (e.g. from "page 1" to "page 2"), then you can control which view the user sees by setting the document.location property, (although using the history API might give you more precise control).
On the other hand, if the address is always the same, you probably can't rely on as much help from the browser to set a particular state, but if the change to "page 2" used an AJAX request to the server, then you can send a similar request to reproduce a similar state.
In either case, the state you get (from the browser's history API or the server's AJAX response) will probably not exacly match the state you want because the user has already made some changes to "page 2". You are already capturing some user-entered information: the id parameter of your rinnova function presumably represents something the user entered. Once you have captured any such information you need, you will trigger the reset of "page 2", then add the finishing touch by
restoring the captured values into whatever page elements they were captured from.
For example, let's imagine that the earlier code said:
const cardID = ("#card-id").val();
rinnova(cardID);
Now that you have reset "page 2", you would repopulate the #card-id element by doing something like:
$("#card-id").val(cardID);
A small caveat about making your changes directly in the browser:
Seeing a change in the URL (when the user's view changes) does means that the browser knows about the change, but it doesn't guarantee that the browser was directly responsible for the change. Using the browser to navigate between views is appropriate in a "single-page application" website where such navigation is generally handled on the client side (i.e. by the browser). But if your site is making these changes from the server side via AJAX, your reset of "page 2" should also be done via AJAX so the server always knows what you're doing.

Dropzone chunk uploading to Azure Storage

I'm trying to use dropzone to upload large files directly to Azure storage using a SAS(Shared Access Signature). This is documented on the Azure side here: https://learn.microsoft.com/en-us/rest/api/storageservices/Put-Block
So I have to get the blockId(A Base64 string that identifies the chunk I'm sending) and put it in the url of the request sending that chunk.
Dropzone supports chunking now so I decided to use that. Unfortunately, implementations of it are hard to find.
I can change the url in the processing event but that's per file and I can't get chunk data from it.
I can send the blockId in the form data using params but can't seem to change the url from it.
Is it possible to add the blockId to my url? Or will I have to send it to my server first and upload from there? Thanks.
$("#videoSection").dropzone({
params: function (files, xhr, chunk) {
console.log(chunk);
xhr.setRequestHeader('x-ms-blob-type', 'BlockBlob');
//I can get the blockId from the chunk here
//this.options.url.replace("test") //doesn't work
//xhr.open(this.options.method, this.options.url.replace("test"), true); //doesn't work
return {"url": "test"}; //this returns form-data
},
url: "Create",
method: "PUT",
//headers: { "x-ms-blob-type": "BlockBlob" },
chunking: true,
chunkSize: 4000000,
forceChunking: true,
retryChunks: true,
retryChunksLimit: 3,
autoProcessQueue: false,
acceptedFiles: "video/*",
maxFiles: 1,
maxFilesize: 3000,
previewTemplate: $("#videoTemplate").html(),
dictDefaultMessage: "Drop Video Here",
init: function () {
this.on("processing", function (file) {
var blockId = 1;
#*this.options.url = "#(Config.Value.StoragePrefix)/#(user.Company.StorageName)/inspections/#Model.InspectionData.Inspection.Id/videos/"
+ file.name + "#Html.Raw(Model.SharedAccessSignature + "&comp=block&blockid=")" + blockId;*#
var progressBar = $(file.previewElement).find(".dropzoneProgressBar");
progressBar.show();
});
this.on("chunksUploaded", function (file, done) {
$.ajax({
type: "PUT",
url: "#(Config.Value.StoragePrefix)/#(user.Company.StorageName)/inspections/#Model.InspectionData.Inspection.Id/videos/"
+ file.name + "#Html.Raw(Model.SharedAccessSignature + "&comp=blocklist")",
success: function (data) {
done();
},
error: function (e) {
toastr.error(e);
console.log(e);
}
});
});
this.on("uploadprogress", function (file, progress, bytesSent) {
var progressBar = $(file.previewElement).find(".dropzoneProgressBar");
progress = bytesSent / file.size * 100;
progressBar.width(progress + "%");
});
this.on("success", function (file, response) {
var successCheckmark = $(file.previewElement).find(".dropSuccess");
successCheckmark.toggle();
var progressBar = $(file.previewElement).find(".dropzoneProgressBar");
progressBar.css("background-color", "green");
});
}
});
There is a problem with this line this.options.url.replace("test") //doesn't work. It should be this.options.url = this.options.url.replace("test", "newValue") //this should work. I am using a similar approach. The issue is that the params callback is called once XmlHttpRequest has been opened so you need to set the url for the first chunk before you start processing the file and in the params you need to set the url for the next chunk to be sent (use chunk.index + 1 for zero based indexing). I am not sure if this would work if you had parallelChunkUploads enabled - I haven't tested if the first chunk to be processed is the first one. Let me know if you need more information.

Populate dropzone.js with image from the database issue removing images

I have an issue trying to remove the files which I have passed dropzone.js after calling the database.
When I navigate to the page and upload a new image and then remove it without refreshing the page it all works as expected.
Below is my current upload method
myDropzone.on("success", function (file, response) {
console.log(response);
file.serverId = response;
});
This is what is inside the response after doing console.log(response);
Object { UniqueId="evgopvdjfs1w9sos3jt5"}
Which is correct.
Now when I press F5 and refresh the page I populate dropzone with the following snippet which returns me the image that I've just uploaded.
$.getJSON("/Person/GetPreviews/").done(function (data) {
if (data.Data != '') {
$.each(data.Data, function (index, item) {
var UniqueId = item.ImageName;
var mockFile = {
name: item.ImageName,
serverId: UniqueId // This is what I need to delete the image
};
console.log(mockFile);
// Call the default addedfile event handler
myDropzone.emit("addedfile", mockFile);
// And optionally show the thumbnail of the file:
myDropzone.emit("thumbnail", mockFile, item.ImageUrl);
myDropzone.files.push(mockFile);
});
}
});
Now when I do console.log(mockFile); the below is shown, again this is correct.
Object { name="evgopvdjfs1w9sos3jt5", UniqueId="evgopvdjfs1w9sos3jt5"}
Now when it comes to removing the file this is my current delete function
removedfile: function (file) {
console.log(file);
$.ajax({
type: 'POST',
url: '#Url.Action("DeleteUploadedFile", "Person", new {userId= #Model.UserId})',
data: "id=" + file.serverId['UniqueId'],
dataType: 'html',
});
var ref;
return (ref = file.previewElement) != null ? ref.parentNode.removeChild(file.previewElement) : void 0;
},
Now when I press remove file on the pre populated image from the database, it throws an error on
data: "id=" + file.serverId['UniqueId'],
saying its underfined, I personally cannot see how because both console.logs show the UniqueId it makes me wonder if I'm missing something when I'm pre populating dropzone with the images?
As you can see I have a console.log(file); in the delete function which when hit shows the following
Object { name="evgopvdjfs1w9sos3jt5", UniqueId="evgopvdjfs1w9sos3jt5"}
Can anyone see whats wrong?
So this question is a bit old. I came across it and also found this answer, which helped me:
http://www.stackoverflow.com/questions/24445724/add-existing-image-files-in-dropzone
This particular question was answered here and just points out a mix-up between serverId and UniqueId:
https://github.com/enyo/dropzone/issues/844

Cannot remove files from file list using JQuery-File-Upload

I have an issue using the JQuery-File-Upload plugin. I am using the plugin directly and not through the author's provided html example pages. Basically I have a form with some inputs
one of which is a file input. The first upload works fine but when I attempt a second upload both files are sent (the first one for the second time) when it should only be the second one.
Example:
File 1 is selected.
File 1 is uploaded.
Success.
Using jquery I reset the form with $(FORM_SELECTOR).trigger('reset')
File 2 is selected.
File 1 and file 2 are BOTH uploaded.
Problem.
Now I have two copies of file 1. This is not what I want.
Obviously there isn't much point of using an ajax form upload if it only works once so I assume that there is something I am missing.
Is there a way to reset the file queue?
When examining the data.files object I can see that the files are there after the form
is reset. What can I do to sync the plugin with the input or clear out the data.files.
If I manually clear out the data.files array (via pop or data.files = []) attempting a
second upload does not work.
I init the upload form like this:
$('#file-upload-form').fileupload({
url: 'uploads/upload',
type: 'POST',
dataType: 'json',
multipart: true,
dropZone: null,
formAcceptCharset: 'utf-8',
autoUpload: true,
add: function (e, data) {
fileUploadData = data;
$("#upload-file-btn").click(function () {
data.submit()
.success(function (e, status, data) {
console.log("success response from post", status, data);
var i = '<input id="file-select-input" name="files[]" multiple/>';
$('#file-select-input').replaceWith(i);
})
});
}
});
I have a custom .add event handler, in which I have called .off("click") on my button:
add: function (e, data) {
$('#btnstartupload').off("click");
data.context = $('#btnstartupload')
.click(function () {
data.submit();
$(".fileinput-button").hide();
});
}
I had the same problem and the only way I've managed to solve that was to check in the submit callback if the file was already uploaded. I just check if the file name is included in the array fileNames which I push the current file name before the submission and checks on the next time if the next one is present on the array and if so cancel the submission.
var fileNames = new Array();
$('#uploadfile').fileupload({
submit: function(e, data) ->
var fileName = data.files[0].name;
if ($.inArray(fileName, fileNames) === -1)
fileNames.push(fileName);
else
return false;
});
You can reset inputs quite easy.
If that does not work for you, you might store the file somwhere.
DEMO
$("button#1").click(function(){
//check file
alert($("input").val());
return false;
});
$("button#2").click(function(){
//reset form
$("form")[0].reset();
return false;
});
$("button#3").click(function(){
//replace input
$("input").replaceWith($('<input type="file"/>'));
return false;
});
I ran into the same problem. It's puzzling why the library re-sends previously uploaded files. I tried to destroy it, re-initialize it and clear/reset the actual file input field, but none of them worked.
The solution I came up with is to keep track of all upload attempts by storing file names in an array and checking in the "send" callback if a file has been already uploaded.
Callback:
send: function (e, data) {
var file = data.files[0];
// if we already attempted to upload a file with the same file name
// do not send it in order to avoid duplicates
if (existsInAttemptedUploads(file.name)) {
return false;
}
},
Helper methods:
// list of file names that a user attempted to upload
var attemptedUploads = [];
// adds a file name to the list of attempted uploads
function addAttemptedUpload(fileName) {
attemptedUploads.push(fileName);
};
// removes a given file name from the list of attempted uploads
function removeAttemptedUpload(fileName) {
var index = $.inArray(fileName, attemptedUploads);
if (index > -1) {
attemptedUploads.splice(index, 1);
}
};
// checks if a given file name is in the list of attempted uploads
function existsInAttemptedUploads(fileName) {
var result = $.inArray(fileName, attemptedUploads);
if (result == -1) {
return false;
}
return true
};
Make sure to update the list of attemptedUploads in your "done" and "fail" callbacks and remove a file from the list if it was removed. For example:
done: function (e, data) {
var id = e.target.id;
// List all uploaded files
$.each(data.result.files[0], function (index, file) {
// add the current file name to the list of attempted file names
addAttemptedUpload(file.origFileName);
$('#uploadedFiles').append('<li id="' + file.id + '" data-orig-filename="' + file.origFileName + '">' + file.fileName + ' <a class="removeFile" href="<?php echo $deleteUrl; ?>/' + file.id + '">' + ' Remove</a></li>');
});
},
My code like that, when clicking the Remove link into the multiple file from JSP page, It's OK for IE11, Chrome and Firefox.
function removeFileLink(id) {
var fileName = document.getElementById("import_file_" + id).value.replace(/^.*[\\\/]/, '');
var objFiles = document.getElementsByName("import_fileList");
for(var i=0; i<objFiles.length; i++){
if(objFiles[i].value.replace(/^.*[\\\/]/, '') == fileName) {
$(objFiles[i]).remove();
}
}
document.getElementById("import_form").enctype="multipart/form-data";
document.getElementById("import_form").submit();
return false;
}

how to upload and delete files from dropzone.js

I have used the below code the image has been deleted but the thumbnail image still showing.
Dropzone.options.myDropzone = {
init: function() {
this.on("success", function(file, response) {
file.serverId = response;
});
this.on("removedfile", function(file) {
if (!file.serverId) { return; }
$.post("delete-file.php?id=" + file.serverId);
});
}
For deleting thumbnails you have to enable
addRemoveLinks: true,
and to use "removedfile" option in dropzonejs
removedfile: 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.
addRemoveLinks: true,
removedfile: function(file) {
var _ref;
return (_ref = file.previewElement) != null ? _ref.parentNode.removeChild(file.previewElement) : void 0;
}
I also added an ajax call for delete script and it looks like this:
addRemoveLinks: true,
removedfile: function(file) {
var name = file.name;
$.ajax({
type: 'POST',
url: 'delete.php',
data: "id="+name,
dataType: 'html'
});
var _ref;
return (_ref = file.previewElement) != null ? _ref.parentNode.removeChild(file.previewElement) : void 0;
}
It works on my side, so I hope it helps.
In addition to the best answer above - to remove spaces and replace with dashes and convert to lower case apply this js in the dropzone.js file:
name = name.replace(/\s+/g, '-').toLowerCase();
To the filename handling - I edited the dropzone.js file AND the above ajax call. This way, the client handles the file naming, and it AUTOMATICALLY gets sent to the PHP/server-side, so u don't have to redo it there, and it's URL safe pretty much.
In some instances the js changed depending on the function / naming:
dropzone.js - line 293 (approx):
node = _ref[_i];
node.textContent = this._renameFilename(file.name.replace(/\s+/g, '-').toLowerCase());
dropzone.js - line 746 (approx):
Dropzone.prototype._renameFilename = function(name) {
if (typeof this.options.renameFilename !== "function") {
return name.replace(/\s+/g, '-').toLowerCase();
}
return this.options.renameFilename(name.replace(/\s+/g, '-').toLowerCase());
};
I moved the whole removedFile section up to the top of dropzone.js like so:
autoQueue: true,
addRemoveLinks: true,
removedfile: function(file) {
var name = file.name;
name =name.replace(/\s+/g, '-').toLowerCase(); /*only spaces*/
$.ajax({
type: 'POST',
url: 'dropzone.php',
data: "id=" + name,
dataType: 'html',
success: function(data) {
$("#msg").html(data);
}
});
var _ref;
if (file.previewElement) {
if ((_ref = file.previewElement) != null) {
_ref.parentNode.removeChild(file.previewElement);
}
}
return this._updateMaxFilesReachedClass();
},
previewsContainer: null,
hiddenInputContainer: "body",
Note I also added in a message box in the html: (div id="msg"></div>) in the HTML that will allow server side error handling/deleting to post a message back to the user as well.
in dropzone.php:
if (isset($_POST['id']) {
//delete/unlink file
echo $_POST['id'] . ' deleted'; // send msg back to user
}
This is only an expansion with the working code on my side. I have 3 files, dropzone.html, dropzone.php, dropzone.js
Obviously, you would create a js function instead of repeating the code, but it suited me since the naming changes. You could pass the variables in a js function yourself to handle filename spaces/chars / etc.

Categories