20

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);
nielsv
  • 6,540
  • 35
  • 111
  • 215
  • Hi @nielsv, possibilities are abundant. Check if you are loading all the required JS files appropriately and in the proper order. It would be good if you can create your code either in Plunker or JSFiddle, so that we can help you. – Vinod Tigadi Jun 10 '15 at 14:40
  • Could you post your `submit` method? – Jaanus Varus Jun 17 '15 at 09:11
  • I've updated my topic. Could you take a look? – nielsv Jun 17 '15 at 09:20
  • Could you also post how you setup your angular module and controller? Just the declaration would suffice. – Jaanus Varus Jun 17 '15 at 09:31
  • Check out Angular UI Utils - File uploader as an alternative approach http://angular-ui.github.io/ui-utils/ – Alex Jun 17 '15 at 10:16
  • @nielsv updated the answer with missing steps and more explanation. Demo [Plunkr](http://plnkr.co/edit/4Bcs61qmdTZ47Rewpgpn?p=preview) also updated with comments and image preview functionality – Prayag Verma Jun 22 '15 at 16:44
  • 1
    http://fineuploader.com/demos You can use fineuploader. – ajay Jun 23 '15 at 07:10
  • Do you have any particular reason to use jQuery File Upload? I have been using [ng-file-uplad](https://github.com/danialfarid/ng-file-upload) in a couple of projects and it does things in a more "angular" way. – pasine Jun 23 '15 at 09:58

2 Answers2

9

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

Prayag Verma
  • 5,581
  • 3
  • 30
  • 56
  • Guys, i've done everything just like described above but still have an error '$element.fileupload is not a function' in FileUploadController when file-upload directive is compiled. Could this be due to i still missing something or there are already some incompatibilities of angular and jquery-file-upload? – Aleksei Yerokhin May 29 '16 at 13:52
  • I solved it by attaching the jquery.ui.widget.js library – fabian enos Jun 07 '18 at 17:44
5

I'm taking a shot here. What causes the submit() not to function is the fact that the third party module which declares the file-upload directive is not available for your app. submit() should be part of the scope for the controller used by the file-upload directive.

Try changing app.js:

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

Jaanus Varus
  • 3,508
  • 3
  • 31
  • 49