Sélection de fichier avec JS angulaire

Je voudrais récupérer un fichier avec AngularJS:

HTML:

<div ng-controller="TopMenuCtrl">
    <button class="btn" ng-click="isCollapsed = !isCollapsed">Toggle collapse</button>
    <input type="file" ng-model="filepick" ng-change="pickimg()" multiple />
    <output id="list"></output> 
</div>

Javascript:

angular.module('plunker', ['ui.bootstrap']);
function TopMenuCtrl($scope) {
    $scope.pickimg = function() {
        alert('a');
    };
}

Comment puis-je lier l'action du fichier d'entrée onchange sur la fonction AngularJS pickimg? Et comment puis-je manipuler les fichiers téléchargés après?

26
demandé sur Sr.Richie 2013-05-19 09:22:25

7 réponses

Angular ne prend pas encore en charge ng-changepour input[type=file] vous devez donc lancer vous-même l'implémentation onchange.

Tout d'abord, dans le HTML, définissez Javascript pour onchange comme suit:

<input ng-model="photo"
       onchange="angular.element(this).scope().file_changed(this)"
       type="file" accept="image/*" />

Puis dans votre code de contrôleur angulaire, définissez la fonction:

$scope.file_changed = function(element) {

     $scope.$apply(function(scope) {
         var photofile = element.files[0];
         var reader = new FileReader();
         reader.onload = function(e) {
            // handle onload
         };
         reader.readAsDataURL(photofile);
     });
};
49
répondu Teemu Kurppa 2015-09-14 17:03:46

J'ai utilisé la méthode ci-dessus en essayant de charger une image d'aperçu lorsque le nouveau fichier est sélectionné, mais cela n'a pas fonctionné quand je l'ai essayé comme ça:

$scope.file_changed = function(element, $scope) {

     $scope.$apply(function(scope) {
         var photofile = element.files[0];
         var reader = new FileReader();
         reader.onload = function(e) {
            $scope.prev_img = e.target.result;
         };
         reader.readAsDataURL(photofile);
     });
});

J'ai creusé plus dedans et j'ai trouvé que le $scope.$s'appliquent doivent être à l'intérieur du lecteur.onLoad sinon changer une variable $ scope ne fonctionnera pas, donc j'ai fait ce qui suit et cela a fonctionné:

$scope.file_changed = function(element) {

        var photofile = element.files[0];
        var reader = new FileReader();
        reader.onload = function(e) {
            $scope.$apply(function() {
                $scope.prev_img = e.target.result;
            });
        };
        reader.readAsDataURL(photofile);
 };
17
répondu Sharon Abu 2013-12-24 08:16:14

La solution Teemu ne fonctionnera pas pour IE9.

J'ai mis en place une directive angulaire simple avec flash polyfill pour les navigateurs ne supportant pas HTML5 FormData, vous pouvez également écouter upload progress event.

Https://github.com/danialfarid/ng-file-upload Démo: http://angular-file-upload.appspot.com/

<script src="angular.min.js"></script>
<script src="ng-file-upload.js"></script>

<div ng-controller="MyCtrl">
  <input type="text" ng-model="additionalData">
  <div ngf-select ng-model="files" >
</div>

Contrôleur:

Upload.upload({
    url: 'my/upload/url',
    data: additionalData,
    file: files
  }).then(success, error, progress); 
6
répondu danial 2015-05-07 16:08:24

Voici mon approche avec une directive.

Directive

angular
  .module('yourModule')
  .directive('fileChange', function() {
    return {
     restrict: 'A',
     scope: {
       handler: '&'
     },
     link: function (scope, element) {
      element.on('change', function (event) {
        scope.$apply(function(){
          scope.handler({files: event.target.files});
        });
      });
     }
    };
});

HTML

<input type="file" file-change handler="fileSelect(files)">

Contrôleur

fileSelect = function (files) {
      var file = files[0];
      //you will get the file object here
}
3
répondu Madura Pradeep 2016-03-02 13:22:15

Voici une directive légère que j'ai écrite pour résoudre ce problème, qui reflète la manière angulaire d'attacher des événements.

Vous pouvez utiliser la directive comme ceci:

HTML

<input type="file" file-change="yourHandler($event, files)" />

Comme vous pouvez le voir, vous pouvez injecter les fichiers sélectionnés dans votre gestionnaire d'événement que vous donneriez à un $objet d'événement dans toute ng gestionnaire d'événement.

Javascript

angular
  .module('yourModule')
  .directive('fileChange', ['$parse', function($parse) {

    return {
      require: 'ngModel',
      restrict: 'A',
      link: function ($scope, element, attrs, ngModel) {

        // Get the function provided in the file-change attribute.
        // Note the attribute has become an angular expression,
        // which is what we are parsing. The provided handler is 
        // wrapped up in an outer function (attrHandler) - we'll 
        // call the provided event handler inside the handler()
        // function below.
        var attrHandler = $parse(attrs['fileChange']);

        // This is a wrapper handler which will be attached to the
        // HTML change event.
        var handler = function (e) {

          $scope.$apply(function () {

            // Execute the provided handler in the directive's scope.
            // The files variable will be available for consumption
            // by the event handler.
            attrHandler($scope, { $event: e, files: e.target.files });
          });
        };

        // Attach the handler to the HTML change event 
        element[0].addEventListener('change', handler, false);
      }
    };
  }]);
2
répondu Simon Robb 2014-10-27 15:34:31

En utilisant la réponse de Madura d'en haut, voici le flux complet pour lire un fichier JSON local:

Créer la directive:

angular
  .module('app.services')
  .directive('fileChange', function() {
    return {
     restrict: 'A',
     scope: {
       handler: '&'
     },
     link: function (scope, element) {
      element.on('change', function (event) {
        scope.$apply(function(){
          scope.handler({files: event.target.files});
        });
      });
     }
    };
});

HTML:

<input type="file" file-change handler="fileSelect(files)">

Javascript:

$scope.fileSelect = function(files) {
  var file = files[0];
  var reader = new FileReader();
  reader.onload = function(e) {
    console.log("on load", e.target.result);
  }
  reader.readAsText(file);
}
2
répondu Snowman 2017-05-23 12:26:23

J'ai fait une directive. Voici le violon .
L'application fonctionne pour sélectionner des CSV et les montrer sous forme de tables html.
Avec la directive on-file-change, vous seriez en mesure de définir la lecture et l'analyse des fichiers (avec des services, peut-être) dans les contrôleurs eux-mêmes, ce qui fournira plus de flexibilité. Juste pour la note, la fonction ac.onFileChange passée à l'attribut on-file-change deviendra le gestionnaire de l'événement de changement d'entrée à l'intérieur directive.

(function (angular, document) {

   angular
      .module("app.directives", [])
      .directive("onFileChange", ["$parse", function ($parse) {
         return {
            restrict: "A",
            link: function (scope, ele, attrs) {
               // onFileChange is a reference to the same function which you would define 
               // in the controller. So that you can keep your logic in the controller.
               var onFileChange = $parse(attrs.onFileChange.split(/\(/)[0])(scope)
               ele.on("change", onFileChange)
               ele.removeAttr("on-file-change")
            }
         }
      }])

   angular
      .module("app.services", [])
      .service("Parse", ["$q", function ($q) {
         var Parse = this
         Parse.csvAsGrid = function (file) {
            return $q(function (resolve, reject) {
               try {
                  Papa.parse(file, {
                     complete: function (results) {
                        resolve(results.data)
                     }
                  })
               } catch (e) {
                  reject(e)
               }
            })
         }
      }])

   angular
      .module("app", ["app.directives", "app.services"])
      .controller("appCtrl", ["$scope", "Parse", function ($scope, Parse) {
         var ac = this
         ac.fileName = ""
         ac.onFileChange = function (event) {
            if (!event.target.files.length) {
               return
            }
            Parse.csvAsGrid(event.target.files[0]).then(outputAsTable)
         }

         ac.clearInput = function (event) {
            var input = angular.element(event.target)
            input.val("")
            document.getElementById("output").innerHTML = ""
         }

         function outputAsTable(grid) {
            var table = ['<table border="1">']
            grid.map(function (row) {
               table.push('<tr>')
               row.map(function (cell) {
                  table.push('<td>' + cell.replace(/["']/g, "") + '</td>')
               })
               table.push('</tr>')
            })
            table.push('</table>')
            document.getElementById("output").innerHTML = table.join("\n")
         }
      }])

})(angular, document)
table {
  border-collapse: collapse;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.2/papaparse.min.js"></script>

<div ng-app="app" ng-controller="appCtrl as ac">
  <label>Select a comma delimited CSV file:-</label>  
  <input id="filePicker" type="file" on-file-change="ac.onFileChange(event)" ng-click="ac.clearInput($event)"/>{{ac.fileName}}  
</div>
<div id="output"></div>
0
répondu Vikas Gautam 2016-10-15 16:57:51