[javascript] AngularJS Uploading An Image With ng-upload

I am trying to upload a file in AngularJS using ng-upload but I am running into issues. My html looks like this:

<div class="create-article" ng-controller="PostCreateCtrl">
        <form ng-upload method="post" enctype="multipart/form-data" action="/write" >
            <fieldset>
                <label>Category</label>
                <select name="category_id" class="">
                    <option value="0">Select A Category</option>
                    <?php foreach($categories as $category): ?>
                        <option value="<?= $category -> category_id; ?>"><?= $category -> category_name; ?></option>
                    <?php endforeach; ?>
                </select>

                <label>Title</label>
                <input type="text" class="title span5" name="post_title"
                       placeholder="A catchy title here..."
                       value="<?= $post -> post_title; ?>" />

                <label>Attach Image</label>
                <input type="file" name="post_image" />

                 <a href='javascript:void(0)'  class="upload-submit: uploadPostImage(contents, completed)" >Crop Image</a>

                <label>Body</label>
                <div id="container">
                <textarea id="mytextarea" wrap="off" name="post_content" class="span7" placeholder="Content..."><?= $post -> post_content; ?></textarea>
                </div>
                <div style='clear:both;'></div>
                <label>Preview</label>
                <div id='textarea-preview'></div>

            </fieldset>
            <div class="span7" style="margin: 0;">
                <input type="submit" class="btn btn-success" value="Create Post" />
            <input type="submit" class="btn btn-warning pull-right draft" value="Save as Draft" />
            </div>

        </form>
    </div>

And my js controller looks like this:

ClabborApp.controller("PostCreateCtrl", ['$scope', 'PostModel',
function($scope, PostModel) {

    $scope.uploadPostImage = function(contents, completed) {
        console.log(completed);
        alert(contents);
    }

}]);

The problem I am facing is when the crop image is hit and it executes uploadPostImage, it uploads the entire form. Not desired behavior but I can make it work. The big problem is in the js the function uploadPostImage 'contents' parameters is always undefined, even when the 'completed' parameter comes back as true.

The goal is to only upload an image for cropping. What am I doing wrong in this process?

The answer is


You can try ng-file-upload angularjs plugin (instead of ng-upload).

It's fairly easy to setup and deal with angularjs specifics. It also supports progress, cancel, drag and drop and is cross browser.

html

<!-- Note: MUST BE PLACED BEFORE angular.js-->
<script src="ng-file-upload-shim.min.js"></script> 
<script src="angular.min.js"></script>
<script src="ng-file-upload.min.js"></script> 

<div ng-controller="MyCtrl">
  <input type="file" ngf-select="onFileSelect($files)" multiple>
</div>

JS:

//inject angular file upload directives and service.
angular.module('myApp', ['ngFileUpload']);

var MyCtrl = [ '$scope', '$upload', function($scope, $upload) {
  $scope.onFileSelect = function($files) {
    //$files: an array of files selected, each file has name, size, and type.
    for (var i = 0; i < $files.length; i++) {
      var file = $files[i];
      $scope.upload = $upload.upload({
        url: 'server/upload/url', //upload.php script, node.js route, or servlet url
        data: {myObj: $scope.myModelObj},
        file: file,
      }).progress(function(evt) {
        console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
      }).then(function(response) {
        var data = response.data;
        // file is uploaded successfully
        console.log(data);
      });
    }
  };
}];

There's little-no documentation on angular for uploading files. A lot of solutions require custom directives other dependencies (jquery in primis... just to upload a file...). After many tries I've found this with just angularjs (tested on v.1.0.6)

html

<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>

Angularjs (1.0.6) not support ng-model on "input-file" tags so you have to do it in a "native-way" that pass the all (eventually) selected files from the user.

controller

$scope.uploadFile = function(files) {
    var fd = new FormData();
    //Take the first selected file
    fd.append("file", files[0]);

    $http.post(uploadUrl, fd, {
        withCredentials: true,
        headers: {'Content-Type': undefined },
        transformRequest: angular.identity
    }).success( ...all right!... ).error( ..damn!... );

};

The cool part is the undefined content-type and the transformRequest: angular.identity that give at the $http the ability to choose the right "content-type" and manage the boundary needed when handling multipart data.


        var app = angular.module('plunkr', [])
    app.controller('UploadController', function($scope, fileReader) {
        $scope.imageSrc = "";

        $scope.$on("fileProgress", function(e, progress) {
        $scope.progress = progress.loaded / progress.total;
        });
    });




    app.directive("ngFileSelect", function(fileReader, $timeout) {
        return {
        scope: {
            ngModel: '='
        },
        link: function($scope, el) {
            function getFile(file) {
            fileReader.readAsDataUrl(file, $scope)
                .then(function(result) {
                $timeout(function() {
                    $scope.ngModel = result;
                });
                });
            }

            el.bind("change", function(e) {
            var file = (e.srcElement || e.target).files[0];
            getFile(file);
            });
        }
        };
    });

    app.factory("fileReader", function($q, $log) {
    var onLoad = function(reader, deferred, scope) {
        return function() {
        scope.$apply(function() {
            deferred.resolve(reader.result);
        });
        };
    };

    var onError = function(reader, deferred, scope) {
        return function() {
        scope.$apply(function() {
            deferred.reject(reader.result);
        });
        };
    };

    var onProgress = function(reader, scope) {
        return function(event) {
        scope.$broadcast("fileProgress", {
            total: event.total,
            loaded: event.loaded
        });
        };
    };

    var getReader = function(deferred, scope) {
        var reader = new FileReader();
        reader.onload = onLoad(reader, deferred, scope);
        reader.onerror = onError(reader, deferred, scope);
        reader.onprogress = onProgress(reader, scope);
        return reader;
    };

    var readAsDataURL = function(file, scope) {
        var deferred = $q.defer();

        var reader = getReader(deferred, scope);
        reader.readAsDataURL(file);

        return deferred.promise;
    };

    return {
        readAsDataUrl: readAsDataURL
    };
    });



    *************** CSS ****************

    img{width:200px; height:200px;}

    ************** HTML ****************

    <div ng-app="app">
    <div ng-controller="UploadController ">
        <form>
        <input type="file" ng-file-select="onFileSelect($files)" ng-model="imageSrc">
                <input type="file" ng-file-select="onFileSelect($files)" ng-model="imageSrc2">
        <!--  <input type="file" ng-file-select="onFileSelect($files)" multiple> -->
        </form>

        <img ng-src="{{imageSrc}}" />
    <img ng-src="{{imageSrc2}}" />

    </div>
    </div>

In my case above mentioned methods work fine with php but when i try to upload files with these methods in node.js then i have some problem. So instead of using $http({..,..,...}) use the normal jquery ajax.

For select file use this

<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this)"/>

And in controller

$scope.uploadFile = function(element) {   
var data = new FormData();
data.append('file', $(element)[0].files[0]);
jQuery.ajax({
      url: 'brand/upload',
      type:'post',
      data: data,
      contentType: false,
      processData: false,
      success: function(response) {
      console.log(response);
      },
      error: function(jqXHR, textStatus, errorMessage) {
      alert('Error uploading: ' + errorMessage);
      }
 });   
};

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to angularjs

AngularJs directive not updating another directive's scope ERROR in Cannot find module 'node-sass' CORS: credentials mode is 'include' CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response WebSocket connection failed: Error during WebSocket handshake: Unexpected response code: 400 Print Html template in Angular 2 (ng-print in Angular 2) $http.get(...).success is not a function Angular 1.6.0: "Possibly unhandled rejection" error Find object by its property in array of objects with AngularJS way Error: Cannot invoke an expression whose type lacks a call signature

Examples related to angularjs-http

How do I POST urlencoded form data with $http without jQuery? Using success/error/finally/catch with Promises in AngularJS The 'Access-Control-Allow-Origin' header contains multiple values AngularJS not detecting Access-Control-Allow-Origin header? AngularJS Uploading An Image With ng-upload

Examples related to angularjs-fileupload

File Upload using AngularJS AngularJS Uploading An Image With ng-upload ng-model for `<input type="file"/>` (with directive DEMO) AngularJS: how to implement a simple file upload with multipart form?