AngularJs: come verificare la presenza di modifiche nei campi di input del file?


281

Sono nuovo di angolare. Sto cercando di leggere il percorso del file caricato dal campo "file" HTML ogni volta che si verifica una "modifica" in questo campo. Se uso 'onChange' funziona ma quando lo uso in modo angolare usando 'ng-change' non funziona.

<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 () non chiama mai. Firebug inoltre non mostra alcun errore.



Risposte:


240

Nessun supporto di associazione per il controllo del caricamento dei file

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>

invece di

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

Puoi provare

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

Nota: ciò richiede che l'applicazione angolare sia sempre in modalità debug . Questo non funzionerà nel codice di produzione se la modalità debug è disabilitata.

e nella tua funzione cambia invece di

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

Puoi provare

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

Di seguito è riportato un esempio funzionante di caricamento di file con caricamento di trascinamento di file che può essere utile http://jsfiddle.net/danielzen/utp7j/

Informazioni sul caricamento del file angolare

URL per il caricamento del file AngularJS in ASP.Net

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

Caricamento multi-file nativo di AngularJs con avanzamento con NodeJS

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

ngUpload - Un servizio AngularJS per il caricamento di file tramite iframe

http://ngmodules.org/modules/ngUpload


2
onchange = "angular.element (this) .scope (). fileNameChaged (this)" $ scope.fileNameChaged = function (element) {console.log ('files:', element.files); } Puoi cambiare in due posizioni e controllare? a seconda del browser otterrai il percorso completo del file che ho aggiornato il violino qui sotto è il file di caricamento dell'URL
JQuery Guru

13
Questo metodo elude l'elaborazione AngularJS predefinita, quindi $ scope. $ Digest () non viene chiamato (i collegamenti non vengono aggiornati). Chiamare 'angular.element (this) .scope (). $ Digest ()' in seguito o chiamare un metodo scope che utilizza $ apply.
Ramon de Klein,

113
Questa è una risposta orribile. Chiamare angular.element (blah) .scope () è una funzione di debug che dovresti usare solo per il debug. Angular non utilizza la funzione .scope. È lì solo per aiutare gli sviluppatori a eseguire il debug. Quando crei la tua app e la distribuisci in produzione, dovresti disattivare le informazioni di debug. Questo accelera il tuo tutto MOLTO !!! Quindi, scrivere codice che dipende dalle chiamate element.scope () è una cattiva idea. Non farlo La risposta sulla creazione di una direttiva personalizzata è il modo giusto per farlo. @JQueryGuru dovresti aggiornare la tua risposta. Poiché ha il maggior numero di voti, le persone seguiranno questo cattivo consiglio.
gelido il

7
Questa soluzione interromperà l'app quando le informazioni di debug sono disabilitate. Disabilitare quello per la produzione è una raccomandazione ufficiale docs.angularjs.org/guide/production . Vedi anche blog.thoughtram.io/angularjs/2014/12/22/…
wiherek,

4
BAD SOLUTION ... ... leggi altri commenti su "disattivare il debug" ... ... ... e vedi sqrenla soluzione ... ... ... @ 0MV1
dsdsdsdsd

477

Ho fatto una piccola direttiva per ascoltare le modifiche all'input del file.

Visualizza JSFiddle

view.html:

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

controller.js:

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

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();
      });

    }
  };
});

4
Funziona bene se si sceglie un file diverso ogni volta. È possibile ascoltare anche quando è selezionato lo stesso file?
JDawg,

12
Questo ha un "bug" molto sfortunato: il controller non è associato come "this" in customOnChange, ma l'input del file! Mi ha gettato a lungo e non sono riuscito a trovare il vero bug. Non sono ancora sicuro di come chiamarlo effettivamente impostato correttamente "questo".
Karel Bílek,

4
il violino non funziona per me, né su Chrome né su Firefox, ad oggi.
seguso,

2
Questa risposta è errata, come affermato da @ KarelBílek. Non riuscivo a aggirare il problema e ho deciso di utilizzare angular-file-upload.
Gunar Gessner il

2
Questo è assolutamente come farlo, ha funzionato come un fascino. Inoltre, perché dovresti usare una funzione di debug in produzione?
Justin Kruse,

42

Questo è un perfezionamento di alcuni degli altri in giro, i dati finiranno in un modello ng, che è normalmente quello che vuoi.

Markup (basta creare un file di dati degli attributi in modo che la direttiva possa trovarlo)

<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();
            });
        }
    };
});

1
È $scope.$apply();richiesto? Il mio ng-changeevento sembra ancora sparare senza di esso.
row1,

1
@ row1 devi solo lanciare manualmente un applicare se hai altre cose angolari che devi sapere al riguardo durante quell'operazione.
Scott Sword,

27

Il modo più semplice è scrivere la propria direttiva per associare l'evento "change". Solo per farti sapere che IE9 non supporta FormData, quindi non puoi davvero ottenere l'oggetto file dall'evento change.

È possibile utilizzare la libreria ng-file-upload che supporta già IE con polyfill FileAPI e semplificare la registrazione del file sul server. Usa una direttiva per raggiungere questo obiettivo.

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

Questo esempio non è aggiornato. Prendi uno nuovo dai documenti qui .
Gunar Gessner il

26

Ho ampliato l'idea di @Stuart Axon di aggiungere un'associazione bidirezionale per l'input del file (ovvero consentire il ripristino dell'input ripristinando il valore del modello su 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("");
                }
            });
        }
    };
}]);

dimostrazione


Grazie @JLRishe. Una nota, in realtà data-ng-click="vm.theFile=''"funziona bene, non c'è bisogno di scriverla su un metodo controller
Sergey,

20

Simile ad alcune delle altre buone risposte qui, ho scritto una direttiva per risolvere questo problema, ma questa implementazione rispecchia più da vicino il modo angolare di collegare gli eventi.

Puoi usare la direttiva in questo modo:

HTML

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

Come puoi vedere, puoi iniettare i file selezionati nel gestore di eventi, come inietteresti un oggetto $ event in qualsiasi gestore di eventi ng.

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

ho lottato per un paio di giorni fino a quando ho provato la tua risposta. Grazie!
Michael Tranchida,

Come potrei passare un parametro aggiuntivo fileChangeall'espressione? Sto usando questa direttiva all'interno di ng-repeate la $scopevariabile passata attrHandlerè la stessa per ogni record. Qual è la soluzione migliore?

16

Questa direttiva passa anche i file selezionati:

/**
 *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);
          }
        });
      });

    }
  };
});

L'HTML, come usarlo:

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

Nel mio controller:

$scope.onFilesSelected = function(files) {
     console.log("files - " + files);
};

Come appare $ scope.onFilesSelected nel tuo controller?
Ingaham,

Se viene aperto un file e quando proviamo a caricarlo nel browser Microsoft Edge. Non sta succedendo niente. Puoi aiutare?
Naveen Katakam,

Sì, funziona bene con altri browser. sto affrontando il problema in microsoft edge @ingaham
Naveen Katakam il

8

Consiglio di creare una direttiva

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

        };
    }]);

questa direttiva può essere utilizzata più volte, utilizza il proprio ambito e non dipende dall'ambito padre. È inoltre possibile assegnare alcuni parametri alla funzione gestore. La funzione del gestore verrà chiamata con l'oggetto scope, che era attivo quando è stato modificato l'input. $ apply aggiorna il tuo modello ogni volta che viene chiamato l'evento change


4

La versione angolare jqLite più semplice.

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()">

Se viene aperto un file e quando proviamo a caricarlo nel browser Microsoft Edge. Non sta succedendo niente. Puoi aiutare?
Naveen Katakam,

2

Demo funzionante della direttiva "immissione file" che funziona con ng-change1

Per far funzionare un <input type=file>elemento come direttiva ng-change , è necessaria una direttiva personalizzata che funzioni con la direttiva ng-model .

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

La DEMO

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>


Fantastico ma la direttiva si chiama una sola volta! Se cambio file selezionato, la direttiva non viene chiamata una seconda volta! Hai qualche idea su questo comportamento?
hugsbrugs

La DEMO non ha questo problema. Crea una nuova domanda con un esempio riproducibile che i lettori possano esaminare.
Georg

sì, ha quel problema il log della console "input change" viene visualizzato solo una volta anche se si cambiano i file selezionati molte volte!
hugsbrugs

dopo il test, funziona su cromo ma non sull'ultima versione di Firefox ... dannatamente compatibilità con il browser !!!
hugsbrugs

0

Soluzione troppo completa basata su:

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

Un modo semplice per nascondere il campo di input e sostituirlo con un'immagine, qui dopo una soluzione, che richiede anche un hack su angolare ma che fa il lavoro [TriggerEvent non funziona come previsto]

La soluzione:

  • posizionare il campo di input in display: none [il campo di input esiste nel DOM ma non è visibile]
  • posiziona l'immagine subito dopo Sull'immagine usa nb-click () per attivare un metodo

Quando si fa clic sull'immagine, simulare un'azione DOM "clic" sul campo di input. 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!!!
    };

1
Non puoi usare angular.element (this) .scope (), questa è una funzione di debug!
Cesar,

0

L'ho fatto in questo modo;

<!-- 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

Un altro modo interessante per ascoltare le modifiche all'input del file è con un controllo sull'attributo ng-model del file di input. Ovviamente FileModel è una direttiva personalizzata.

Come questo:

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

Codice JS ->

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

Spero che possa aiutare qualcuno.


0

Gli elementi angolari (come l'elemento radice di una direttiva) sono oggetti jQuery [Lite]. Ciò significa che possiamo registrare il listener di eventi in questo modo:

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)
}

Questa è la delega degli eventi jQuery. Qui, il listener è associato all'elemento root della direttiva. Quando viene attivato, l'evento si sposta sull'elemento registrato e jQuery determinerà se l'evento ha avuto origine su un elemento interno corrispondente al selettore definito. In tal caso, il gestore sparerà.

I vantaggi di questo metodo sono:

  • il gestore è associato all'elemento $ che verrà automaticamente ripulito quando viene distrutto l'ambito della direttiva.
  • nessun codice nel modello
  • funzionerà anche se il delegato di destinazione (input) non è stato ancora visualizzato quando si registra il gestore eventi (come quando si utilizza ng-ifo ng-switch)

http://api.jquery.com/on/


-1

Puoi semplicemente aggiungere il seguente codice in onchange e rileverà il cambiamento. puoi scrivere una funzione su X clic o qualcosa per rimuovere i dati del file ..

document.getElementById(id).value = "";

Questa è una cattiva pratica e non angolare.
Sebastian,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.