AngularJS file drag and drop in directive

36,075

Solution 1

To consolidate the comments into an answer, change ondrop to drop, add e.stopPropagation(), change holder to elem.

edo.directive('fileDrag', function () {
  return {
    restrict: 'A',
    link: function (scope, elem) {
      elem.bind('drop', function(e){
        e.preventDefault();
        e..stopPropagation();
        var file = e.dataTransfer.files[0], reader = new FileReader();
          reader.onload = function (event) {
          console.log(event.target);
          elem.style.background = 'url(' + event.target.result + ') no-repeat center';
        };
        console.log(file);
        reader.readAsDataURL(file);

        return false;
      });
    }
  };
});

I was doing something similar and here is my working solution:

HTML

app.directive("dropzone", function() {
    return {
        restrict : "A",
        link: function (scope, elem) {
            elem.bind('drop', function(evt) {
                evt.stopPropagation();
                evt.preventDefault();

                var files = evt.dataTransfer.files;
                for (var i = 0, f; f = files[i]; i++) {
                    var reader = new FileReader();
                    reader.readAsArrayBuffer(f);

                    reader.onload = (function(theFile) {
                        return function(e) {
                            var newFile = { name : theFile.name,
                                type : theFile.type,
                                size : theFile.size,
                                lastModifiedDate : theFile.lastModifiedDate
                            }

                            scope.addfile(newFile);
                        };
                    })(f);
                }
            });
        }
    }
});
div[dropzone] {
    border: 2px dashed #bbb;
    border-radius: 5px;
    padding: 25px;
    text-align: center;
    font: 20pt bold;
    color: #bbb;
    margin-bottom: 20px;
}
<div dropzone>Drop Files Here</div>

Solution 2

Preventing default events, and getting file from original event. All can be implemented in directive. You should pass function, for work with files to attribute on-file-drop. Also 'dragging' class is added to dropzone element while dragging. In view it looks like this:

<div file-dropzone on-file-drop="myFunction">This is my dropzone </div>

directive:

function fileDropzoneDirective() {
    return {
        restrict: 'A',
        link: fileDropzoneLink
    };
    function fileDropzoneLink($scope, element, attrs) {
        element.bind('dragover', processDragOverOrEnter);
        element.bind('dragenter', processDragOverOrEnter);
        element.bind('dragend', endDragOver);
        element.bind('dragleave', endDragOver);
        element.bind('drop', dropHandler);

        function dropHandler(angularEvent) {

            var event = angularEvent.originalEvent || angularEvent;
            var file = event.dataTransfer.files[0];
            event.preventDefault();
            $scope.$eval(attrs.onFileDrop)(file);

        }
        function processDragOverOrEnter(angularEvent) {
            var event = angularEvent.originalEvent || angularEvent;
            if (event) {
                event.preventDefault();
            }
            event.dataTransfer.effectAllowed = 'copy';
            element.addClass('dragging');
            return false;
        }

        function endDragOver() {
            element.removeClass('dragging');
        }
    }
}
Share:
36,075
piggyback
Author by

piggyback

Updated on July 09, 2022

Comments

  • piggyback
    piggyback almost 2 years

    This example does pretty much what I would like to port in Angular-js: HTML5 File API.

    I have been trying to google some example of directives however I found old example that do massive use of DOM or are not written for Angular 1.0.4.

    Basically this is the pure js code:

    var holder = document.getElementById('holder'),
        state = document.getElementById('status');
    
    if (typeof window.FileReader === 'undefined') {
      state.className = 'fail';
    } else {
      state.className = 'success';
      state.innerHTML = 'File API & FileReader available';
    }
    
    holder.ondragover = function () { this.className = 'hover'; return false; };
    holder.ondragend = function () { this.className = ''; return false; };
    holder.ondrop = function (e) {
      this.className = '';
      e.preventDefault();
    
      var file = e.dataTransfer.files[0],
          reader = new FileReader();
      reader.onload = function (event) {
        console.log(event.target);
        holder.style.background = 'url(' + event.target.result + ') no-repeat center';
      };
      console.log(file);
      reader.readAsDataURL(file);
    
      return false;
    };
    

    The only possible way I can think of is creating a directive that does

    edo.directive('fileDrag', function () {
      return {
        restrict: 'A',
        link: function (scope, elem) {
          elem.bind('ondrop', function(e){
            e.preventDefault();
            var file = e.dataTransfer.files[0], reader = new FileReader();
              reader.onload = function (event) {
              console.log(event.target);
              holder.style.background = 'url(' + event.target.result + ') no-repeat center';
            };
            console.log(file);
            reader.readAsDataURL(file);
    
            return false;
          });
        }
      };
    });
    

    However (1) it did not work, (2) before I fix it I would like to know if something exists or if I am doing it properly,

    Any hint or help is very much appreciated.

  • Joseph
    Joseph over 9 years
    No, Dropzone doesn't play nice with angular
  • huan feng
    huan feng over 8 years
    I've just used your piece of code to create a plnkr, and i got no luck with it. Can you help to point what is the problem with it?plnkr.co/edit/C7Vv8d?p=preview
  • JSV
    JSV about 8 years
    Thanks for this. I know this is a older post but straight out of the box I only had 1 problem. Getting the file info I had to traverse a bit different. var files = evt.dataTransfer.files; Change To var files = evt.originalEvent.dataTransfer.files
  • Kevin Vincent
    Kevin Vincent almost 8 years
    Thanks a lot for your solution.
  • tonestrike
    tonestrike over 5 years
    In order for this to work in Chrome, you now need to prevent default on the 'dragover' event. You can use code like this: element.bind('dragover', ($event) => { $event.preventDefault() $event.stopPropagation() })
  • rodzmkii
    rodzmkii over 4 years
    This is a nice answer. Just one thing, I had to call endDragOver at the end of dropHandler to remove the dragging effect once the file is processed.