292 votes

ng-model pour <input type="file"/>

J'ai essayé d'utiliser ng-model sur la balise d'entrée avec type file:

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

Mais après avoir sélectionné un fichier, dans le contrôleur, $ scope.vm.uploadme n'est toujours pas défini.

Comment puis-je obtenir le fichier sélectionné dans mon contrôleur?

334voto

Endy Tjahjono Points 4458

J'ai créé une solution de contournement avec 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]);
            });
        }
    }
}]);
 

Et l'étiquette d'entrée devient:

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

Ou si seulement la définition du fichier est nécessaire:

 .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;
                });
            });
        }
    }
}]);
 

53voto

Elmer Points 2377

J'ai utiliser cette directive:

angular
.module('app.directives')
.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

})//appFilereader
;

et à l'appeler comme ceci:

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

La propriété (editItem.editItem._attachments_uri.image) sera remplie avec le contenu du fichier que vous sélectionnez comme un data-uri (!).

Veuillez noter que ce script ne sera pas télécharger quoi que ce soit. Il ne fera que remplir votre modèle avec le contenu de votre fichier encodé annonce d'un data-uri (base64).

Découvrez un travail démo ici: http://plnkr.co/CMiHKv2BEidM9SShm9Vv

26voto

Ce document est un addendum à @endy-tjahjono de la solution.

J'ai fini par ne pas être en mesure d'obtenir la valeur de uploadme de l'étendue. Même si uploadme dans le HTML était visiblement mis à jour par la directive, je ne pouvais pas encore accéder à sa valeur par $champ d'application.uploadme. J'ai été en mesure de définir sa valeur à partir de la portée, même si. Mystérieux, le droit..?

Comme il s'est avéré, un enfant a été créé par la directive, et l'enfant avait sa propre uploadme.

La solution a été d'utiliser un objet plutôt que d'une primitive pour contenir la valeur de uploadme.

Dans le contrôleur, j'ai:

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

et dans le HTML:

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

Il n'y a aucune modification de la directive.

Maintenant, tout fonctionne comme prévu. Je peux récupérer la valeur de uploadme.src de mon contrôleur à l'aide de $champ d'application.uploadme.

4voto

asiop Points 11

C'est une version légèrement modifiée qui vous permet de spécifier le nom de l'attribut dans la portée, comme vous le feriez avec ng-model, en utilisant:

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

Directif:

 .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'

    };
});
 

2voto

Hugo Points 1

J'ai dû faire la même chose sur plusieurs entrées, j'ai donc mis à jour la méthode @Endy Tjahjono. Il retourne un tableau contenant tous les fichiers lus.

   .directive("fileread", function () {
    return {
      scope: {
        fileread: "="
      },
      link: function (scope, element, attributes) {
        element.bind("change", function (changeEvent) {
          var readers = [] ,
              files = changeEvent.target.files ,
              datas = [] ;
          for ( var i = 0 ; i < files.length ; i++ ) {
            readers[ i ] = new FileReader();
            readers[ i ].onload = function (loadEvent) {
              datas.push( loadEvent.target.result );
              if ( datas.length === files.length ){
                scope.$apply(function () {
                  scope.fileread = datas;
                });
              }
            }
            readers[ i ].readAsDataURL( files[i] );
          }
        });

      }
    }
  });
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X