【问题标题】:AngularJs: How to check for changes in file input fields?AngularJs:如何检查文件输入字段的变化?
【发布时间】:2013-07-29 03:44:09
【问题描述】:

我是 Angular 的新手。每当此字段发生“更改”时,我都会尝试从 HTML“文件”字段读取上传的文件路径。如果我使用“onChange”它可以工作,但是当我使用“ng-change”以角度方式使用它时它不起作用。

<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() 永远不会调用。 Firebug 也没有显示任何错误。

【问题讨论】:

标签: angularjs


【解决方案1】:

文件上传控件不支持绑定

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>

而不是

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

你可以试试

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

注意:这要求 Angular 应用程序始终位于 debug mode 中。如果调试模式被禁用,这将在生产代码中不起作用。

并在您的功能更改 而不是

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

你可以试试

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

以下是使用拖放文件上传的文件上传的一个工作示例可能会有所帮助 http://jsfiddle.net/danielzen/utp7j/

Angular 文件上传信息

ASP.Net 中 AngularJS 文件上传的 URL

https://github.com/geersch/AngularJSFileUpload

AngularJs 原生多文件上传与 NodeJS 进度

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

ngUpload - 一个使用 iframe 上传文件的 AngularJS 服务

http://ngmodules.org/modules/ngUpload

【讨论】:

  • onchange="angular.element(this).scope().fileNameChaged(this)" $scope.fileNameChaged = function(element) { console.log('files:', element.files) ;你可以在两个地方改变并检查吗?根据浏览器,您将获得我更新的文件完整路径下面的小提琴是 url 上传文件并检查控制台jsfiddle.net/utp7j/260
  • 这个方法绕过了默认的 AngularJS 处理,所以 $scope.$digest() 没有被调用(绑定没有更新)。之后调用 'angular.element(this).scope().$digest()' 或调用使用 $apply 的范围方法。
  • 这是一个可怕的答案。调用 angular.element(blah).scope() 是一个调试功能,您应该只用于调试。 Angular 不使用 .scope 功能。它仅用于帮助开发人员调试。当您构建应用程序并部署到生产环境时,您应该关闭调试信息。这可以加快你的所有速度!!!因此,编写依赖于 element.scope() 调用的代码是个坏主意。不要这样做。关于创建自定义指令的答案是正确的方法。 @JQueryGuru 你应该更新你的答案。因为它的票数最多,所以人们会听从这个糟糕的建议。
  • 当调试信息被禁用时,此解决方案将破坏应用程序。为生产禁用它是官方建议docs.angularjs.org/guide/production。另请参阅blog.thoughtram.io/angularjs/2014/12/22/…
  • BAD SOLUTION ... ...阅读其他有关“关闭调试”的cmets ... ...并查看sqren的解决方案... ... .. .@0MV1
【解决方案2】:

另一种监听文件输入变化的有趣方法是监视输入文件的 ng-model 属性。当然,FileModel 是自定义指令。

像这样:

HTML -> &lt;input type="file" file-model="change.fnEvidence"&gt;

JS代码->

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

希望它可以帮助某人。

【讨论】:

    【解决方案3】:

    您只需在 onchange 中添加以下代码,它就会检测到更改。您可以在 X click 或其他东西上编写一个函数来删除文件数据..

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

    【讨论】:

    • 这是不好的做法,不像有角度的。
    【解决方案4】:

    我做了一个小指令来监听文件输入的变化。

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

    【讨论】:

    • 如果每次都选择不同的文件,这会很有效。是否也可以在选择相同文件时进行监听?
    • 这有一个非常不幸的“bug”——控制器在customOnChange中没有绑定为“this”,而是文件输入!它让我很长时间没有找到真正的错误。我还不确定如何正确设置“this”来实际调用它。
    • 从今天开始,无论是在 Chrome 还是 Firefox 上,小提琴都不适合我。
    • 这个答案有缺陷,正如@KarelBílek 所说。我无法绕过它并决定使用 angular-file-upload。
    • 这绝对是如何做到的,就像一个魅力。此外,为什么要在生产环境中使用调试功能?
    【解决方案5】:

    Angular 元素(例如指令的根元素)是 jQuery [Lite] 对象。这意味着我们可以像这样注册事件监听器:

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

    这是 jQuery 事件委托。在这里,侦听器附加到指令的根元素。当事件被触发时,它将冒泡到注册的元素,jQuery 将确定事件是否源自与定义的选择器匹配的内部元素。如果是,则处理程序将触发。

    这种方法的好处是:

    • 处理程序绑定到 $element,当指令范围被销毁时,该元素将自动清理。
    • 模板中没有代码
    • 即使在您注册事件处理程序时(例如使用ng-ifng-switch 时)尚未呈现目标委托(输入)也可以工作

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

    【讨论】:

      【解决方案6】:

      ng-change一起使用的“files-input”指令的工作演示1

      要使&lt;input type=file&gt; 元素与ng-change 指令配合使用,它需要一个与ng-model 指令配合使用的custom directive

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

      演示

      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>

      【讨论】:

      • 很好,但指令只被调用一次!如果我更改选择的文件,则不会第二次调用指令!你对这种行为有什么想法吗?
      • DEMO 没有这个问题。创建一个带有reproducible example 的新问题供读者检查。
      • 是的,即使您多次更改所选文件,控制台日志“输入更改”也只会显示一次!
      • 经过测试,它可以在 chromium 上运行,但不能在最新的 firefox 版本上运行……该死的浏览器兼容性!!!
      【解决方案7】:

      该指令也传递选定的文件:

      /**
       *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,如何使用它:

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

      在我的控制器中:

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

      【讨论】:

      • $scope.onFilesSelected 在你的控制器中是什么样子的?
      • 如果文件被打开并且当我们尝试在 Microsoft Edge 浏览器中上传文件时。什么都没有发生。你能帮忙吗?
      • 是的,它在其他浏览器上运行良好。我在 microsoft edge @ingaham 中遇到问题
      • 这正是我需要的,谢谢。
      【解决方案8】:

      我是这样做的;

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

      【讨论】:

        【解决方案9】:

        干净的方法是编写自己的指令来绑定到“更改”事件。 只是为了让您知道 IE9 不支持 FormData,因此您无法真正从更改事件中获取文件对象。

        您可以使用已经支持 IE 的 ng-file-upload 库和 FileAPI polyfill,并简化将文件发布到服务器的过程。它使用指令来实现这一点。

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

        【讨论】:

        • 这个例子已经过时了。从文档here 获取新的。
        【解决方案10】:

        太完整的解决方案基于:

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

        一种隐藏输入字段并将其替换为图像的简单方法,在解决方案之后,这也需要对角度进行 hack,但可以完成工作 [TriggerEvent 无法按预期工作]

        解决办法:

        • 将输入字段置于 display:none [输入字段存在于 DOM 中但不可见]
        • 把你的图片放在后面 在图像上使用 nb-click() 激活方法

        单击图像时,在输入字段上模拟 DOM 操作“单击”。等等!

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

        【讨论】:

        • 你不能使用 angular.element(this).scope(),这是一个调试功能!
        【解决方案11】:

        我已经扩展了@Stuart Axon 的想法,为文件输入添加双向绑定(即允许通过将模型值重置回 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("");
                        }
                    });
                }
            };
        }]);
        

        Demo

        【讨论】:

        • 谢谢@JLRishe。注意,其实data-ng-click="vm.theFile=''" 效果很好,不需要写到控制器方法中
        【解决方案12】:

        最简单的 Angular jqLit​​e 版本。

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

        【讨论】:

        • 如果文件被打开并且当我们尝试在 Microsoft Edge 浏览器中上传文件时。什么都没有发生。你能帮忙吗?
        【解决方案13】:

        我建议创建一个指令

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

        该指令可以多次使用,它使用自己的作用域,不依赖于父作用域。您还可以为处理函数提供一些参数。将使用范围对象调用处理程序函数,该对象在您更改输入时处于活动状态。 每次调用 change 事件时,$apply 都会更新你的模型

        【讨论】:

          【解决方案14】:

          与此处的其他一些好的答案类似,我编写了一个指令来解决这个问题,但这个实现更接近地反映了附加事件的角度方式。

          你可以像这样使用指令:

          HTML

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

          如您所见,您可以将选定的文件注入到事件处理程序中,就像将 $event 对象注入到任何 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);
                }
              };
            }]);
          

          【讨论】:

          • 为此苦苦挣扎了几天,直到我尝试了您的答案。谢谢!
          • 如何将附加参数传递给fileChange 表达式?我在ng-repeat 中使用了这个指令,并且传递给attrHandler$scope 变量对于每条记录都是相同的。最好的解决方案是什么?
          【解决方案15】:

          这是对周围其他一些的改进,数据最终会出现在 ng-model 中,这通常是您想要的。

          标记(只需制作一个属性数据文件,以便指令可以找到它)

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

          【讨论】:

          • 是否需要$scope.$apply();?没有它,我的 ng-change 事件似乎仍然会触发。
          • @row1 如果您在操作期间有其他需要了解的问题,您只需要手动触发应用。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-23
          • 2020-07-24
          • 2017-08-24
          • 2017-01-08
          • 1970-01-01
          相关资源
          最近更新 更多