OdeToCode IC Logo

AngularJS Drag and Drop Photo Directive

Wednesday, July 10, 2013

Continuing from previous posts on building a file input directive and a file reader service, this post contains my first try at a drag-n-drop directive that uses the file reader service to copy an image dropped from the desktop into an img element.

As always, I welcome suggestions!

Native HTML 5 drag-and-drop is easy to work with. The directive handles the dragover, dragleave, and drop events on the target element. Dragover and dragleave are mostly about manipulating classes on the element to style it as a droppable target, as well as using e.preventDefault(), which is required for the drop event to work.

module.directive("imageDrop",
  function ($parse, fileReader, resampler) {

    return {
        restrict: "EA",
        link: function (scope, element, attrs) {

            var expression = attrs.imageDrop;
            var accesor = $parse(expression);


            var onDragOver = function (e) {
                e.preventDefault();
                element.addClass("dragOver");
            };

            var onDragEnd = function (e) {
                e.preventDefault();
                element.removeClass("dragOver");
            };

            var placeImage = function (imageData) {
                accesor.assign(scope, imageData);
            };

            var resampleImage = function (imageData) {                       
                return resampler.resample(
                    imageData, element.width(),
                    element.height(), scope);
            };

            var loadFile = function (file) {
                fileReader
                    .readAsDataUrl(file, scope)
                    .then(resampleImage)
                    .then(placeImage);
            };


            element.bind("dragover", onDragOver)
                   .bind("dragleave", onDragEnd)
                   .bind("drop", function (e) {
                       onDragEnd(e);
                       loadFile(e.originalEvent.dataTransfer.files[0]);
                   });

            scope.$watch(expression, function () {
                element.attr("src", accesor(scope));
            });
        }
    };
});

Some of the code to ensure only images are being processed is omitted, but I do want to point out the  image resizing code. It’s wrapped by the resampler service below, which in turn uses Resampler.js from the post “100% Client Side Image Resizing”.

var resampler = function ($q) {

    var resample = function (imageData, width, height, scope) {
        var deferred = $q.defer();

        Resample(imageData, width, height, function (result) {

            scope.$apply(function () {
                deferred.resolve(result);
            });
        });

        return deferred.promise;

    };

    return {
        resample: resample
    };
};

module.factory("resampler", resampler);