Scott Scott - 1 month ago 11
AngularJS Question

angular.bind() for 'drop' event listener not behaving properly

Plunker demonstrating code

I have a

<div>
element with the directive
dragAndDrop
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 call
e.stopPropagation()
and
e.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:
// http://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]);
}
}
});
}
};
});

Answer

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();
}); 

Updated plnkr.

Comments