jQuery File Upload in AngularJS

nielsv picture nielsv · Jun 10, 2015 · Viewed 8.5k times · Source

I'm trying to upload a file with jQuery File Upload in combination with angularJS.

I have a multistep form, this are 2 steps of my multistep form:

<div ng-switch="step">
    <div ng-switch-when="1">
        <h1>Identity</h1>
        <form name="steponeForm" data-file-upload="options" enctype="multipart/form-data" novalidate autocomplete="off">
            <input type="submit" ng-click="next(steponeForm.$valid)" value="next" /><br>

            <span class="button fileinput-button" ng-class="{disabled: disabled}">
                <input type="file" id="fileupload" name="files[]" multiple="" >
            </span>
            <button type="button" class="btn btn-primary start" data-ng-click="submit()">
                <span>Start upload</span>
            </button>

            <input ng-model="application.lastName" string-pattern required type="text" placeholder="{{ 'Last name'|translate }} *" name="appname" id="appname" />
            <div ng-show="steponeForm.$submitted || steponeForm.appname.$touched">
                <div class="error" ng-show="steponeForm.appname.$error.required">Last name is required.</div>
                <div class="error" ng-show="steponeForm.appname.$error.stringPattern">Doesn't look like a text.</div>
            </div>

            <input type="submit" ng-click="next(steponeForm.$valid)" value="next" />
        </form>
    </div>

    <div ng-switch-when="2">
        <h1>Studies</h1>
        <form name="steptwoForm" novalidate autocomplete="off">
            <input type="submit" ng-click="previous()" value="previous" />
            <input type="submit" ng-click="next(steptwoForm.$valid)" value="next" />

            <fieldset class="input-group">
                <legend translate>Lower secondary studies</legend>
                <em>Last obtained degree</em>

                <input ng-model="application.LowerSecondaryStudies.degreeTitle" type="text" placeholder="Degree Title" name="moreLowerSecondaryStudies-degreetitle" id="lwsappdegreetitle" />
                <input ng-model="application.LowerSecondaryStudies.educationAuthority" type="text" placeholder="Education authority" name="moreLowerSecondaryStudies-educationauthority" id="lwsappeducationauthority" />
                <input ng-model="application.LowerSecondaryStudies.graduationYear" style="padding: 0.5278em; width: 100%;" type="number" min="1960" max="2015" value="2015" placeholder="Graduation year" name="moreLowerSecondaryStudiesgraduationyear" id="lwsappgraduationyear" />
                <div ng-show="steptwoForm.$submitted || steptwoForm.moreLowerSecondaryStudiesgraduationyear.$touched">
                    <div class="error" ng-show="steptwoForm.moreLowerSecondaryStudiesgraduationyear.$error.number">Must be valid year.</div>
                </div>
            </fieldset>

            <input type="submit" ng-click="previous()" value="previous" />
            <input type="submit" ng-click="next(steptwoForm.$valid)" value="next" />
        </form>
    </div>
</div>

In my custom js file I have:

jQuery('#fileupload').fileupload({
    dataType: 'json'
});

In my controller (angularjs) I have:

$scope.options = {
    maxFileSize: 5000000,
    type: "POST",
    acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i
};

As you can see I call the submit() function on Start upload, but that doesn't trigger anything. I'm also not getting any errors in my browser console. What am I missing?

UPDATE:

I don't have a submission function in my controller.js . I thought this was standard added with jquery.fileupload-angular.js . They also didn't specify a submit function here in the example jQuery fileupload + angularjs.

The declaration of my module in app.js:

var app = angular.module('dxs-vkgroupApp', ['ngRoute', 'gettext'])
.config(function($routeProvider, $httpProvider, $locationProvider){
    // send all requests payload as query string
    $httpProvider.defaults.transformRequest = function(data){
        if (data === undefined) {
            return data;
        }
        return jQuery.param(data);
    };

    // set all post requests content type
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';

    // all routes
    $routeProvider
        .when('/edit.php/submissions/', {
            templateUrl: viewsPath.views + 'submissions.html',
            controller: 'SubmissionOverviewController'
        })
        .when('/edit.php/submission/show/:fid/', {
            templateUrl: viewsPath.views + 'submission.html',
            controller: 'ShowSubmissionController'
        })
        .when('/edit.php/submission/delete/:fid/', {
            templateUrl: viewsPath.views + 'delete-submission.html',
            controller: 'DeleteSubmissionController'
        })
        .when('/wp-admin/', {
            controller: 'RouteDeciderController',
            template: '<div ng-include="getTemplateUrl()"></div>'
        })
        .when('/:l/submission/new/:jid', {
            templateUrl: viewsPath.views + 'new-submission.html',
            controller: 'StepController'
        })
        .when('/:l/projects/', {
            templateUrl: viewsPath.views + 'projects.html',
            controller: 'ProjectsOverviewController'
        }).otherwise({
            controller: 'RouteDeciderController',
            template: '<div ng-include="getTemplateUrl()"></div>'
        });

    $locationProvider.html5Mode(true);
})
.run(function (gettextCatalog, $location) {
    var curr_path = $location.path();
    var result = curr_path.split("/");
    var language = result[1];

    gettextCatalog.setCurrentLanguage(language);
    gettextCatalog.debug = true;
});

In my controller.js I have amongst other things:

/**
 * Deals with advancing, going back or finishing the multi step form
 *
 * @param $scope
 * @param $http
 * @param $routeParams
 * @constructor
 */
function StepController($scope, $http, $routeParams)
{
    // inits
    $scope.application = {};
    $scope.application.children = [];

    // counters
    $scope.childCounter = 0;
    $scope.moreLowerSecondaryStudiesCounter = 0;
    $scope.moreHigherSecondaryStudiesCounter = 0;
    $scope.moreHigherShortTermEducationCounter = 0;
    $scope.moreHigherLongTermEducationCounter = 0;
    $scope.moreAdditionalStudiesSpecialtyCounter = 0;
    $scope.moreAdditionalStudiesThesisCounter = 0;
    $scope.languageCounter = 0;
    $scope.experienceCounter = 0;

    // select options
    $scope.languageOptions = ['--select--', 'very good', 'good', 'notions', 'no notion'];

    // languages
    // @todo make the default list dynamic instead of hardcoded (problem is, the variable expressions wont get accepted in the select attributes)
    //$scope.languages = ['dutch', 'french', 'english', 'german'];

    $scope.job_id = $routeParams.jid;

    $scope.step = 1;

    $scope.noneSelected = function (type) {
        switch(type)
        {
            case 'appcontact':
                    if(!$scope.application.contact){
                        return true;
                    }
                    else
                    {
                        return !($scope.application.contact.relations || $scope.application.contact.employees || $scope.application.contact.jobad || $scope.application.contact.website || $scope.application.contact.other)
                    }
                break;
            case 'appworklocation':
                    if(!$scope.application.worklocation){
                        return true;
                    }
                    else
                    {
                        return !($scope.application.worklocation.roeselare || $scope.application.worklocation.brussel || $scope.application.worklocation.merelbeke)
                    }
                break;
        }
    };

    $scope.next = function($valid){
        if(!$valid)
        {
            $scope.step = $scope.step;
        }
        else if($scope.step == 2)
        {
            $scope.inputgrouperror = false;

            // special check for 6 input groups (input fields)
            if(check())
            {
                $scope.step += 1;
            }
            else
            {
                $scope.inputgrouperror = true;
                $scope.step = $scope.step;
            }
        }
        else
        {
            $scope.step += 1;
        }

        window.scrollTo(0,0);
    };

    $scope.previous = function(){
        $scope.step -= 1;
        window.scrollTo(0,0);
    };

    $scope.finish = function($valid){
        if(!$valid)
        {
            $scope.step = $scope.step;
        }
        else
        {
            $http.post('new-submission', { id: $scope.job_id, application: $scope.application })
                .success(function(data, status, headers, config){
                    window.location.href = data.redirect_url;
                });
        }
    };
}

function check() {
    var check = false;
    jQuery.each(jQuery('fieldset.input-group'), function () { //loops through all fieldsets
        if (!check) { //are there no fieldsets with  3 filled input elements then check is false so far
            check = jQuery(this).find('input:text,[type^="number"]').filter(function () { //checks whether inputs are filled
                return this.value != "";
            }).length > 2; //If filled inputs > 2 -> check = true
        }
    });

    return check;
}

angular.module('dxs-vkgroupApp')
    .controller('StepController', StepController);

Answer

Prayag Verma picture Prayag Verma · Jun 17, 2015

Firstly include all the basic files for jQuery File Upload Plugin

<!-- jQuery File Upload Stylesheets -->
<link rel="stylesheet" href="jquery.fileupload.css" />
<link rel="stylesheet" href="jquery.fileupload-ui.css" />

<!-- The Load Image plugin is included for image preview and resizing functionality -->
<script src="load-image.all.min.js"></script>
<!-- The Canvas to Blob plugin is included for image resizing functionality -->
<script src="canvas-to-blob.min.js"></script>
<!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
<script src="jquery.iframe-transport.js"></script>
<!-- The basic File Upload plugin -->
<script src="jquery.fileupload.js"></script>
<!-- The File Upload processing plugin -->
<script src="jquery.fileupload-process.js"></script>
<!-- The File Upload image preview & resize plugin -->
<script src="jquery.fileupload-image.js"></script>
<!-- The File Upload validation plugin -->
<script src="jquery.fileupload-validate.js"></script>
<!-- The File Upload Angular JS module -->
<script src="jquery.fileupload-angular.js"></script>

Now as @Discosultan mentioned , include the blueimp.fileupload module in the app.js file

var app = angular.module('dxs-vkgroupApp', ['blueimp.fileupload', 'ngRoute', 'gettext'])

Make sure to mention URL to which you have to upload the image to , either in the form tag's action attribute

<form action="//jquery-file-upload.appspot.com/" file-upload="options" 
enctype="multipart/form-data" name="steponeForm" novalidate autocomplete="off">
....
  <!-- Add Files Button -->
  <span class="btn btn-success fileinput-button">
    <i class="glyphicon glyphicon-plus"></i>
  <span>Add files...</span>
  <input type="file" name="files" multiple="" ng-disabled="disabled">
  </span>

  <!-- Start Upload Button -->
  <button type="button" class="btn btn-primary start" ng-click="submit()">
    <i class="glyphicon glyphicon-upload"></i>
    <span>Start upload</span>
  </button>
....
</form>

or in the options object passed to file-upload directive

$scope.options = {
    maxFileSize: 5000000,
    type: "POST",
    url:'//jquery-file-upload.appspot.com/',
    acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i
};

Also a thing to note is , the submit() mentioned in the HTML template is implemented by the Plugin itself and doesn't need to be overridden by us in the controller

Updated the Plunkr to include image preview before uploading and progress of file upload as implemented in plugin demo