AngularJs: Làm thế nào để kiểm tra các thay đổi trong các trường nhập tệp?


281

Tôi mới đến góc cạnh. Tôi đang cố đọc đường dẫn tệp được tải lên từ trường 'tệp' HTML bất cứ khi nào 'thay đổi' xảy ra trên trường này. Nếu tôi sử dụng 'onChange' thì nó hoạt động nhưng khi tôi sử dụng nó theo cách góc cạnh bằng cách sử dụng 'ng-change' thì nó không hoạt động.

<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 () không bao giờ gọi. Firebird cũng không hiển thị bất kỳ lỗi nào.



Câu trả lời:


240

Không hỗ trợ ràng buộc cho kiểm soát tải lên tệp

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>

thay vì

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

bạn có thể thử

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

Lưu ý: điều này đòi hỏi ứng dụng góc phải luôn ở chế độ gỡ lỗi . Điều này sẽ không hoạt động trong mã sản xuất nếu chế độ gỡ lỗi bị tắt.

và trong chức năng của bạn thay đổi thay vì

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

bạn có thể thử

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

Dưới đây là một ví dụ hoạt động của tải lên tệp với kéo thả tệp tải lên có thể hữu ích http://jsfiddle.net/danielzen/utp7j/

Thông tin tải lên tập tin góc

URL để tải lên tệp AngularJS trong ASP.Net

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

Tải lên đa tệp gốc của AngularJs với tiến trình với NodeJS

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

ngUpload - Dịch vụ AngularJS để tải tệp lên bằng iframe

http://ngmodules.org/modules/ngUpload


2
onchange = "angular.element (this) .scope (). fileNameChaged (this)" $ scope.fileNameChaged = function (Element) {console.log ('files:', Element.files); } Bạn có thể thay đổi ở hai nơi và kiểm tra? tùy thuộc vào trình duyệt, bạn sẽ nhận được đường dẫn đầy đủ của tệp mà tôi đã cập nhật fiddle bên dưới là tệp tải lên url và kiểm tra bảng điều khiển jsfiddle.net/utp7j/260
JQuery Guru

13
Phương thức này phá vỡ quy trình xử lý AngularJS mặc định, vì vậy $ scope. $ Digest () không được gọi (các ràng buộc không được cập nhật). Gọi 'angular.element (this) .scope (). $ Digest ()' sau đó hoặc gọi một phương thức phạm vi sử dụng $ áp dụng.
Ramon de Klein

113
Đây là một câu trả lời khủng khiếp. Gọi angular.element (blah) .scope () là một tính năng gỡ lỗi mà bạn chỉ nên sử dụng để gỡ lỗi. Angular không sử dụng tính năng .scope. Nó chỉ ở đó để giúp các nhà phát triển gỡ lỗi. Khi bạn xây dựng ứng dụng của mình và triển khai vào sản xuất, bạn có nghĩa vụ tắt thông tin gỡ lỗi. Điều này tăng tốc tất cả rất nhiều của bạn !!! Vì vậy, để viết mã phụ thuộc vào các lệnh gọi Element.scope () là một ý tưởng tồi. Đừng làm điều này. Câu trả lời về việc tạo một chỉ thị tùy chỉnh là cách đúng đắn để làm điều này. @JQueryGuru bạn nên cập nhật câu trả lời của mình. Bởi vì nó có nhiều phiếu bầu nhất, mọi người sẽ làm theo lời khuyên tồi tệ này.
sương giá

7
Giải pháp này sẽ phá vỡ ứng dụng khi thông tin gỡ lỗi bị vô hiệu hóa. Vô hiệu hóa điều đó cho sản xuất là một khuyến nghị chính thức docs.angularjs.org/guide/production . Vui lòng xem thêm blog. Dùtram.io / angjj / 2014/12/22 / từ
wiherek

4
GIẢI PHÁP BAD ... ... đọc các bình luận khác về 'tắt gỡ lỗi' ... ... ... và xem sqrengiải pháp ... ... ... @ 0MV1
dsdsdsdsd

477

Tôi đã thực hiện một chỉ thị nhỏ để lắng nghe những thay đổi đầu vào tập tin.

Xem JSFiddle

xem.html:

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

control.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
Điều này hoạt động tốt nếu một tập tin khác nhau được chọn mỗi lần. Có thể nghe khi cùng một tệp được chọn không?
JDawg

12
Điều này có một "lỗi" rất đáng tiếc - bộ điều khiển không bị ràng buộc là "cái này" trong customOnChange, nhưng đầu vào tệp! Nó đã ném tôi đi trong một thời gian dài và tôi không thể tìm thấy lỗi thực sự. Tôi vẫn chưa chắc chắn làm thế nào để thực sự gọi nó với "set" này.
Karel Bílek

4
Fiddle không hoạt động đối với tôi, kể cả trên Chrome hay Firefox, cho đến ngày hôm nay.
seguso

2
Câu trả lời này là thiếu sót như đã nêu bởi @ KarelBílek. Tôi không thể đi xung quanh nó và quyết định sử dụng tải lên tập tin góc.
Gunar Gessner

2
Đây hoàn toàn là cách để làm điều đó, làm việc như một lá bùa. Bên cạnh đó, tại sao bạn lại sử dụng tính năng gỡ lỗi trong sản xuất?
Justin Kruse

42

Đây là một sàng lọc của một số những người khác xung quanh, dữ liệu sẽ kết thúc trong một mô hình ng, đó là những gì bạn muốn.

Đánh dấu (chỉ cần tạo một tệp dữ liệu thuộc tính để lệnh có thể tìm thấy nó)

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

Mã não

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();bắt buộc không? Sự ng-changekiện của tôi dường như vẫn cháy mà không có nó.
hàng1

1
@ row1 bạn chỉ cần thực hiện thủ công một ứng dụng nếu bạn có những thứ góc cạnh khác cần biết về nó trong quá trình hoạt động đó.
Scott Sword

27

Cách rõ ràng là viết chỉ thị của riêng bạn để liên kết với sự kiện "thay đổi". Chỉ cần cho bạn biết IE9 không hỗ trợ FormData nên bạn không thể thực sự lấy đối tượng tệp từ sự kiện thay đổi.

Bạn có thể sử dụng thư viện tải lên ng-file đã hỗ trợ IE với tệp polyfill FileAPI và đơn giản hóa việc đăng tệp lên máy chủ. Nó sử dụng một chỉ thị để đạt được điều này.

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

Ví dụ này đã lỗi thời. Nhận một cái mới từ các tài liệu ở đây .
Gunar Gessner

26

Tôi đã mở rộng ý tưởng của @Stuart Axon để thêm ràng buộc hai chiều cho đầu vào tệp (nghĩa là cho phép đặt lại đầu vào bằng cách đặt lại giá trị mô hình trở về 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("");
                }
            });
        }
    };
}]);

Bản giới thiệu


Cảm ơn bạn @JLRishe. Một lưu ý, thực sự data-ng-click="vm.theFile=''"hoạt động tốt, không cần phải viết nó vào một phương thức điều khiển
Sergey

20

Tương tự như một số câu trả lời hay khác ở đây, tôi đã viết một chỉ thị để giải quyết vấn đề này, nhưng việc thực hiện này phản ánh chặt chẽ hơn cách thức gắn kết các sự kiện.

Bạn có thể sử dụng lệnh như thế này:

HTML

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

Như bạn có thể thấy, bạn có thể tiêm các tệp được chọn vào trình xử lý sự kiện của mình, vì bạn sẽ tiêm một đối tượng sự kiện $ vào bất kỳ trình xử lý sự kiện ng nào.

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

vật lộn trong một vài ngày với điều này cho đến khi tôi thử câu trả lời của bạn. Cảm ơn!
Michael Tranchida

Làm thế nào tôi có thể truyền một tham số bổ sung cho fileChangebiểu thức? Tôi đang sử dụng chỉ thị này bên trong một ng-repeat$scopebiến được truyền attrHandlergiống nhau cho mỗi bản ghi. Giải pháp tốt nhất là gì?

16

Lệnh này cũng chuyển các tệp đã chọn:

/**
 *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, cách sử dụng nó:

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

Trong bộ điều khiển của tôi:

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

$ Scope.onFilesSelected trông như thế nào trong bộ điều khiển của bạn?
ingaham

Nếu một tệp được mở và khi chúng tôi thử tải lên tệp trong trình duyệt Microsoft Edge. Không có gì xảy ra cả. Bạn có thể giúp?
Naveen Katakam

Vâng, nó đang hoạt động tốt với các trình duyệt khác. tôi đang phải đối mặt với vấn đề ở microsoft edge @ingaham
Naveen Katakam

8

Tôi khuyên bạn nên tạo một chỉ thị

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

        };
    }]);

lệnh này có thể được sử dụng nhiều lần, nó sử dụng phạm vi riêng của nó và không phụ thuộc vào phạm vi cha. Bạn cũng có thể cung cấp một số thông số cho chức năng xử lý. Hàm xử lý sẽ được gọi với đối tượng scope, được kích hoạt khi bạn thay đổi đầu vào. $ áp dụng cập nhật mô hình của bạn mỗi khi sự kiện thay đổi được gọi


4

Phiên bản jqLite Angular đơn giản nhất.

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

Nếu một tệp được mở và khi chúng tôi thử tải lên tệp trong trình duyệt Microsoft Edge. Không có gì xảy ra cả. Bạn có thể giúp?
Naveen Katakam

2

Bản demo làm việc của Chỉ thị "nhập tệp" hoạt động với ng-change1

Để làm cho một <input type=file>phần tử hoạt động theo lệnh ng-change , nó cần một lệnh tùy chỉnh hoạt động với chỉ thị 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>


Tuyệt vời nhưng chỉ thị chỉ được gọi một lần! Nếu tôi thay đổi tập tin được chọn thì lệnh không được gọi lần thứ hai! Bạn có ý kiến ​​gì về hành vi này không?
ôm

DEMO không có vấn đề đó. Tạo một câu hỏi mới với một ví dụ có thể lặp lại để người đọc kiểm tra.
georgeawg

vâng, có vấn đề đó, nhật ký giao diện điều khiển "thay đổi đầu vào" chỉ hiển thị một lần ngay cả khi bạn thay đổi các tệp đã chọn nhiều lần!
ôm

sau khi thử nghiệm, nó hoạt động trên crom nhưng nó không có trên phiên bản firefox mới nhất ... tương thích với trình duyệt !!!
ôm

0

Quá đầy đủ giải pháp dựa trên:

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

Một cách đơn giản để ẩn trường đầu vào và thay thế nó bằng một hình ảnh, ở đây sau một giải pháp, cũng yêu cầu hack trên góc nhưng thực hiện công việc [TriggerEvent không hoạt động như mong đợi]

Giải pháp:

  • đặt trường đầu vào trong màn hình: không có [trường đầu vào tồn tại trong DOM nhưng không hiển thị]
  • đặt hình ảnh của bạn ngay sau khi trên hình ảnh sử dụng nb-click () để kích hoạt một phương thức

Khi hình ảnh được nhấp vào mô phỏng hành động DOM 'nhấp' trên trường đầu vào. 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
Bạn không thể sử dụng angular.element (this) .scope (), đây là một tính năng gỡ lỗi!
Cesar

0

Tôi đã làm nó như thế này;

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

Một cách thú vị khác để nghe các thay đổi nhập tệp là theo dõi thuộc tính ng-model của tệp đầu vào. Tất nhiên FileModel là một chỉ thị tùy chỉnh.

Như thế này:

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

Mã JS ->

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

Hy vọng nó có thể giúp ai đó.


0

Các phần tử góc (như phần tử gốc của một lệnh) là các đối tượng jQuery [Lite]. Điều này có nghĩa là chúng ta có thể đăng ký người nghe sự kiện như vậy:

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

Đây là đoàn sự kiện jQuery. Ở đây, người nghe được gắn vào phần tử gốc của lệnh. Khi sự kiện được kích hoạt, nó sẽ nổi bong bóng lên thành phần đã đăng ký và jQuery sẽ xác định xem sự kiện có bắt nguồn từ một thành phần bên trong khớp với bộ chọn đã xác định hay không. Nếu có, xử lý sẽ bắn.

Lợi ích của phương pháp này là:

  • trình xử lý được liên kết với phần tử $ sẽ được tự động dọn sạch khi phạm vi chỉ thị bị hủy.
  • không có mã trong mẫu
  • sẽ hoạt động ngay cả khi đại biểu đích (đầu vào) chưa được hiển thị khi bạn đăng ký xử lý sự kiện (chẳng hạn như khi sử dụng ng-ifhoặc ng-switch)

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


-1

Bạn chỉ cần thêm mã dưới đây vào onchange và nó sẽ phát hiện sự thay đổi. bạn có thể viết một hàm trên X click hoặc một cái gì đó để xóa dữ liệu tệp ..

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

Đây là thực hành xấu và không góc cạnh như thế.
Sebastian
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.