AngularJs: Wie überprüfe ich die Dateieingabefelder auf Änderungen?

281

Ich bin neu in eckig. Ich versuche, den hochgeladenen Dateipfad aus dem HTML-Feld "Datei" zu lesen, wenn in diesem Feld eine "Änderung" erfolgt. Wenn ich 'onChange' benutze, funktioniert es, aber wenn ich es mit 'ng-change' eckig benutze, funktioniert es nicht.

<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 () ruft niemals auf. Firebug zeigt auch keinen Fehler.

Manish Kumar
quelle
1
Könnte hilfreich sein: stackoverflow.com/q/17063000/983992
Marcel Gwerder

Antworten:

240

Keine Bindungsunterstützung für das Steuerelement zum Hochladen von Dateien

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>

anstatt

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

kannst du es versuchen

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

Hinweis: Dazu muss sich die Winkelanwendung immer im Debug-Modus befinden . Dies funktioniert im Produktionscode nicht, wenn der Debug-Modus deaktiviert ist.

und in deiner Funktion ändert sich statt

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

kannst du es versuchen

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

Im Folgenden finden Sie ein funktionierendes Beispiel für das Hochladen von Dateien mit Drag-Drop-Dateien. Das Hochladen von Dateien kann hilfreich sein. Http://jsfiddle.net/danielzen/utp7j/

Informationen zum Hochladen von Winkeldateien

URL für das Hochladen von AngularJS-Dateien in ASP.Net

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

AngularJs nativer Upload mehrerer Dateien mit Fortschritten bei NodeJS

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

ngUpload - Ein AngularJS-Dienst zum Hochladen von Dateien mit iframe

http://ngmodules.org/modules/ngUpload

JQuery Guru
quelle
2
onchange = "angle.element (this) .scope (). fileNameChaged (this)" $ scope.fileNameChaged = function (element) {console.log ('files:', element.files); } Können Sie an zwei Stellen wechseln und überprüfen? Abhängig vom Browser erhalten Sie den vollständigen Pfad der Datei. Ich habe die Geige unten aktualisiert. Es handelt sich um die URL-Upload-Datei. Überprüfen Sie die Konsole. jsfiddle.net/utp7j/260
JQuery Guru
13
Diese Methode umgeht die standardmäßige AngularJS-Verarbeitung, sodass $ scope. $ Digest () nicht aufgerufen wird (Bindungen werden nicht aktualisiert). Rufen Sie anschließend 'angle.element (this) .scope (). $ Digest ()' auf oder rufen Sie eine Bereichsmethode auf, die $ apply verwendet.
Ramon de Klein
113
Dies ist eine schreckliche Antwort. Das Aufrufen von angle.element (blah) .scope () ist eine Debug-Funktion, die Sie nur zum Debuggen verwenden sollten. Angular verwendet die .scope-Funktion nicht. Es ist nur dazu da, Entwicklern beim Debuggen zu helfen. Wenn Sie Ihre App erstellen und für die Produktion bereitstellen, sollten Sie die Debug-Informationen deaktivieren. Dies beschleunigt alles VIEL !!! Es ist daher eine schlechte Idee, Code zu schreiben, der von den Aufrufen von element.scope () abhängt. Tu das nicht. Die Antwort zum Erstellen einer benutzerdefinierten Direktive ist der richtige Weg, dies zu tun. @JQueryGuru Sie sollten Ihre Antwort aktualisieren. Weil es die meisten Stimmen hat, werden die Leute diesem schlechten Rat folgen.
Frosty
7
Diese Lösung bricht die App, wenn die Debug-Informationen deaktiviert sind. Das Deaktivieren für die Produktion ist eine offizielle Empfehlung docs.angularjs.org/guide/production . Bitte sehen Sie auch blog.ehowtram.io/angularjs/2014/12/22/…
wiherek
4
SCHLECHTE LÖSUNG ... ... lesen Sie andere Kommentare zum sqrenThema "
Debugging
477

Ich habe eine kleine Anweisung gemacht, um auf Änderungen bei der Dateieingabe zu warten.

JSFiddle anzeigen

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

    }
  };
});
sqren
quelle
4
Dies funktioniert gut, wenn jedes Mal eine andere Datei ausgewählt wird. Ist es möglich zu warten, wenn dieselbe Datei ebenfalls ausgewählt ist?
JDawg
12
Dies hat einen sehr unglücklichen "Fehler" - der Controller ist im customOnChange nicht als "this" gebunden, sondern als Dateieingabe! Es warf mich für eine lange Zeit ab und ich konnte den tatsächlichen Fehler nicht finden. Ich bin mir noch nicht sicher, wie ich es mit richtig eingestelltem "this" nennen soll.
Karel Bílek
4
Die Geige funktioniert bei mir weder auf Chrome noch auf Firefox ab heute.
Seguso
2
Diese Antwort ist fehlerhaft, wie von @ KarelBílek angegeben. Ich konnte es nicht umgehen und entschied mich für Angular-File-Upload.
Gunar Gessner
2
Dies ist absolut, wie es geht, arbeitete wie ein Zauber. Außerdem, warum sollten Sie eine Debug-Funktion in der Produktion verwenden?
Justin Kruse
42

Dies ist eine Verfeinerung einiger der anderen, die Daten werden in einem ng-Modell enden, was normalerweise das ist, was Sie wollen.

Markup (erstellen Sie einfach eine Attribut-Datendatei, damit die Direktive sie finden kann)

<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();
            });
        }
    };
});
Stuart Axon
quelle
1
Ist $scope.$apply();erforderlich? Meine ng-changeVeranstaltung scheint immer noch ohne sie zu feuern.
Reihe1
1
@ row1 Sie müssen eine Anwendung nur manuell auslösen, wenn Sie andere eckige Dinge haben, die während dieser Operation darüber Bescheid wissen müssen .
Scott Sword
27

Der saubere Weg besteht darin, eine eigene Direktive zu schreiben, um sie an das Ereignis "change" zu binden. Nur um Sie wissen zu lassen, dass IE9 FormData nicht unterstützt, sodass Sie das Dateiobjekt nicht wirklich aus dem Änderungsereignis abrufen können.

Sie können die ng-file-upload- Bibliothek verwenden, die IE bereits mit FileAPI-Polyfill unterstützt, und das Posten der Datei auf dem Server vereinfachen. Es verwendet eine Richtlinie, um dies zu erreichen.

<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);
      }); 
    }
  }
}];
danial
quelle
Dieses Beispiel ist veraltet. Erhalten Sie neue von docs hier .
Gunar Gessner
26

Ich habe die Idee von @Stuart Axon erweitert, eine bidirektionale Bindung für die Dateieingabe hinzuzufügen (dh das Zurücksetzen der Eingabe durch Zurücksetzen des Modellwerts auf Null zu ermöglichen):

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

Demo

JLRishe
quelle
Vielen Dank, dass Sie @JLRishe. Ein Hinweis, data-ng-click="vm.theFile=''"funktioniert tatsächlich gut, keine Notwendigkeit, es in eine Controller-Methode zu schreiben
Sergey
20

Ähnlich wie bei einigen anderen guten Antworten hier habe ich eine Anweisung geschrieben, um dieses Problem zu lösen, aber diese Implementierung spiegelt die eckige Art des Anhängens von Ereignissen genauer wider.

Sie können die Direktive wie folgt verwenden:

HTML

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

Wie Sie sehen, können Sie die ausgewählten Dateien in Ihren Ereignishandler einfügen, so wie Sie ein $ event-Objekt in einen beliebigen ng-Ereignishandler einfügen würden.

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);
      }
    };
  }]);
Simon Robb
quelle
Ich hatte ein paar Tage damit zu kämpfen, bis ich deine Antwort versuchte. Vielen Dank!
Michael Tranchida
Wie kann ich dem fileChangeAusdruck einen zusätzlichen Parameter übergeben ? Ich verwende diese Direktive in einem ng-repeatund die übergebene $scopeVariable attrHandlerist für jeden Datensatz gleich. Was ist die beste Lösung?
16

Diese Anweisung übergibt auch die ausgewählten Dateien:

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

    }
  };
});

Der HTML-Code, wie man ihn benutzt:

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

In meinem Controller:

$scope.onFilesSelected = function(files) {
     console.log("files - " + files);
};
Ingaham
quelle
Wie sieht $ scope.onFilesSelected in Ihrem Controller aus?
Ingaham
Wenn eine Datei geöffnet wird und wir versuchen, die Datei im Microsoft Edge-Browser hochzuladen. Nichts passiert. Kannst du helfen?
Naveen Katakam
Ja, es funktioniert gut mit anderen Browsern. Ich stehe vor einem Problem in Microsoft Edge @ingaham
Naveen Katakam
8

Ich empfehle eine Richtlinie zu erstellen

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

        };
    }]);

Diese Direktive kann mehrfach verwendet werden, verwendet ihren eigenen Bereich und hängt nicht vom übergeordneten Bereich ab. Sie können der Handlerfunktion auch einige Parameter zuweisen. Die Handlerfunktion wird mit dem Bereichsobjekt aufgerufen, das aktiv war, als Sie die Eingabe geändert haben. $ apply aktualisiert Ihr Modell jedes Mal, wenn das Änderungsereignis aufgerufen wird

Julia Usanova
quelle
4

Die einfachste Angular jqLite-Version.

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()">
Jonáš Krutil
quelle
Wenn eine Datei geöffnet wird und wir versuchen, die Datei im Microsoft Edge-Browser hochzuladen. Nichts passiert. Kannst du helfen?
Naveen Katakam
2

Arbeitsdemo der "Dateieingabe" -Richtlinie, die mit 1 funktioniertng-change

Damit ein <input type=file>Element mit der Direktive ng-change funktioniert , benötigt es eine benutzerdefinierte Direktive , die mit der Direktive ng-model funktioniert .

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

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

georgeawg
quelle
Großartig, aber Direktive wird nur einmal aufgerufen! Wenn ich die ausgewählte Datei ändere, wird die Direktive nicht ein zweites Mal aufgerufen! Haben Sie eine Vorstellung von diesem Verhalten?
Umarmungen
Die DEMO hat dieses Problem nicht. Erstellen Sie eine neue Frage mit einem reproduzierbaren Beispiel, das die Leser prüfen können.
Georgeawg
Ja, es gibt dieses Problem. Das Konsolenprotokoll "Eingabeänderung" wird nur einmal angezeigt, selbst wenn Sie ausgewählte Dateien mehrmals ändern.
Umarmungen
Nach dem Testen funktioniert es auf Chrom, aber nicht auf der neuesten Firefox-Version ... verdammte Browserkompatibilität !!!
Umarmungen
0

Zu vollständige Lösung basiert auf:

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

Eine einfache Möglichkeit, das Eingabefeld auszublenden und durch ein Bild zu ersetzen, hier nach einer Lösung, die ebenfalls einen Winkel-Hack erfordert, aber die Aufgabe erfüllt [TriggerEvent funktioniert nicht wie erwartet]

Die Lösung:

  • Platzieren Sie das Eingabefeld in der Anzeige: keine [das Eingabefeld ist im DOM vorhanden, aber nicht sichtbar]
  • Platzieren Sie Ihr Bild direkt nach Auf dem Bild verwenden Sie nb-click (), um eine Methode zu aktivieren

Wenn Sie auf das Bild klicken, simulieren Sie eine DOM-Aktion, indem Sie auf das Eingabefeld klicken. 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!!!
    };
Fulup
quelle
1
Sie können angle.element (this) .scope () nicht verwenden, dies ist eine Debug-Funktion!
Cesar
0

Ich habe es so gemacht;

<!-- 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");
}
Ali Sakhi
quelle
0

Eine weitere interessante Möglichkeit, Änderungen an der Dateieingabe abzuhören, besteht darin, das Attribut ng-model der Eingabedatei zu überwachen. Natürlich ist FileModel eine benutzerdefinierte Direktive.

So was:

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

JS Code ->

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

Hoffe es kann jemandem helfen.

Halbano
quelle
0

Winkelelemente (z. B. das Stammelement einer Direktive) sind jQuery [Lite] -Objekte. Dies bedeutet, dass wir den Ereignis-Listener folgendermaßen registrieren können:

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

Dies ist die jQuery-Ereignisdelegierung. Hier wird der Listener an das Stammelement der Direktive angehängt. Wenn das Ereignis ausgelöst wird, sprudelt es bis zum registrierten Element und jQuery bestimmt, ob das Ereignis von einem inneren Element stammt, das mit dem definierten Selektor übereinstimmt. In diesem Fall wird der Handler ausgelöst.

Vorteile dieser Methode sind:

  • Der Handler ist an das $ -Element gebunden, das automatisch bereinigt wird, wenn der Gültigkeitsbereich der Direktive zerstört wird.
  • Kein Code in der Vorlage
  • funktioniert auch dann, wenn der Zieldelegat (Eingabe) bei der Registrierung des Ereignishandlers noch nicht gerendert wurde (z. B. bei Verwendung von ng-ifoder ng-switch).

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

Matt S.
quelle
-1

Sie können einfach den folgenden Code in onchange hinzufügen, um Änderungen zu erkennen. Sie können eine Funktion auf X-Klick oder etwas schreiben, um Dateidaten zu entfernen.

document.getElementById(id).value = "";
Jijo Thomas
quelle
Dies ist eine schlechte Praxis und nicht eckig.
Sebastian