32

I've a situation where I've a form in which I've a row where I've two text fields entries and I've to upload a file for that row and this kind of rows can be 'N' and then there is a master files that can be entered for whole form while these are some part of the form and I've to submit all these files at once on clicking a save button.

I'm kind of stuck with ng-upload it needs an api call, and I really can't have more than one api call for this form. The sample html code is below :

<!DOCTYPE html>
<html>

<head>
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>

  <form editable-form name="xyzForm" ng-submit="create(model)">
    <label>Tags: </label><input class="col-xs-12 col-md-12" ng-model="model.tags" type="text" name="Tags">
    <label>Notes: </label> <input class="col-xs-12 col-md-11" ng-model="model.notes" type="text" name="notes">
    <table class=" col-xs-3 col-md-11 table" border="1px solid red;">
      <thead>
        <tr>
          <th>Product</th>
          <th>Manufacturer</th>
          <th>Location</th>
          <th>Specification</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="itemRow in item.singleItem">
          <td><input type="text" class="xdTextBox" name="itemRow.name" ng-model="model.itemRow[$index].name" /></td>
          <td><input type="text" class="xdTextBox" name="itemRow.manufacturer" ng-model="model.itemRow[$index].manufacturer" /></td>
          <td><input type="text" class="xdTextBox" name="itemRow.location" ng-model="model.itemRow[$index].location" /></td>
          <td><i class="pull-left glyphicon glyphicon-upload"><input type="file" name="itemRow.doc" ng-model="model.itemRow[$index].doc" multiple=false></i></td>
          <td><i class="pull-left glyphicon glyphicon-remove"></i></td>
        </tr>

      </tbody>
      </span>
    </table>

    <label>Product Spec: </label><input type="file" ng-model="prefabdoc" multiple="true" ngf-maxsize="15000000" />
  </form>

</body>

</html>
Prashant Pokhriyal
  • 3,125
  • 4
  • 25
  • 30
J Bourne
  • 1,337
  • 2
  • 13
  • 33
  • 2
    Is using `https://github.com/danialfarid/ng-file-upload` not an option? – Rahat Mahbub Jul 20 '15 at 11:11
  • 1
    I'm using the same thing already in my code, but confused how to implement this at once for multiple files and that too with different set of files at different places in the form – J Bourne Jul 20 '15 at 11:13
  • This is also a good resource on youtube: https://www.youtube.com/watch?v=sm84cTdkd80 – maruf najm Feb 02 '17 at 11:24
  • @Bran Stark - I am trying to achieve exactly what you have mentioned above in your problem statement.I am new to Angular Js and I found accepted answer below somewhat close but still not clear how can I implement for the problem statement as you mentioned above.Can you please share code which you implemented,it would be really helpful..Thanks. – javaguy Mar 07 '17 at 05:59
  • Did the accepted answer work for you @BranStark? – seanmus Sep 13 '18 at 23:24
  • Yes, it worked for me @seanmus – J Bourne Sep 18 '18 at 01:27

3 Answers3

50

Here is file value binding directive example ..

http://plnkr.co/edit/B13t84j5IPzINMh1F862?p=preview

Js code is:

var app = angular.module('myApp', []);

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
  $scope.files = []; 
  $scope.upload=function(){
    alert($scope.files.length+" files selected ... Write your Upload Code"); 

  };
});


app.directive('ngFileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var model = $parse(attrs.ngFileModel);
            var isMultiple = attrs.multiple;
            var modelSetter = model.assign;
            element.bind('change', function () {
                var values = [];
                angular.forEach(element[0].files, function (item) {
                    var value = {
                       // File Name 
                        name: item.name,
                        //File Size 
                        size: item.size,
                        //File URL to view 
                        url: URL.createObjectURL(item),
                        // File Input Value 
                        _file: item
                    };
                    values.push(value);
                });
                scope.$apply(function () {
                    if (isMultiple) {
                        modelSetter(scope, values);
                    } else {
                        modelSetter(scope, values[0]);
                    }
                });
            });
        }
    };
}]);

Html Code is:

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link href="style.css" rel="stylesheet" />
    <script data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js" 
        data-require="angular.js@1.4.x"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>
    <input type="file" ng-file-model="files" multiple />
    <button type="button" ng-click="upload()">Upload</button>

    <p ng-repeat="file in files">
      {{file.name}}
    </p>
  </body>

</html>
Xtreme Biker
  • 28,480
  • 12
  • 120
  • 195
Saltuk
  • 1,149
  • 9
  • 12
  • how to limit the number of files uploadable, like maximum 5 files needs to be uploaded once – codelearner Jun 21 '16 at 06:02
  • you can do it on $scope.upload method $scope.file.length>5 and dont forget this code is only explains how to bind angularjs and fileinput ... read about file uploading https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest and replace with the alert code.. i hope this helps .. – Saltuk Jun 22 '16 at 00:21
  • So, now the files are available in controller through the "`values`" array right? As `values[i]`. Why are you accessing it using "`files`" variable in your upload function? – Tuhina Singh May 19 '17 at 02:48
  • outputs something like this in php `[additionals] => [object File],[object File]` – Ashish Dec 19 '17 at 13:40
  • the object of this directive supports browser base view before upload show file , name size validation etc. you can access raw file by value._file – Saltuk Dec 19 '17 at 22:28
5

If you don't care about browsers less than IE 9. Then you can follow this post and construct a FormData object in your ng-submit event. This will create a form/multipart and might not be what your looking for but it does the trick.

Community
  • 1
  • 1
soaP
  • 91
  • 5
  • Thank You for pointing out this post. I've two different type of form files here one or more for each product item and for whole product itself one or more files.But I like the way the multiform is implemented here. – J Bourne Jul 20 '15 at 11:11
  • It should be possible to combine the two type of form files into one FormData object just before you issue the post request. Haven't tried it myself though :)! – soaP Jul 20 '15 at 11:27
  • yeah sounds good, I'm trying this right now, let me see if I can get all the file data onto server side. – J Bourne Jul 20 '15 at 11:29
  • Im kind of stuck, I want to do the $http post in another method, while Im trying to populate file data into fd from the above link, and file data does not display. FormData is empty even after appending the file – J Bourne Jul 21 '15 at 10:34
  • 1
    I'm trying to do $http.post in a different method, I want to return this FormData there, but this comes empty? What am I doing wrong, filedatais not going into FormData here is the code http://pastebin.com/DM4LNWkF – J Bourne Jul 21 '15 at 10:37
1

from saltuk's answer above there is a small change for the code to work

    var modelSetter = model.assign;
        element.bind('change', function () {
            var values = [];
            angular.forEach(element[0].files, function (item) {
                var value = {...
                }
            }
        }

the array var should be defined above before forEach function

    var modelSetter = model.assign;
    element.bind('change', function () {
        var values = [];
        angular.forEach(element[0].files, function (item) {
            var value = {...
            }
        }
    }
Community
  • 1
  • 1
madhu reddy
  • 316
  • 3
  • 6