AngularJs:如何检查文件输入字段中的更改?


281

我是新手。每当此字段发生“更改”时,我都试图从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也不会显示任何错误。



1
另请参
见此

Answers:


240

不支持文件上载控件

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应用程序始终处于调试模式。如果禁用了调试模式,则此功能在生产代码中将不起作用。

并且在您的功能中进行更改,而不是

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

你能试一下吗

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

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

角度文件上传信息

ASP.Net中AngularJS文件上传的URL

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

AngularJs本机多文件上传以及NodeJS的进展

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

ngUpload-AngularJS服务,用于使用iframe上传文件

http://ngmodules.org/modules/ngUpload


2
onchange =“ angular.element(this).scope()。fileNameChaged(this)” $ scope.fileNameChaged = function(element){console.log('files:',element.files); 您可以在两个地方更改并检查吗?根据浏览器的不同,您将获得文件的完整路径。我已经更新了小提琴,下面是网址上传文件,并检查控制台jsfiddle.net/utp7j/260
JQuery Guru

13
此方法绕过了默认的AngularJS处理,因此不会调用$ scope。$ digest()(绑定不会更新)。之后调用'angular.element(this).scope()。$ digest()'或调用使用$ apply的scope方法。
拉蒙·

113
这是一个可怕的答案。调用angular.element(blah).scope()是一种调试功能,您仅应将其用于调试。Angular不使用.scope功能。它只是用来帮助开发人员进行调试。在构建应用程序并将其部署到生产环境时,应该关闭调试信息。这加快了您的全部速度!!!因此,编写依赖element.scope()调用的代码是一个坏主意。不要这样 创建自定义指令的答案是正确的方法。@JQueryGuru,您应该更新答案。因为它的票数最多,所以人们会听从这个错误的建议。
2014年

7
禁用调试信息后,此解决方案将破坏应用程序。正式禁止将此功能用于生产docs.angularjs.org/guide/production。另请参见blog.thoughtram.io/angularjs/2014/12/22/…–
wiherek

4
错误的解决方案... ...阅读有关“关闭调试”的其他评论... ...并查看其sqren解决方案... ... ... @ 0MV1
dsdsdsdsd

477

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

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

指令.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
如果每次选择一个不同的文件,这将很好地工作。选择同一文件时是否可以收听?
JDawg 2015年

12
这有一个非常不幸的“错误”-控制器在customOnChange中没有绑定为“ this”,而是文件输入!它使我离开了很长时间,但我找不到真正的错误。我还不确定如何正确设置“ this”来实际调用它。
卡雷尔·比列克(KarelBílek),2015年

4
到目前为止,小提琴不适用于我,无论是在Chrome还是Firefox上。
seguso 2015年

2
如@KarelBílek所述,此答案有缺陷。我无法解决这个问题,因此决定使用angular-file-upload。
Gunar Gessner,2015年

2
这绝对是怎么做的,就像一个魅力。此外,为什么还要在生产中使用调试功能?
贾斯汀·克鲁斯

42

这是对其他一些数据的改进,数据最终将以ng模型结束,这通常是您想要的。

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

<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
$scope.$apply();必需的吗?ng-change没有它,我的活动似乎仍然会触发。
ROW1

1
@ row1只有在执行该操作时需要了解其他有关角度的知识,才需要手动触发应用。
Scott Sword

27

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

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

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

这个例子已经过时了。从此处获取新文档。
Gunar Gessner,2015年

26

我已经扩展了@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("");
                }
            });
        }
    };
}]);

演示版


谢谢@JLRishe。一个便笺,实际上data-ng-click="vm.theFile=''"工作得很好,无需将其写入控制器方法
Sergey

20

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

您可以使用如下指令:

的HTML

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

如您所见,您可以将选择的文件注入事件处理程序中,就像将$ event对象注入任何ng事件处理程序中一样。

Java脚本

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

为此苦苦挣扎了几天,直到我尝试了您的答案。谢谢!
Michael Tranchida

如何将其他参数传递给fileChange表达式?我在中使用此指令,ng-repeat并且$scope传递给attrHandler每个记录的变量相同。最好的解决方案是什么?

16

该指令也传递所选文件:

/**
 *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在控制器中的外观如何?
ingaham '17

如果打开了文件,当我们尝试在Microsoft Edge浏览器中上传文件时。没事 你能帮我吗?
纳文·卡塔卡姆

是的,它在其他浏览器上运行正常。我在Microsoft Edge @ingaham中面临问题
Naveen Katakam

8

我建议创建一个指令

<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都会更新您的模型


4

最简单的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浏览器中上传文件时。没事 你能帮我吗?
纳文·卡塔卡姆

2

适用于1的“文件输入”指令的工作演示ng-change

要使<input type=file>元素在ng-change 指令中起作用,需要一个与ng-model指令一起使用的自定义指令。

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


很棒,但指令只被调用一次!如果我更改所选文件,则不会再次调用该指令!您对此行为有任何想法吗?
hugsbrugs

演示没有这个问题。创建一个带有可复制示例的新问题,以供读者检查。
georgeawg

是的,它有问题,即使您多次更改所选文件,控制台日志“输入更改”也只显示一次!
hugsbrugs

经过测试,它可以在铬上工作,但不能在最新的Firefox版本上使用……该死的浏览器兼容性!
hugsbrugs

0

过于完整的解决方案基于:

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

一种简单的隐藏输入字段并将其替换为图像的简单方法,在解决方案之后,这也需要对angular进行修改,但可以完成工作[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!!!
    };

1
您不能使用angular.element(this).scope(),这是一个调试功能!
Cesar

0

我是这样做的;

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

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

像这样:

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

JS代码->

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

希望它可以帮助某人。


0

角元素(例如指令的根元素)是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-if或时ng-switch)尚未呈现目标委托(输入)也将起作用

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


-1

您可以简单地在onchange中添加以下代码,它将检测到更改。您可以通过单击X或编写一些函数来删除文件数据。

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

这是不好的做法,而不是棱角分明。
塞巴斯蒂安
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.