AngularJs: Comment vérifier les changements dans les champs de saisie de fichiers?

Je suis nouveau à angular. J'essaie de lire le chemin du fichier téléchargé à partir du champ 'file' HTML chaque fois qu'un 'changement' se produit sur ce champ. Si j'utilise 'onChange' cela fonctionne mais quand je l'utilise de manière angulaire en utilisant 'ng-change' cela ne fonctionne pas.

<script>
   var DemoModule = angular.module("Demo",[]);
   DemoModule .controller("form-cntlr",function($scope){
   $scope.selectFile = function()
   {
        $("#file").click();
   }
   $scope.fileNameChaged = function()
   {
        alert("select file");
   }
});
</script>

<div ng-controller="form-cntlr">
    <form>
         <button ng-click="selectFile()">Upload Your File</button>
         <input type="file" style="display:none" 
                          id="file" name='file' ng-Change="fileNameChaged()"/>
    </form>  
</div>

FileNameChaged () n'appelle jamais. Firebug ne montre pas non plus d'erreur.

261
demandé sur MayurB 2013-07-29 15:13:10

14 réponses

Pas de support de liaison pour le contrôle de téléchargement de fichiers

Https://github.com/angular/angular.js/issues/1375

<div ng-controller="form-cntlr">
        <form>
             <button ng-click="selectFile()">Upload Your File</button>
             <input type="file" style="display:none" 
                id="file" name='file' onchange="angular.element(this).scope().fileNameChanged(this)" />
        </form>  
    </div>

Au Lieu de

 <input type="file" style="display:none" 
    id="file" name='file' ng-Change="fileNameChanged()" />

Pouvez-vous essayer

<input type="file" style="display:none" 
    id="file" name='file' onchange="angular.element(this).scope().fileNameChanged()" />

Remarque: cela nécessite que l'application angulaire soit toujours en mode de débogage . Cela ne fonctionnera pas dans le code de production si le mode débogage est désactivé.

Et dans votre fonction change au lieu de

$scope.fileNameChanged = function() {
   alert("select file");
}

Pouvez-vous essayer

$scope.fileNameChanged = function() {
  console.log("select file");
}

Voici un exemple de travail de téléchargement de fichiers avec drag drop téléchargement de fichiers peut être utile http://jsfiddle.net/danielzen/utp7j/

Informations De Téléchargement De Fichier Angulaire

URL pour le téléchargement du fichier AngularJS dans ASP.Net

Http://cgeers.com/2013/05/03/angularjs-file-upload/

Téléchargement multi-fichiers natif AngularJs avec progression avec NodeJS

Http://jasonturim.wordpress.com/2013/09/12/angularjs-native-multi-file-upload-with-progress/

NgUpload - Un Service AngularJS pour le téléchargement de fichiers en utilisant iframe

Http://ngmodules.org/modules/ngUpload

226
répondu JQuery Guru 2015-11-24 16:01:52

J'ai fait une petite directive pour écouter les modifications d'entrée de fichier.

Voir JSFiddle

Vue.html:

<input type="file" custom-on-change="uploadFile">

Le Contrôleur.js:

app.controller('myCtrl', function($scope){
    $scope.uploadFile = function(event){
        var files = event.target.files;
    };
});     

La Directive.js:

app.directive('customOnChange', function() {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      var onChangeHandler = scope.$eval(attrs.customOnChange);
      element.on('change', onChangeHandler);
      element.on('$destroy', function() {
        element.off();
      });

    }
  };
});
440
répondu sqren 2017-11-14 13:48:16

C'est un raffinement de certains des autres, les données se retrouveront dans un modèle ng, ce qui est normalement ce que vous voulez.

Markup (il suffit de créer un fichier de données d'attribut pour que la directive puisse le Trouver)

<input
    data-file
    id="id_image" name="image"
    ng-model="my_image_model" type="file">

JS

app.directive('file', function() {
    return {
        require:"ngModel",
        restrict: 'A',
        link: function($scope, el, attrs, ngModel){
            el.bind('change', function(event){
                var files = event.target.files;
                var file = files[0];

                ngModel.$setViewValue(file);
                $scope.$apply();
            });
        }
    };
});
34
répondu Stuart Axon 2014-06-10 18:28:30

La manière propre est d'écrire votre propre directive pour se lier à l'événement" change". Juste pour vous faire savoir que IE9 ne prend pas en charge FormData, donc vous ne pouvez pas vraiment obtenir l'objet file De l'événement change.

Vous pouvez utiliser la bibliothèque ng-file-upload qui prend déjà en charge IE avec FileAPI polyfill et simplifie la publication du fichier sur le serveur. Il utilise une directive pour y parvenir.

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

<div ng-controller="MyCtrl">
  <input type="file" ngf-select="onFileSelect($files)" multiple>
</div>

JS:

//inject angular file upload directive.
angular.module('myApp', ['ngFileUpload']);

var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
  $scope.onFileSelect = function($files) {
    //$files: an array of files selected, each file has name, size, and type.
    for (var i = 0; i < $files.length; i++) {
      var $file = $files[i];
      Upload.upload({
        url: 'my/upload/url',
        data: {file: $file}
      }).then(function(data, status, headers, config) {
        // file is uploaded successfully
        console.log(data);
      }); 
    }
  }
}];
26
répondu danial 2015-12-22 23:35:43

J'ai développé l'idée de @Stuart Axon d'ajouter une liaison bidirectionnelle pour l'entrée du fichier (c'est-à-dire permettre de réinitialiser l'entrée en réinitialisant la valeur du modèle à null):

app.directive('bindFile', [function () {
    return {
        require: "ngModel",
        restrict: 'A',
        link: function ($scope, el, attrs, ngModel) {
            el.bind('change', function (event) {
                ngModel.$setViewValue(event.target.files[0]);
                $scope.$apply();
            });

            $scope.$watch(function () {
                return ngModel.$viewValue;
            }, function (value) {
                if (!value) {
                    el.val("");
                }
            });
        }
    };
}]);

Démo

25
répondu JLRishe 2015-01-08 06:33:26

Semblable à certaines des autres bonnes réponses ici, j'ai écrit une directive pour résoudre ce problème, mais cette implémentation reflète plus étroitement 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);
      }
    };
  }]);
18
répondu Simon Robb 2014-10-27 15:28:56

Cette directive transmet également les fichiers sélectionnés:

/**
 *File Input - custom call when the file has changed
 */
.directive('onFileChange', function() {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      var onChangeHandler = scope.$eval(attrs.onFileChange);

      element.bind('change', function() {
        scope.$apply(function() {
          var files = element[0].files;
          if (files) {
            onChangeHandler(files);
          }
        });
      });

    }
  };
});

Le HTML, comment l'utiliser:

<input type="file" ng-model="file" on-file-change="onFilesSelected">

Dans mon contrôleur:

$scope.onFilesSelected = function(files) {
     console.log("files - " + files);
};
13
répondu ingaham 2016-05-20 09:44:50

Je recommande de créer une directive

<input type="file" custom-on-change handler="functionToBeCalled(params)">

app.directive('customOnChange', [function() {
        'use strict';

        return {
            restrict: "A",

            scope: {
                handler: '&'
            },
            link: function(scope, element){

                element.change(function(event){
                    scope.$apply(function(){
                        var params = {event: event, el: element};
                        scope.handler({params: params});
                    });
                });
            }

        };
    }]);

Cette directive peut être utilisée plusieurs fois, elle utilise sa propre portée et ne dépend pas de la portée parente. Vous pouvez également donner quelques paramètres à la fonction de gestionnaire. La fonction de gestionnaire sera appelée avec l'objet scope, qui était actif lorsque vous avez modifié l'entrée. $ apply met à jour votre modèle chaque fois que l'événement change est appelé

5
répondu Julia Usanova 2014-11-25 13:03:34

La version angulaire Jqlite la plus simple.

JS:

.directive('cOnChange', function() {
    'use strict';

    return {
        restrict: "A",
        scope : {
            cOnChange: '&'
        },
        link: function (scope, element) {
            element.on('change', function () {
                scope.cOnChange();
        });
        }
    };
});

HTML:

<input type="file" data-c-on-change="your.functionName()">
3
répondu Jonáš Krutil 2015-01-05 23:44:38

Solution trop complète base sur:

`onchange="angular.element(this).scope().UpLoadFile(this.files)"`

Un moyen simple de masquer le champ de saisie et de le remplacer par une image, ici après une solution, qui nécessite également un hack sur angular mais qui fait le travail [TriggerEvent ne fonctionne pas comme prévu]

La solution:

  • Placez le champ de saisie dans display: none [le champ de saisie existe dans le DOM mais n'est pas visible]
  • Placez votre image juste après Sur l'image, utilisez nb-click() pour activer une méthode

Lorsque l'image est cliqué simuler une action DOM 'click' sur le champ de saisie. Et voilà!

 var tmpl = '<input type="file" id="{{name}}-filein"' + 
             'onchange="angular.element(this).scope().UpLoadFile(this.files)"' +
             ' multiple accept="{{mime}}/*" style="display:none" placeholder="{{placeholder}}">'+
             ' <img id="{{name}}-img" src="{{icon}}" ng-click="clicked()">' +
             '';
   // Image was clicked let's simulate an input (file) click
   scope.inputElem = elem.find('input'); // find input in directive
   scope.clicked = function () {
         console.log ('Image clicked');
         scope.inputElem[0].click(); // Warning Angular TriggerEvent does not work!!!
    };
0
répondu Fulup 2015-12-13 20:29:47

Je l'ai fait comme ça;

<!-- HTML -->
<button id="uploadFileButton" class="btn btn-info" ng-click="vm.upload()">    
<span  class="fa fa-paperclip"></span></button>
<input type="file" id="txtUploadFile" name="fileInput" style="display: none;" />
// self is the instance of $scope or this
self.upload = function () {
   var ctrl = angular.element("#txtUploadFile");
   ctrl.on('change', fileNameChanged);
   ctrl.click();
}

function fileNameChanged(e) {
    console.log(self.currentItem);
    alert("select file");
}
0
répondu Ali Sakhi 2016-02-10 10:10:41

Une autre façon intéressante d'écouter les changements d'entrée de fichier est de surveiller l'attribut ng-model du fichier d'entrée. Bien sûr FileModel est une directive personnalisée.

Comme ceci:

HTML -> <input type="file" file-model="change.fnEvidence">

Code JS- >

$scope.$watch('change.fnEvidence', function() {
                    alert("has changed");
                });

J'espère que cela peut aider quelqu'un.

0
répondu halbano 2017-04-17 18:45:02

Travailler Démo de "fichiers d'entrée" de la Directive qui Fonctionne avec ng-change1

Pour un <input type=file> élément de travail de la ng-changement de directive, il a besoin d'un personnalisé directive qui fonctionne avec la ng-model directive.

<input type="file" files-input ng-model="fileList" 
       ng-change="onInputChange()" multiple />

La démo

angular.module("app",[])
.directive("filesInput", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
})

.controller("ctrl", function($scope) {
     $scope.onInputChange = function() {
         console.log("input change");
     };
})
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app" ng-controller="ctrl">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" files-input ng-model="fileList" 
           ng-change="onInputChange()" multiple />
    
    <h2>Files</h2>
    <div ng-repeat="file in fileList">
      {{file.name}}
    </div>
  </body>
0
répondu georgeawg 2017-08-30 17:35:34

Les éléments angulaires (tels que l'élément racine d'une directive) sont des objets jQuery [Lite]. Cela signifie que nous pouvons enregistrer l'écouteur d'événement comme ceci:

link($scope, $el) {
    const fileInputSelector = '.my-file-input'

    function setFile() {
        // access file via $el.find(fileInputSelector).get(0).files[0]
    }

    $el.on('change', fileInputSelector, setFile)
}

C'est une délégation d'événement jQuery. Ici, l'auditeur est attaché à l'élément racine de la directive. Lorsque l'événement est déclenché, il apparaîtra dans l'élément enregistré et jQuery déterminera si l'événement provient d'un élément interne correspondant au sélecteur défini. Si c'est le cas, le gestionnaire d'incendie.

Avantages de cette méthode sont:

  • le gestionnaire est lié à l'élément $qui sera automatiquement nettoyé lorsque la portée de la directive sera détruite.
  • pas de code dans le modèle
  • fonctionnera même si le délégué cible (entrée) n'a pas encore été rendu lorsque vous enregistrez le gestionnaire d'événements (par exemple lorsque vous utilisez ng-if ou ng-switch)

Http://api.jquery.com/on/

0
répondu Matt S 2017-09-08 00:39:56