Advertisement
Advertisement


ng-model for `` (with directive DEMO)


Question

I tried to use ng-model on input tag with type file:

<input type="file" ng-model="vm.uploadme" />

But after selecting a file, in controller, $scope.vm.uploadme is still undefined.

How do I get the selected file in my controller?

2018/06/27
1
285
6/27/2018 6:21:45 PM

Accepted Answer

I created a workaround with directive:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            element.bind("change", function (changeEvent) {
                var reader = new FileReader();
                reader.onload = function (loadEvent) {
                    scope.$apply(function () {
                        scope.fileread = loadEvent.target.result;
                    });
                }
                reader.readAsDataURL(changeEvent.target.files[0]);
            });
        }
    }
}]);

And the input tag becomes:

<input type="file" fileread="vm.uploadme" />

Or if just the file definition is needed:

.directive("fileread", [function () {
    return {
        scope: {
            fileread: "="
        },
        link: function (scope, element, attributes) {
            element.bind("change", function (changeEvent) {
                scope.$apply(function () {
                    scope.fileread = changeEvent.target.files[0];
                    // or all selected files:
                    // scope.fileread = changeEvent.target.files;
                });
            });
        }
    }
}]);
2014/01/12
328
1/12/2014 3:53:42 PM

I use this directive:

angular.module('appFilereader', []).directive('appFilereader', function($q) {
    var slice = Array.prototype.slice;

    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, element, attrs, ngModel) {
                if (!ngModel) return;

                ngModel.$render = function() {};

                element.bind('change', function(e) {
                    var element = e.target;

                    $q.all(slice.call(element.files, 0).map(readFile))
                        .then(function(values) {
                            if (element.multiple) ngModel.$setViewValue(values);
                            else ngModel.$setViewValue(values.length ? values[0] : null);
                        });

                    function readFile(file) {
                        var deferred = $q.defer();

                        var reader = new FileReader();
                        reader.onload = function(e) {
                            deferred.resolve(e.target.result);
                        };
                        reader.onerror = function(e) {
                            deferred.reject(e);
                        };
                        reader.readAsDataURL(file);

                        return deferred.promise;
                    }

                }); //change

            } //link
    }; //return
});

and invoke it like this:

<input type="file" ng-model="editItem._attachments_uri.image" accept="image/*" app-filereader />

The property (editItem.editItem._attachments_uri.image) will be populated with the contents of the file you select as a data-uri (!).

Please do note that this script will not upload anything. It will only populate your model with the contents of your file encoded ad a data-uri (base64).

Check out a working demo here: http://plnkr.co/CMiHKv2BEidM9SShm9Vv

2015/03/17

How to enable <input type="file"> to work with ng-model

Working Demo of Directive that Works with ng-model

The core ng-model directive does not work with <input type="file"> out of the box.

This custom directive enables ng-model and has the added benefit of enabling the ng-change, ng-required, and ng-form directives to work with <input type="file">.

angular.module("app",[]);

angular.module("app").directive("selectNgFiles", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
});
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" select-ng-files ng-model="fileArray" multiple>

    <code><table ng-show="fileArray.length">
    <tr><td>Name</td><td>Date</td><td>Size</td><td>Type</td><tr>
    <tr ng-repeat="file in fileArray">
      <td>{{file.name}}</td>
      <td>{{file.lastModified | date  : 'MMMdd,yyyy'}}</td>
      <td>{{file.size}}</td>
      <td>{{file.type}}</td>
    </tr>
    </table></code>
    
  </body>

2020/06/20

This is an addendum to @endy-tjahjono's solution.

I ended up not being able to get the value of uploadme from the scope. Even though uploadme in the HTML was visibly updated by the directive, I could still not access its value by $scope.uploadme. I was able to set its value from the scope, though. Mysterious, right..?

As it turned out, a child scope was created by the directive, and the child scope had its own uploadme.

The solution was to use an object rather than a primitive to hold the value of uploadme.

In the controller I have:

$scope.uploadme = {};
$scope.uploadme.src = "";

and in the HTML:

 <input type="file" fileread="uploadme.src"/>
 <input type="text" ng-model="uploadme.src"/>

There are no changes to the directive.

Now, it all works like expected. I can grab the value of uploadme.src from my controller using $scope.uploadme.


Hi guys i create a directive and registered on bower.

this lib will help you modeling input file, not only return file data but also file dataurl or base 64.

{
    "lastModified": 1438583972000,
    "lastModifiedDate": "2015-08-03T06:39:32.000Z",
    "name": "gitignore_global.txt",
    "size": 236,
    "type": "text/plain",
    "data": "data:text/plain;base64,DQojaWdub3JlIHRodW1ibmFpbHMgY3JlYXRlZCBieSB3aW5kb3dz…xoDQoqLmJhaw0KKi5jYWNoZQ0KKi5pbGsNCioubG9nDQoqLmRsbA0KKi5saWINCiouc2JyDQo="
}

https://github.com/mistralworks/ng-file-model/

Hope will help you

2015/09/10

This is a slightly modified version that lets you specify the name of the attribute in the scope, just as you would do with ng-model, usage:

    <myUpload key="file"></myUpload>

Directive:

.directive('myUpload', function() {
    return {
        link: function postLink(scope, element, attrs) {
            element.find("input").bind("change", function(changeEvent) {                        
                var reader = new FileReader();
                reader.onload = function(loadEvent) {
                    scope.$apply(function() {
                        scope[attrs.key] = loadEvent.target.result;                                
                    });
                }
                if (typeof(changeEvent.target.files[0]) === 'object') {
                    reader.readAsDataURL(changeEvent.target.files[0]);
                };
            });

        },
        controller: 'FileUploadCtrl',
        template:
                '<span class="btn btn-success fileinput-button">' +
                '<i class="glyphicon glyphicon-plus"></i>' +
                '<span>Replace Image</span>' +
                '<input type="file" accept="image/*" name="files[]" multiple="">' +
                '</span>',
        restrict: 'E'

    };
});
2014/08/03