65

Does there exist a good file uploader with good integration (a directive) for AngularJS?

I am looking for something that is easy to style and supports HTML5 drag and drop etc.

Someone will probably say that its easy to use an existing uploader and integrate it into AngularJS - to that I'll say: if its easy then someone should have done it already.

Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
mkoryak
  • 57,086
  • 61
  • 201
  • 257
  • http://jsfiddle.net/danielzen/utp7j/ Don't you know that Giuthub page that showcase some great example for basic process : https://github.com/angular/angular.js/wiki/JSFiddle-Examples – Spir Oct 19 '12 at 21:41
  • 2
    @Spir While it might work, that's not the best example of angular at work. It throws dependency injection out of the window, and also does heavy DOM manipulation and event binding inside of the controller. – Ben Lesh Oct 22 '12 at 00:00
  • 17
    +29, 27 starred. "closed as off topic". – Quibblesome Oct 17 '13 at 10:29
  • 7
    when i asked this question, there wasn't a file uploader for angular. So it wasn't off topic at that time, and it seems to be still pretty useful to people even now. – mkoryak Oct 19 '13 at 02:35
  • 3
    mkoryak - I'm quite sure that @Quibblesome is actually making a dig at the 'new' super-strict-and-thus-not-so-useful site policy, and not at your question. – david.barkhuizen Jan 06 '15 at 12:47

5 Answers5

47

I actually have rolled my own uploader once... but only because I didn't like any of the already made JQuery ones. Unfortunately that's proprietary and I can't post it on the internet... but... I can show you how to use just about any JQuery plugin from Angular:

Someone will probably say that its easy to use an existing uploader and integrate it into AngularJS - to that i'll say: if its easy then someone should have done it already.

Let's presume I have a jQuery plugin that works by selecting a div and calling pluginUploadCall() on it...

app.directive('myJqueryPluginUploader', function() {
   return {
      restrict: 'A',
      link: function(scope, elem, attr, ctrl) {
          // elem is a jQuery lite object
          // or a jQuery object if jQuery is present.
          // so call whatever plugins you have.
          elem.pluginUploadCall();
      }
   };
});

And here's how it would be used.

<div my-jquery-plugin-uploader></div>

Angular actually integrates really well with jQuery so any plugins that work in jQuery should work pretty easily in Angular. The only trickiness comes in when you want to keep Dependency Injection alive so you can keep your Angular App testable. JQuery isn't very good at DI, so you may have to jump through some hoops.

If you wanted to roll your own, I can tell you I did something like this:

app.directive('customUploader', function(){
    return {
       restrict: 'E',
       scope: {},
       template: '<div class="custom-uploader-container">Drop Files Here<input type="file" class="custom-uploader-input"/><button ng-click="upload()" ng-disabled="notReady">Upload</button></div>',
       controller: function($scope, $customUploaderService) {
          $scope.notReady = true;
          $scope.upload = function() {
             //scope.files is set in the linking function below.
             $customUploaderService.beginUpload($scope.files);
          };
          $customUploaderService.onUploadProgress = function(progress) {
             //do something here.
          };
          $customUploaderService.onComplete = function(result) {
             // do something here.
          };
       },
       link: function(scope, elem, attr, ctrl) {
          fileInput = elem.find('input[type="file"]');
          fileInput.bind('change', function(e) {               
               scope.notReady = e.target.files.length > 0;
               scope.files = [];
               for(var i = 0; i < e.target.files.length; i++) {
                   //set files in the scope
                   var file = e.target.files[i];
                   scope.files.push({ name: file.name, type: file.type, size: file.size });
               }
          });
       }
});

Where $customUploaderService would be a custom service you create with Module.factory() that uses $http to post the files and check the progress on the server.

I know that's vague, and I'm sorry that's all I can provide, but I hope that helps.

EDIT: The drag and drop file upload is a bit of a trick of CSS, BTW... for Chrome and FF, what you do is put the in a containing div... then do something like this:

<div class="uploadContainer">Drop Files Here<input type="file"/></div>
div.uploadContainer {
   position: relative;
   width: 600px;
   height: 100px;
}

div.uploadContainer input[type=file] {
   visibility: hidden;
   position: absolute;
   top: 0;
   bottom: 0;
   left: 0;
   right: 0;
}

... now anything you drop on that div will really be dropped on the file upload, and you can make the div look like whatever you want.

Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
  • thanks for answering pretty much all of my angluarjs questions so far. Ill accept them as soon as i work through them :) This info is really good since im just starting out and there isnt much available – mkoryak Oct 22 '12 at 02:49
  • 1
    lol... I didn't realize I'd done that. I really like Angular and I think the documentation is lacking so I'm trying to help out. – Ben Lesh Oct 22 '12 at 02:51
  • 3
    angular seems really interesting. i wish i didnt haven to forget all i know about writing client side code to use it... but i guess thats how really interesting things usually are. – mkoryak Oct 22 '12 at 02:57
  • 7
    Well, in my mind the problem is that for more than a decade now people haven't treated JavaScript with much respect as a programming language. Which is why we haven't seen too many frameworks like this until recently. I mean, I remember the times when JS was avoided at all costs because things just didn't work between browsers. Or hell, people just had it turned off. – Ben Lesh Oct 22 '12 at 12:57
  • ok, one more question. how do you post the files? $http uses ajax and you cant ajax files. i can get around it using iframes, or a file uploader lib, but is there actually an angular way to do file uploads without having to resort to those things (that works on ie7+) – mkoryak Oct 23 '12 at 02:30
  • now that I think about it, I think I ended up just creating a service that wrapped jQuery's $.ajax because I had a hard time getting $http to play nice with multipart/form-data. What I wrapped was basically the same thing you'll find [on the answer to this question](http://stackoverflow.com/questions/166221/how-can-i-upload-files-asynchronously-with-jquery) – Ben Lesh Oct 23 '12 at 04:02
  • beautiful. Thanks, been looking to avoid jQuery if I can and this gives me a great place to start (if not pretty much finish?). – Quibblesome Oct 17 '13 at 10:37
  • Note: fileInput = elem.find('input[type="file"]'); this doesn't work in angular, find is limited to tag names only, so it works like this: elem.find('input'); – transient_loop Dec 10 '13 at 02:43
  • @fablife: Please notice this line: "I can show you how to use just about any JQuery plugin from Angular" ... If you've referenced JQuery *before* you referenced Angular, find will work as shown because the element will be a jquery object. – Ben Lesh Dec 10 '13 at 14:24
25

You can give AngularJS.ngUpload a try.

It's an HTML5-free solution that uses an invisible iFrame for file upload. Since it does not rely on HTML5, it works across browser!

Sample code:

<form action='/server/upload/handler' ng-upload="callbackFunction">
   <!-- other form inputs goes here -->
   <input type="file" name="anyEasyName" />
   <input type="submit" class="upload-submit" value="Submit" />
</form>
<div>{{uploadReport}}</div>

Any html element that supports a click event can be used to submit a form marked with the ngUpload directive, only that such elements must be marked with the upload-submit css class (as the case with the input[type=submit] above.

The example below uses a styled div to submit the form.

<form action='/server/upload/handler' ng-upload="callbackFunction">
   <!-- other form inputs goes here -->
   <input type="file" name="anyEasyName" />
   <div style="cursor: pointer; padding: 5px" class="upload-submit">Submit</div>
</form>
<div>{{uploadReport}}</div>

You can make your /server/upload/handler spit a valid url, so that {{uploadReport}} can be used to set the src of an <img> tag, like so:

<img ng-src={{uploadReport}} />

and see the uploaded image appear immediately!

The ngController for the above examples is:

var UploadCtrl = function ($scope) {
     $scope.callbackFunction = function(contentOfInvisibleFrame) {
         $scope.uploadReport = contentOfInvisibleFrame;
     }
}

The ngUpload directive can be registered with your AngularJS application module viz:

var mainApp = angular.module('MainApp', ["ngUpload", ...]);

and added to your document as:

<html ng-app="MainApp">

</html>

AngularJS.ngUpload works in the context of a ngController; and such you can have as many uploaders as possible in a single ngController. For example:

<form action='/server/upload/handler' ng-upload="callbackFunction1">
   <!-- other form inputs goes here -->
   <input type="file" name="anyEasyName" />
   <input type="submit" class="upload-submit" value="Submit" />
</form>
Server response: {{uploadReport1}}

<form action='/server/upload/handler' ng-upload="callbackFunction2">
   <!-- other form inputs goes here -->
   <input type="file" name="anotherEasyName" />
   <input type="submit" class="upload-submit" value="Submit" />
</form>
Server response: {{uploadReport2}}

to be served by:

var UploadCtrl = function ($scope) {
     $scope.callbackFunction1 = function(contentOfInvisibleFrame) {
         $scope.uploadReport1 = contentOfInvisibleFrame;
     }

     $scope.callbackFunction2 = function(contentOfInvisibleFrame) {
         $scope.uploadReport2 = contentOfInvisibleFrame;
     }
}

A NodeJS-based upload handler demo of this directive can be found at http://ng-upload.eu01.aws.af.cm.

An ASP.Net MVC and NodeJS sample codes can be found on the project website at github.com/twilson63/ngUpload/tree/master/examples

Hope this helps.

12

I have put together a simple/light angular directive with polyfill for browsers not supporting HTML5 FormData here:

https://github.com/danialfarid/ng-file-upload

You can send other model object along with the file to the server. Here is the demo page:

http://angular-file-upload.appspot.com/

<script src="angular.min.js"></script>
<script src="ng-file-upload.js"></script>

<div ng-controller="MyCtrl">
  <input type="text" ng-model="myModelObj">
  <input type="file" ngf-select ng-model="files" >
</div>

controller:

Upload.upload({
    url: 'my/upload/url',
    data: {myObj: $scope.myModelObj},
    file: $scope.files
  }).then(function(data, status, headers, config) {
    // file is uploaded successfully
    console.log(data);
  }); 
danial
  • 4,058
  • 2
  • 32
  • 39
7

If you want to handle multiple files, try this

jQuery File Upload Angularjs wrap from the original author (blueimp)

I think it is the most powerful uploader so far.

maxisam
  • 21,975
  • 9
  • 75
  • 84
5

I recently wrote a directive that supports native multiple file uploads.

Example usage:

<lvl-file-upload
    auto-upload='false'
    choose-file-button-text='Choose files'
    upload-file-button-text='Upload files'
    upload-url='http://localhost:3000/files'
    max-files='10'
    max-file-size-mb='5'
    get-additional-data='getData(files)'
    on-done='done(files, data)'
    on-progress='progress(percentDone)'
    on-error='error(files, type, msg)'/>

You can find the code on github, and the documentation on my blog

Jason
  • 15,915
  • 3
  • 48
  • 72