Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the Nylon Technology 10 Year Anniversary (Jul. 2007) with:

Using Plupload To Upload Files In AngularJS

By Ben Nadel on

Plupload is a very powerful JavaScript library for local file access and file upload. I've been using it for years and absolutely love it. But, I haven't yet done much with it in AngularJS. Sure, I've uploaded files; but, I haven't leveraged much of the Plupload 2.x features in my AngularJS applications. As such, I wanted to start experimenting with some more robust integrations between Plupload and AngularJS.


 
 
 

 
 
 
 
 

View this project on my GitHub account.

There's a significant amount of code in this project, so I don't know how much I actually want to explain in this blog post directly. To me, the most interesting part of this experiment is that I used both a primary uploader (a Plupload "Uploader" instance) and a secondary dropzone (a mOxie "FileDrop" instance). Both of these were encapsulated within AngularJS directives and cross-communicated using the native event system provided by the AngularJS scope chain.

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Using Plupload To Upload Files In AngularJS
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="css/app.css"></link>
  • </head>
  • <body ng-app="PluploadApp">
  •  
  • <h1>
  • Using Plupload To Upload Files In AngularJS
  • </h1>
  •  
  • <div ng-controller="ImagesController">
  •  
  • <!--
  • This is the main image uploader.
  • --
  • NOTE: The uploader directive exposes a "queue" value that can be used to
  • render the list of files inside the uploader.
  • -->
  • <div bn-image-uploader class="m-uploader">
  • <div ng-switch="!! queue.length" class="dropzone">
  •  
  • <!-- Active upload file queue. -->
  • <ul ng-switch-when="true" class="queue">
  • <li ng-repeat="item in queue track by item.id" class="item">
  •  
  • {{ item.percent }}%
  •  
  • </li>
  • </ul>
  •  
  • <!-- Non-active upload instructions. -->
  • <div ng-switch-when="false" class="instructions">
  •  
  • Select Files &mdash; or &mdash; Drag-n-Drop Files
  •  
  • </div>
  •  
  • </div>
  • </div>
  •  
  • <!--
  • This list shows the uploaded images; but, it also serves as a secondary
  • dropzone into which the user can drop new images in a targeted location.
  • -->
  • <div bn-image-list-uploader class="m-images">
  •  
  • <ul class="images">
  • <li ng-repeat="image in images track by image.id" class="image">
  •  
  • <div class="thumbnail">
  • <img ng-src="{{ image.url }}" />
  • </div>
  •  
  • <div class="name">
  • {{ image.clientFile }}
  • </div>
  •  
  • <a ng-click="deleteImage( image )" class="delete">&times;</a>
  •  
  • <!--
  • This is here to make the insert into the list much easier.
  • Without these handles, you have to actually do "math" to figure
  • out where to put the indicator. I've opted for simplicity over
  • tidy semantics.
  • -->
  • <div class="drop-indicator">
  • <div class="left"><br /></div>
  • <div class="right"><br /></div>
  • </div>
  •  
  • </li>
  • </ul>
  •  
  • </div>
  •  
  • </div>
  •  
  •  
  • <!-- Vendor Scripts. -->
  • <script type="text/javascript" src="vendor/plupload/plupload.full.min.js"></script>
  • <!-- Dev script for when you need to see the inner worksing of Plupload. -->
  • <!--
  • <script type="text/javascript" src="vendor/plupload/moxie.js"></script>
  • <script type="text/javascript" src="vendor/plupload/plupload.dev.js"></script>
  • -->
  • <script type="text/javascript" src="vendor/jquery/jquery-2.1.1.min.js"></script>
  • <script type="text/javascript" src="vendor/angular/angular-1.2.18.min.js"></script>
  •  
  • <!-- Angular Scripts. -->
  • <script type="text/javascript" src="js/main.js"></script>
  • <script type="text/javascript" src="js/images/image-uploader-directive.js"></script>
  • <script type="text/javascript" src="js/images/image-list-uploader-directive.js"></script>
  • <script type="text/javascript" src="js/images/images-controller.js"></script>
  • <script type="text/javascript" src="js/images/images-service.js"></script>
  • <script type="text/javascript" src="js/services/natural-sort-service.js"></script>
  • <script type="text/javascript" src="js/services/plupload-service.js"></script>
  •  
  • </body>
  • </html>

The two Plupload-related directives in the above code are:

  • bn-image-uploader
  • bn-image-list-uploader

The first handles the primary uploader that supports both "drag-n-drop" and "click-to-select" interactions. This is the instance that actually takes care of POSTing the files to the server.

The second directive handles "drag-n-drop" interaction for the list. But, not only does it allow files to be dropped, it allows them to be dropped in a specific area of the list. This latter aspect, the targeted drop, was what took about 90% of the effort of this entier experiment. Overall, this project took about 2 full days to build and the majority of that time was spent trying to figure out how to get the drop-indicators to show in the right place in the list during the drag events.

If you haven't dealt with drag events yet, they are a complete nightmare! Not only are they a quirky in a cross-browser sense, they also fire in counter-intuitive ways that make them dang-near impossible to work with. If you look at my second directive, you'll see that I basically fallback to using a timer that constantly resets itself. This is terribly inefficient; but, after hour and hours of dead-ends and outlier situations, this was the only thing that I could get to work in all the browsers (including IE 10, which now supports drag-n-drop).

As I move forward with these Plupload / AngularJS experiments, I'll keep trying to think about how to best encapsulate things. You might be tempted to simply wrap Plupload in a generic directive; but I'm not sure that it's actually feasible to do that. As the interactions become more granular, and cross-instance communication is required, I think you'd have to start jumping through unnecessarily-complex hoops in an effort to keep Plupload completely back-boxed.

And, the truth is, Plupload is already encapsulated behind the various mOxie components. My directives aren't breaking that encapsulation - they are doing what directives do - piping JavaScript events into the scope.

Hopefully more fun and interesting stuff to come!




Reader Comments

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.