AngularJs: Jak sprawdzić zmiany w polach wejściowych pliku?

281

Jestem nowy w angular. Próbuję odczytać przesłaną ścieżkę pliku z pola „pliku” HTML, ilekroć w tym polu nastąpi „zmiana”. Jeśli używam „onChange”, to działa, ale kiedy używam go pod kątem, używając „ng-change”, to nie działa.

<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 () nigdy nie wywołuje. Firebug również nie pokazuje żadnego błędu.

Manish Kumar
źródło
1
Może być pomocny: stackoverflow.com/q/17063000/983992
Marcel Gwerder

Odpowiedzi:

240

Brak obsługi wiązania dla kontroli przesyłania plików

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>

zamiast

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

możesz spróbować

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

Uwaga: wymaga to, aby aplikacja kątowa zawsze znajdowała się w trybie debugowania . To nie zadziała w kodzie produkcyjnym, jeśli tryb debugowania jest wyłączony.

i w twojej funkcji zmienia się zamiast

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

możesz spróbować

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

Poniżej znajduje się jeden działający przykład przesyłania plików z przesyłaniem plików metodą przeciągnij i upuść http://jsfiddle.net/danielzen/utp7j/

Informacje o przesyłaniu plików kątowych

Adres URL do przesyłania plików AngularJS w ASP.Net

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

AngularJs natywne przesyłanie wielu plików z postępem w NodeJS

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

ngUpload - usługa AngularJS do przesyłania plików przy użyciu iframe

http://ngmodules.org/modules/ngUpload

JQuery Guru
źródło
2
onchange = "angular.element (this) .scope (). fileNameChaged (this)" $ scope.fileNameChaged = function (element) {console.log ('files:', element.files); } Czy możesz zmienić w dwóch miejscach i sprawdzić? w zależności od przeglądarki otrzymasz pełną ścieżkę pliku. Mam zaktualizowane skrzypce poniżej to plik do przesłania adresu
JQuery Guru
13
Ta metoda omija domyślne przetwarzanie AngularJS, więc $ scope. $ Digest () nie jest wywoływany (powiązania nie są aktualizowane). Wywołaj później „angular.element (this) .scope (). $ Digest ()” lub wywołaj metodę zasięgu, która używa $ Apply.
Ramon de Klein
113
To okropna odpowiedź. Wywołanie angular.element (blah) .scope () to funkcja debugowania, której należy używać tylko do debugowania. Angular nie korzysta z funkcji .scope. Jest tylko po to, aby pomóc programistom w debugowaniu. Podczas budowania aplikacji i wdrażania w środowisku produkcyjnym należy wyłączyć informacje debugowania. Przyspiesza to DUŻO !!! Zatem pisanie kodu zależnego od wywołań elementu.scope () jest złym pomysłem. Nie rób tego Odpowiedź na temat utworzenia niestandardowej dyrektywy to właściwy sposób. @JQueryGuru powinieneś zaktualizować swoją odpowiedź. Ponieważ ma najwięcej głosów, ludzie zastosują się do tej złej rady.
mroźny
7
To rozwiązanie spowoduje uszkodzenie aplikacji, gdy informacje debugowania zostaną wyłączone. Wyłączenie tego w przypadku produkcji jest oficjalną rekomendacją docs.quarejs.org/guide/production . Zobacz także blog.thoughtram.io/angularjs/2014/12/22/…
wiherek
4
ZŁE ROZWIĄZANIE ... ... przeczytaj inne komentarze na temat „wyłączania debugowania” ... ... ... i zobacz sqrenrozwiązanie ... ... ... @ 0MV1
dsdsdsdsd
477

Podjąłem małą dyrektywę, aby nasłuchiwać zmian wprowadzanych plików.

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

dyrektywa.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
źródło
4
Działa to dobrze, jeśli za każdym razem wybierany jest inny plik. Czy można nasłuchiwać również po wybraniu tego samego pliku?
JDawg,
12
Ma to jeden bardzo niefortunny „błąd” - kontroler nie jest związany jako „to” w customOnChange, ale plik wejściowy! Długo mnie to wyrzuciło i nie mogłem znaleźć prawdziwego błędu. Nie jestem jeszcze pewien, jak to właściwie nazwać, ustawiając poprawnie to.
Karel Bílek,
4
skrzypce nie działa na mnie, ani w Chrome, ani Firefox, na dzień dzisiejszy.
seguso
2
Ta odpowiedź jest błędna, jak stwierdził @ KarelBílek. Nie mogłem tego obejść i postanowiłem użyć przesyłania plików kątowych.
Gunar Gessner,
2
To absolutnie jak to zrobić, działało jak urok. Poza tym, dlaczego miałbyś używać funkcji debugowania w produkcji?
Justin Kruse,
42

Jest to udoskonalenie niektórych innych wokół, dane zostaną skończone w modelu ng, co zwykle jest tym, czego chcesz.

Znaczniki (wystarczy utworzyć plik danych atrybutu, aby dyrektywa mogła go znaleźć)

<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
źródło
1
Jest $scope.$apply();wymagane? ng-changeWydaje się, że moje wydarzenie wciąż bez niego strzela.
wiersz 1
1
@ wiersz1 musisz wystrzelić ręcznie aplikację tylko wtedy, gdy masz inne ważne rzeczy, które musisz o niej wiedzieć podczas tej operacji.
Scott Sword,
27

Najprościej jest napisać własną dyrektywę, aby powiązać zdarzenie „zmień”. Informuję, że IE9 nie obsługuje FormData, więc nie można tak naprawdę pobrać obiektu pliku ze zdarzenia zmiany.

Możesz użyć biblioteki przesyłania plików ng, która już obsługuje IE z polifillem FileAPI i uprościć wysyłanie pliku na serwer. W tym celu wykorzystuje dyrektywę.

<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
źródło
Ten przykład jest nieaktualny. Pobierz nowy z dokumentów tutaj .
Gunar Gessner,
26

Rozszerzyłem pomysł @Stuart Axon, aby dodać dwukierunkowe wiązanie dla danych wejściowych pliku (tzn. Zezwolić na zresetowanie danych wejściowych poprzez zresetowanie wartości modelu z powrotem do wartości 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("");
                }
            });
        }
    };
}]);

Próbny

JLRishe
źródło
Dziękuję @JLRishe. Jedna nuta, właściwie data-ng-click="vm.theFile=''"działa dobrze, nie trzeba zapisywać jej w metodzie kontrolera
Sergey
20

Podobnie jak w przypadku niektórych innych dobrych odpowiedzi tutaj, napisałem dyrektywę, aby rozwiązać ten problem, ale ta implementacja dokładniej odzwierciedla kątowy sposób dołączania zdarzeń.

Możesz użyć dyrektywy w następujący sposób:

HTML

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

Jak widać, możesz wstrzyknąć wybrane pliki do procedury obsługi zdarzeń, tak jak wstrzyknąłbyś obiekt $ event do dowolnej procedury obsługi zdarzeń 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);
      }
    };
  }]);
Simon Robb
źródło
walczyłem z tym przez kilka dni, dopóki nie spróbowałem twojej odpowiedzi. Dzięki!
Michael Tranchida,
Jak mogę przekazać dodatkowy parametr do fileChangewyrażenia? Używam tej dyrektywy wewnątrz ng-repeati $scopeprzekazywana zmienna attrHandlerjest taka sama dla każdego rekordu. Jakie jest najlepsze rozwiązanie?
16

Niniejsza dyrektywa przekazuje również wybrane pliki:

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

    }
  };
});

HTML, jak go używać:

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

W moim kontrolerze:

$scope.onFilesSelected = function(files) {
     console.log("files - " + files);
};
ingaham
źródło
Jak wygląda $ scope.onFilesSelected w twoim kontrolerze?
ingaham
Jeśli plik jest otwarty i kiedy próbujemy załadować go w przeglądarce Microsoft Edge. Nic się nie dzieje. Możesz pomóc?
Naveen Katakam
Tak, działa dobrze z innymi przeglądarkami. Mam do czynienia z problemem w Microsoft Edge @ingaham
Naveen Katakam
8

Polecam stworzyć dyrektywę

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

        };
    }]);

ta dyrektywa może być używana wiele razy, używa własnego zakresu i nie zależy od zakresu nadrzędnego. Możesz również podać kilka parametrów funkcji obsługi. Funkcja modułu obsługi zostanie wywołana z obiektem zakresu, który był aktywny po zmianie danych wejściowych. $ Apply aktualizuje Twój model za każdym razem, gdy wywoływane jest zdarzenie zmiany

Julia Usanova
źródło
4

Najprostsza wersja Angular jqLite.

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
źródło
Jeśli plik jest otwarty i kiedy próbujemy załadować go w przeglądarce Microsoft Edge. Nic się nie dzieje. Możesz pomóc?
Naveen Katakam
2

Demonstracja robocza dyrektywy „file-input”, która działa z ng-change1

Aby <input type=file>element działał zgodnie z dyrektywą ng-change , potrzebuje niestandardowej dyrektywy, która działa z dyrektywą ng-model .

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

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
źródło
Świetnie, ale dyrektywa nazywa się tylko raz! Jeśli zmienię wybrany plik, dyrektywa nie zostanie wywołana po raz drugi! Czy masz pojęcie o tym zachowaniu?
hugsbrugs
DEMO nie ma tego problemu. Utwórz nowe pytanie z powtarzalnym przykładem do zbadania przez czytelników.
georgeawg
tak, ma ten problem, „zmiana danych wejściowych” dziennika konsoli wyświetla się tylko raz, nawet jeśli zmienisz wybrane pliki wiele razy!
hugsbrugs
po testach działa na chromie, ale nie działa na najnowszej wersji Firefoxa ... cholera kompatybilność z przeglądarką !!!
hugsbrugs
0

Zbyt kompletne rozwiązanie oparte na:

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

Prosty sposób na ukrycie pola wejściowego i zastąpienie go obrazem, tutaj po rozwiązaniu, które również wymagają hackowania kątowego, ale wykonują zadanie [TriggerEvent nie działa zgodnie z oczekiwaniami]

Rozwiązanie:

  • umieść pole wejściowe na wyświetlaczu: brak [pole wejściowe istnieje w DOM, ale nie jest widoczne]
  • umieść obraz zaraz po nim Na obrazie użyj nb-click (), aby aktywować metodę

Po kliknięciu obrazu zasymuluj działanie DOM „kliknij” w polu wejściowym. Zrobione!

 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
źródło
1
Nie możesz użyć angular.element (this) .scope (), jest to funkcja debugowania!
Cesar
0

Zrobiłem to w ten sposób;

<!-- 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
źródło
0

Innym interesującym sposobem nasłuchiwania zmian danych wejściowych pliku jest kontrola nad atrybutem ng-model pliku wejściowego. Oczywiście FileModel to niestandardowa dyrektywa.

Lubię to:

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

Kod JS ->

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

Mam nadzieję, że może komuś pomóc.

halbano
źródło
0

Elementy kątowe (takie jak element główny dyrektywy) są obiektami jQuery [Lite]. Oznacza to, że możemy zarejestrować detektor zdarzeń w następujący sposób:

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

To jest delegacja zdarzenia jQuery. Tutaj nasłuchiwanie jest dołączone do elementu głównego dyrektywy. Kiedy zdarzenie zostanie wyzwolone, będzie się przesuwać do zarejestrowanego elementu, a jQuery określi, czy zdarzenie powstało na elemencie wewnętrznym pasującym do zdefiniowanego selektora. Jeśli tak, przewodnik uruchomi ogień.

Korzyści z tej metody to:

  • moduł obsługi jest powiązany z elementem $, który zostanie automatycznie wyczyszczony po zniszczeniu zakresu dyrektywy.
  • brak kodu w szablonie
  • będzie działać, nawet jeśli docelowy delegat (dane wejściowe) nie został jeszcze zrenderowany podczas rejestracji modułu obsługi zdarzeń (na przykład podczas używania ng-iflub ng-switch)

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

Matt S.
źródło
-1

Możesz po prostu dodać poniższy kod w programie onchange, a on wykryje zmianę. możesz napisać funkcję na X-Click lub coś do usunięcia danych pliku.

document.getElementById(id).value = "";
Jijo Thomas
źródło
To zła praktyka, a nie kanciasta.
Sebastian