I am working from some code that integrated DropboxJS as an angular directive. I cannot get it to work. I've taken his fiddle and updated it with current CDN links. Any idea why the directive code never fires? For ex if I drop an image it will go to /upload instead of /desiredupload and the event doesn't fire.
Fiddle: http://jsfiddle.net/cyberwombat/3tDqZ//1/
angular.module('dropZone', [])
.directive('dropZone', function() {
return function(scope, element, attrs) {
element.dropzone({
url: "/desiredupload",
maxFilesize: 100,
paramName: "uploadfile",
maxThumbnailFilesize: 5,
init: function() {
this.on("addedfile", function(file) {
alert("Added file."); });
}
});
}
});
angular.module('dropZone', [])
.controller('dropZoneCtrl', function() {});
Additionally and unfortunately I cannot replicate in my fiddle - on my local code I get this error: Object [object Object] has no method 'dropzone'
I am loading dropzone, then angular (tried the the other way) then my app, directives, etc.. so I don't think order is an issue. Dropzone successfully detects the form and makes it DnD but my directive element doesn't seem to have dropz
This is how I do it:
.directive('dropZone', function () {
return {
scope: {
action: "#",
autoProcess: "=?",
callBack: "&?",
dataMax: "=?",
mimetypes: "=?",
message: "#?",
},
link: function (scope, element, attrs) {
console.log("Creating dropzone");
// Autoprocess the form
if (scope.autoProcess != null && scope.autoProcess == "false") {
scope.autoProcess = false;
} else {
scope.autoProcess = true;
}
// Max file size
if (scope.dataMax == null) {
scope.dataMax = Dropzone.prototype.defaultOptions.maxFilesize;
} else {
scope.dataMax = parseInt(scope.dataMax);
}
// Message for the uploading
if (scope.message == null) {
scope.message = Dropzone.prototype.defaultOptions.dictDefaultMessage;
}
element.dropzone({
url: scope.action,
maxFilesize: scope.dataMax,
paramName: "file",
acceptedFiles: scope.mimetypes,
maxThumbnailFilesize: scope.dataMax,
dictDefaultMessage: scope.message,
autoProcessQueue: scope.autoProcess,
success: function (file, response) {
if (scope.callBack != null) {
scope.callBack({response: response});
}
}
});
}
}
})
An example usage of this would be:
<div action="/file/upload/" class="dropzone" drop-zone
call-back="myCallBackMethod(response)"
data-max="5"
auto-process="false"
message="Drop file here or click to select"
mimetypes=".doc,.docx,.pages,.pdf,.odt"
id="file-dropzone">
</div>
Any scope variable that has a ? next to it is optional. The only required field is action, which would be the URL to send post to.
$(element).dropzone({
url: "/desiredupload",
maxFilesize: 100,
paramName: "uploadfile",
maxThumbnailFilesize: 5,
init: function() {
this.on("addedfile", function(file) {
alert("Added file."); });
}
});
Wrap element with $(...). In AngularJS it says all DOM elements are JQuery object but I think you might be using a older version of AngularJS.
Related
I'm trying to implement the dropzone.js to my existing form, which has a lot of input fields, select options, etc. I tried to follow this answer: https://stackoverflow.com/a/35275260/13695248 but all I earned is if I press the send button, literally nothing happens. No error or anything, it just doesn't do anything. I've got a lot of help from #Swati so I have some extra functions in the dropzone options, but I don't think it causes the problem.
This is how the html looks like:
<form action="upload.php" method="post" enctype='multipart/form-data' id="add">
<div class="dropzone" id="uploader"></div>
<input type="text" id="mainimage" name="mainimage">
<!-- lot of input fields here -->
<input type="submit" class="btn btn-primary btn-xl" id="sendButton" name="upload" value="Send" />
</form>
and the JS part:
Dropzone.options.uploader = {
url: 'upload.php',
autoProcessQueue: false,
uploadMultiple: true,
paramName: "images", // The name that will be used to transfer the file
maxFilesize: 2, // MB
maxFiles: 5,
addRemoveLinks: true,
acceptedFiles: 'image/*',
accept: function(file) {
let fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onloadend = function() {
$('<a>', {
class: 'primary',
text: "Legyen ez a fő kép",
href: "#"
}).appendTo(file.previewElement)
//file.previewElement.append($textContainer)
console.log(file.previewElement)
file.previewElement.classList.add("dz-success");
if (($(".dz-success.dz-complete").length > 0) && ($(".main").length == 0)) {
$(".dz-success.dz-complete:first .primary").text("Fő kép")
//add class to first one
$(".dz-success.dz-complete:first").addClass("main")
$("#mainimage").val($(".dz-success.dz-complete:first").find(".dz-filename span").text()) //add default name to imgs input
}
}
file.previewElement.classList.add("dz-complete");
},
"error": function(file, message, xhr) {
if (xhr == null) this.removeFile(file);
alert(message);
},
removedfile: function(file) {
var is_there = file.previewElement.classList.contains("main");
console.log(is_there)
file.previewElement.remove();
if (is_there && $(".dz-success.dz-complete").length > 0) {
$(".dz-success.dz-complete .primary").text("Legyen ez a fő kép")
$(".dz-success.dz-complete:first .primary").text("Fő kép")
$(".dz-success.dz-complete:first").addClass("main")
$("#mainimage").val($(".dz-success.dz-complete:first").find(".dz-filename span").text()) //add default name to imgs input
}
if ($(".dz-success.dz-complete").length == 0) {
$("#mainimage").val("")
}
},
init: function() {
dzClosure = this; // Makes sure that 'this' is understood inside the functions below.
// for Dropzone to process the queue (instead of default form behavior):
document.getElementById("sendButton").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
dzClosure.processQueue();
});
//send all the form data along with the files:
this.on("sendingmultiple", function(data, xhr, formData) {
$(":input[name]", $("form")).each(function() {
formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val());
});
});
},
dictDefaultMessage: 'Kérjük húzza ide a képeket vagy kattintson a tallózáshoz!',
dictFallbackMessage: 'Böngészője nem támogatja a kép előnézetet!',
dictFallbackText: 'Kérjük használja a tallózást a képek kiválasztásához!',
dictFileTooBig: 'A fájl mérete túl nagy. ({{filesize}}MiB). Maximum {{maxFilesize}}MiB lehet!',
dictInvalidFileType: 'A kiválasztott fájl kiterjesztése nem megfelelő!',
dictResponseError: 'A szerver {{statusCode}} kóddal válaszolt. Kérjük próbálja meg később!',
dictCancelUpload: 'Feltöltés visszavonása',
dictUploadCanceled: 'feltöltés visszavonva!',
dictCancelUploadConfirmation: 'Biztosan visszavonja a feltöltést?',
dictRemoveFile: 'Kép törlése',
dictMaxFilesExceeded: 'Elérte a maximálisan feltölthető képek számát!'
};
$(document).on("click", ".primary", function() {
$(".dz-success.dz-complete.main .primary").text("Legyen ez a fő kép")
$(this).text("Fő kép")
$(".dz-success.dz-complete").removeClass("main")
$(this).closest(".dz-success.dz-complete").addClass("main")
$("#mainimage").val($(this).closest(".dz-success.dz-complete").find(".dz-filename span").text())
})
I think this init function ruins it, because if I delete it, the button works fine but the data doesn't go to the database
init: function() {
dzClosure = this; // Makes sure that 'this' is understood inside the functions below.
// for Dropzone to process the queue (instead of default form behavior):
document.getElementById("sendButton").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
dzClosure.processQueue();
});
//send all the form data along with the files:
this.on("sendingmultiple", function(data, xhr, formData) {
$(":input[name]", $("form")).each(function() {
formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val());
});
});
}
I had to add 'done' to the accept function:
accept: function(file, done) {
.
.
done();
}
could someone help me with one problem? I want to add a process bar when you waiting for a response from the server (Django 3.x).
Step to reproduce:
On the page 'A' we have the form.
Enter data to form.
Submit POST request by clicking to button on the page 'A'.
Waiting for getting the result on the page 'A'.
Get the result on the page 'A'.
So, I want to add process bar after 4th and before 5th points on the page 'A'. When you will get the result on the page 'A' it should disappear.
Python 3.7
Django 3.x
You can use nprogress, it's a library used for progress bars. Use this inside the interceptor where you can config it for displaying only when request is in progress until finished.
There are lots of ways to do this. I think using jquery would be easier. Basically you just need to prevent submitting the page and do an Ajax request to server. something like
<script type='text/javascript'>
$(document).ready(function () {
$("form").submit(function (e) {
// prevent page loading
e.preventDefault(e);
$('#loadinAnimation').show();
// preapre formdata
$.ajax({
type: "yourRequestType",
url: "yourUrlEndpoint",
data: formdata,
success: function (data) {
$('#loadinAnimation').hide();
// do rest of the work with data
}
});
});
});
</script>
and show appropriate loading animation in your html part
<div id='loadinAnimation' style='display:none'>
<div>loading gif</div>
</div>
You can also do it using UiKit Library in Javascript on your Django Template Page.
Below code is when a file is Uploaded
In your template file (template.html)
<body>
..
<form>
<progress id="js-progressbar" class="uk-progress" value="0" max="100" hidden></progress>
...
<div class="uk-alert-danger uk-margin-top uk-hidden" id="upload_error" uk-alert></div>
...
</form>
</head>
<script type="text/javascript">
$(document).ready(function(){
var bar = document.getElementById('js-progressbar');
UIkit.upload('.js-upload-list', {
url: '',
name : "customer-docs",
params :{
"csrfmiddlewaretoken":"{{csrf_token}}"
},
method : "POST",
concurrent:1,
allow:'*.(csv|xlsx)',
beforeSend: function (environment) {
console.log('beforeSend', arguments);
// The environment object can still be modified here.
// var {data, method, headers, xhr, responseType} = environment;
},
beforeAll: function (args,files) {
console.log('beforeAll', arguments);
},
load: function () {
console.log('load', arguments);
},
error: function (files) {
console.log("---------------")
},
complete: function () {
console.log('complete', arguments);
},
loadStart: function (e) {
console.log('loadStart', arguments);
bar.removeAttribute('hidden');
bar.max = e.total;
bar.value = e.loaded;
},
progress: function (e) {
console.log('progress', arguments);
bar.max = e.total;
bar.value = e.loaded;
},
loadEnd: function (e) {
console.log('loadEnd', arguments);
bar.max = e.total;
bar.value = e.loaded;
},
completeAll: function (data) {
console.log('completeAll', arguments);
console.log('completeAll', data);
let redirect_loc = ""
setTimeout(function () {
bar.setAttribute('hidden', 'hidden');
}, 1000);
// This is the response from your POST method of views.py
data.responseText = JSON.parse(data.responseText)
if(data.responseText.status == 201){
// swal is another library to show sweet alert pop ups
swal({
icon: data.responseText.status_icon,
closeOnClickOutside: true,
text: data.responseText.message,
buttons: {
Done: true
},
}).then((value) => {
switch (value) {
case "Done":
window.location.href = ""
break;
}
});
}
else if(data.responseText.status == 500){
swal({
icon: data.responseText.status_icon,
closeOnClickOutside: true,
text: data.responseText.message,
buttons: {
Ok: true
},
}).then((value) => {
switch (value) {
case "Ok":
window.location.href = ""
break;
}
});
}
}
});
// This block of code is to restrict user to upload only specific FILE formats (below example is for CSV & XLSX files)
(function() {
var _old_alert = window.alert;
window.alert = function(e) {
console.log(e)
if(e.includes("csv|xlsx") || e.includes("Invalid file type")) {
$("#upload_error").html("Invalid file format. Valid formats are CSV, XLSX").removeClass('uk-hidden')
}else if(e.includes("Internal Server Error")) {
$("#upload_error").html("Internal Server Error Kindly upload Documents again").removeClass('uk-hidden')
}
else {
_old_alert.apply(window,arguments);
$("#upload_error").addClass('uk-hidden').html("")
}
};
})();
});
</script>
On your views.py you can do your computation and once done, you can return a response like below
resp_json = {
"status" : 201,
"status_icon" : "success",
"url" : "/",
"message": message
}
return HttpResponse(json.dumps(resp_json))
For more info on SWAL (Sweet Alerts), visit https://sweetalert.js.org/guides/
I am using dropzone for uploading files which I save with some meta data in a database in the backend.
When a user tries to upload a bulk of files I want to check if he has already uploaded a file with this name and warn him with an alert with an option to continue ot stop.
So I disabled the autoProcessQueue.
I am also listening for addedfile event, I get the name, I perform an ajax to check if it exists in the database and return true or false, good.
Let's say the user tries to upload 40 files which all already exist, I don't want 40 warnings that this file already exists, I want one notification printing all 40 filenames.
I was looking for addedfile but for multiple files, I didn't found a solution.
Here is my code for now
This is where I'm stuck
How can I know when I've checked every file ?
$(document).ready(function() {
#if(isset($checkRoute))
function filenameExists(name) {
$.ajax({
type: 'GET',
url: '{{ $checkRoute }}',
data: {
name: name
}
})
.done(function(res) {
// returns true or false
return res
})
.fail(function(err) {
console.log(err)
})
}
#endif
let existingFilenames = []
let fileCount = 0
$('#{{ $element_id }}').dropzone({
url: "{{ $upload }}", // Set the url for your upload script location
paramName: "documents", // The name that will be used to transfer the file
maxFiles: 100,
maxFilesize: 100, // MB
parallelUploads: 100,
timeout: 240000,
addRemoveLinks: true,
acceptedFiles: "application/msword, application/vnd.ms-excel, application/pdf, image/*, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, image/*",
uploadMultiple: true,
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
},
init: function() {
#if(isset($checkRoute))
this.on('addedfile', function(file) {
console.log(this.getAcceptedFiles())
fileCount++
if (filenameExists(file.name)) {
console.log(file.name)
existingFilenames.push(file.name)
}
})
#endif
},
autoProcessQueue: false,
sendingmultiple: function (files) {
// Begin loading
KTApp.blockPage({
overlayColor: '#000000',
type: 'v2',
state: 'success',
message: '{{ __('documents_uploading') }}'
});
},
queuecomplete: function () {
// End loading
KTApp.unblockPage();
$.notify({
// options
message: '{{ __('documents_upload_success') }}'
}, {
// settings
type: 'success',
placement: {
from: "top",
align: "center"
},
animate: {
enter: 'animated fadeInDown',
exit: 'animated fadeOutUp'
},
});
window.location.replace("{{ $redirect }}");
}
});
});
Another thing that concerns me is how will I process the queue at the press of the button in the notification.
I had a similar problem as i wanted to provide the user with a dropdown of different upload directories on the server.
As each directory has a different ruleset of acceptedFiles, the static option acceptedFiles was out of the question.
Instead i used a combination of listening to the drop event on dropzone init and to "extend" the accept function:
The drop event gets the total number of files dropped and stores it in droppedFilesCounter. It also initializes/resets a global Object dzCustomFeedback and clears the "feedback" area from prior feedback.
The extended accept function, which is called for by each dropped file checks which upload directory is currently selected and compares the current file extension against the configured accepted ones.
If there is a match the done() function "bubbles" an "all clear".
Otherwise the file is removed from the preview list (or actually never makes it into it) and the global dzCustomFeedback Object cumulates error-messages of the type invalidFileTypes for all files accordingly.
Once all files have been run through the accept function (droppedFilesCounter == 0) and some feedback messages have been prepared the prepareFeedback function is called, which basically populates a jQuery <div> container with the cumulated content of dzCustomFeedback.
init: function() {
var myDropzone = this;
myDropzone.on("drop", function(event) {
if(typeof event.dataTransfer.files == 'object')
{
droppedFilesCounter = event.dataTransfer.files.length;
}
dzCustomFeedback = {};
jQuery('#dz-feedback').empty();
});
},
accept: function(file, done) {
/ * Erweiterung der vordefinierten accept Function * /
customTypeSelector = document.getElementById("selectUploadFolder");
var currentAcceptedFiles = customTypeAcceptedFiles[customTypeSelector.options[customTypeSelector.selectedIndex].value]
var currentFileExtension = file.name.split(".").pop()
droppedFilesCounter--;
if (typeof customTypeAcceptedFiles[customTypeSelector.options[customTypeSelector.selectedIndex].value] == "object"
&&
jQuery.inArray(currentFileExtension, currentAcceptedFiles["extensions"] ) >= 0) {
//accepted file
done();
}
else {
//Unaccepted file revert
this.removeFile(file);
if(typeof dzCustomFeedback["invalidFileTypes"] == "undefined")
{
dzCustomFeedback["invalidFileTypes"] = {
"msg": [
uploadDndConf["textElements"]["invalidFileTypesUploadFolder"].replace("{{uploadFolder}}", currentAcceptedFiles["label"])
, currentAcceptedFiles["extensions"].join()
]
, "type": "error"
, "filesIgnored": {} };
}
dzCustomFeedback["invalidFileTypes"]["filesIgnored"][file.name] = file;
done(this.options.dictInvalidFileType);
}
if(droppedFilesCounter == 0 && !jQuery.isEmptyObject(dzCustomFeedback))
{
prepareFeedback(dzCustomFeedback);
}
},
I wanted to save each and every button action(for ng-clicked and non-ng-clicked including file uploads/urls i.e. whatever I click, that button should be stored in json file/object i.e. for all <input type="button"> in my case.) in my json file/object using either angularjs or jquery ? How can I do it ? Created Fiddle,Please help me. Thanks in advance.
html:
You can add global beforeSend handler using:
$.ajaxSetup({
beforeSend: function(jqXHR, settings) {
var data = {
url: settings.url,
data: settings.data
};
$.ajax({url: 'save_json.php', data: data, beforeSend: $.noop);
}
});
This will work only for ajax requests, if you want to log form submit you will need to add global submit handler:
$('body').on('submit', 'form', function(e) {
var $target = $(e.target);
$.ajax({
url: 'save_json.php',
data: {url: $target.attr('action')},
beforeSend: $.noop,
sucess: function() {
$target.submit(function(e) {
// prevent global handler
e.stopPropagation();
}).submit();
}
});
return false;
});
Set click listener on the form element.
$('#formid').click(function (e) {
//e.target will give the element clicked inside the form
});
Check if the e.target is a button element and save the button in your json file/object.
I think the most angular-way of achieving this is to create a directive and use this directive instead of <input type="button"> I created a sample;
var actionLogs = [];
yourModule.directive("actionButton", [function () {
return {
scope: {
action: '&',
type: '#'
},
restrict: 'E',
replace: true,
transclude: true,
controller: function ($scope, $element) {
var logClickEvent = function ($event) {
var log = {element: $element, action: $scope.action, clickTime: new Date()};
actionLogs.push(log);
};
$scope.doAction = function ($event) {
//** Do whatever you want here before every action
logClickEvent($event);
$scope.action.apply(null, []);
};
},
link: function (scope, element, attrs) {
if (typeof attrs.type === "undefined") {
scope.type = "button";
}
},
template: '<input type="{{type}}" ng-click="doAction($event)"><span ng-transclude></span></input>'
};
}]);
You can use <action-button action="yourNgClickFunc()"> instead of <input type="button" ng-click="yourNgClickFunc()"> and your clicks will be logged in the array actionLogs.
In some cases you may want to log click events other then <input type="button">s. Then I prefer to use an attribute directive instead of element directive, for example;
var actionLogs = [];
yourModule.directive("loggable", [function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.click(function (e) {
var action = scope[attrs.ngClick.split("(")[0]];
var log = {element: element, action: action, clickTime: new Date()};
actionLogs.push(log);
});
}
};
}]);
You can use <input type="button" ng-click="yourNgClickFunc()" loggable> instead of <action-button> and your clicks still will be logged in the array actionLogs
I am trying to output form validation errors when you hover over the "X" in the dropped off file in Dropzone.
What I get:
How can I make the object Object output the actual error message from the form validation? I can alert the error message but can't actually show the error message upon hovering over the x.
My js file:
Dropzone.options.fileupload = {
maxFilesize: 20,
init: function () {
thisDropzone = this;
this.on("error", function (file, responseText) {
$.each(responseText, function (index, value) {
alert( value); //this shows the alert of the error message
});
});
}
};
My controller:
$this->validate($request, [
'file' => 'max:20000',
]);
I have fixed my issue.
To anyone who might have the same issue.
I fixed it by simply putting $('.dz-error-message').text(value);
Full code:
Dropzone.options.fileupload = {
maxFilesize: 50,
init: function () {
thisDropzone = this;
this.on("error", function (file, responseText) {
$.each(responseText, function (index, value) {
$('.dz-error-message').text(value);
});
});
}
};
If you want just laravel validation errors use that.
...
init: function () {
thisDropzone = this;
this.on("error", function (file, responseText) {
$.each(responseText, function (index, value) {
if (index === 'errors'){
$('.dz-error-message').text(value.file);
}
});
});
},
...
Or if you just want dropzone.js error outputs change.
if(index === message)