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.



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


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

    }
  };
});

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

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

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


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

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

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


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

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>


Ś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!!!
    };

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

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.


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/


-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 = "";

To zła praktyka, a nie kanciasta.
Sebastian
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.