angular.bind() for 'drop' event listener not behaving properly
Angular doesn't know when event handlers you have registered are triggered, so you need to notify Angular that you've changed scope variables. You do that with the $apply
function.
$scope.$apply(function () {
$scope.divClass = 'on-drag-enter';
});
When it comes to the drop handler you're correct--the statements after var droppedFiles = e.dataTransfer.files;
are never run because of the runtime error that statement results in.
e
is a jQuery event, whereas dataTransfer
is a property on the actual DOM event. To fix the problem you need to access the original event through the originalEvent
property.
var droppedFiles = e.originalEvent.dataTransfer.files;
Update
e.originalEvent
is only needed if you have included jQuery before angular, but since you haven't done that in your plnkr, that solution was invalid, I apologize for that.
It seems like you need to opt in for drop support by preventDefault
of the dragover event (Perhaps this can be done in other events as well--haven't tested.)
elem.bind('dragover', function (e) {
e.stopPropagation();
e.preventDefault();
});
Scott
Updated on June 08, 2022Comments
-
Scott about 2 years
I have a
<div>
element with the directivedragAndDrop
set as an attribute:<div ng-class="divClass" drag-and-drop>Drop files here</div>
(as a side, and less important note, ng-class is not behaving as expected, but that's a separate issue) What I would like is for users to drag and drop files from their desktop to the div. Upon doing so though, I use
angular.bind()
to detect the drop event. Of course once they drop, I calle.stopPropagation()
ande.preventDefault()
, but the page continues on with redirecting the page regardless. What am I doing wrong that would prevent the two aforementioned methods from executing?dragAndDrop
directive:directive('dragAndDrop', function() { return { restrict: 'A', link: function($scope, elem, attr) { elem.bind('dragenter', function(e) { e.stopPropagation(); e.preventDefault(); // still can't get this one to behave: // https://stackoverflow.com/q/15419839/740318 $scope.divClass = 'on-drag-enter'; }); elem.bind('dragleave', function(e) { e.stopPropagation(); e.preventDefault(); $scope.divClass = ''; }); elem.bind('drop', function(e) { var droppedFiles = e.dataTransfer.files; // It's as though the following two methods never occur e.stopPropagation(); e.preventDefault(); if (droppedFiles.length > 0) { for (var i=0,ii=droppedFiles.length;i<ii;i++) { $scope.files.push(droppedFiles[i]); } } }); } }; });
-
Scott over 11 yearsNeither of those worked, for either issue. I've updated the plunker to reflect your recommendations. As for the
$scope.$apply()
, I presumed that was unnecessary given theangular.bind()
method put on the element would keep scope, when using something such as.addEventListener()
orelem.ondrop = function()
would not. Am I mistaken? -
Martin over 11 years
$scope.$apply
(you missed the$
) fixes the css class problems. (Gonna take a look at the other problem when I get home.) -
Scott over 11 yearsDoh! Good catch. Oddly enough, I seemingly recall trying that before based off previous advice from elsewhere. I must have either been making a mistake elsewhere or not trying it at all. Regardless, thank you. Looking forward to the ondrop solution.
-
Mark Rajcok over 11 years@Scott, when a
bind()
callback function runs, it runs "outside" of Angular, becausebind()
just sets up normal JavaScript event binding -- hence the need for us to manually call$apply()
. It might be nice if Angular provided some kind ofbindApply()
or 'angularBind()` method that would automatically call$apply()
for us after executing the callback function. -
Scott over 11 yearsSo that begs the question, why even use
angular.bind()
? Formality? -
Martin over 11 yearsYou're not using '
angular.bind
' but their jqLite implementation (it's usually a good idea to include jQuery before Angular, so you get full jQuery support).bind
(oron
which is the 'new' syntax) is a good faqade around different browser implementations. -
Scott over 11 yearsdragover... That's a new one. Alas it does appear to solve the problem! Thanks for your assistance!
-
Alan over 10 yearsYou don't have to set the dropEffect, but you do need to stop the dragover event, otherwise the drop event won't fire.