【问题标题】:File pick with Angular JS使用 Angular JS 选择文件
【发布时间】:2013-05-13 23:01:28
【问题描述】:

我想用 AngularJS 获取一个文件:

HTML:

<div ng-controller="TopMenuCtrl">
    <button class="btn" ng-click="isCollapsed = !isCollapsed">Toggle collapse</button>
    <input type="file" ng-model="filepick" ng-change="pickimg()" multiple />
    <output id="list"></output> 
</div>

javascript:

angular.module('plunker', ['ui.bootstrap']);
function TopMenuCtrl($scope) {
    $scope.pickimg = function() {
        alert('a');
    };
}

如何在 AngularJS pickimg 函数上绑定输入文件 onchange 操作? 以及如何操作之后上传的文件?

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    Angular 还不支持 input[type=file]ng-change,因此您必须自己滚动 onchange 实现。

    首先,在 HTML 中,为 onchange 定义 Javascript 如下:

    <input ng-model="photo"
           onchange="angular.element(this).scope().file_changed(this)"
           type="file" accept="image/*" />
    

    然后在你的 Angular 控制器代码中,定义函数:

    $scope.file_changed = function(element) {
    
         $scope.$apply(function(scope) {
             var photofile = element.files[0];
             var reader = new FileReader();
             reader.onload = function(e) {
                // handle onload
             };
             reader.readAsDataURL(photofile);
         });
    };
    

    【讨论】:

    • 我已经成功地使用了同样的技术。它很丑……但这是我们目前最好的。
    • 什么进入...?
    • @user1876508 "获得文件引用后,实例化一个 FileReader 对象以将其内容读入内存。当加载完成时,会触发读取器的 onload 事件,其结果属性可用于访问文件数据。”来自html5rocks.com/en/tutorials/file/dndfiles
    • 请注意,如果您禁用了 debugInfo,这将不起作用(推荐用于生产环境)
    • 请注意,您可以在指令控制器中获取 $element(或在链接函数中获取元素引用),因此您可以在控制器设置中执行以下操作:$element[0].children[0].onchange = () =&gt; {}。这消除了必须在调试模式下运行的需要,因为您可以在仍然可以访问控制器范围的同时获取元素。
    【解决方案2】:

    我使用上述方法尝试在选择新文件时加载预览图像,但是当我这样尝试时它不起作用:

    $scope.file_changed = function(element, $scope) {
    
         $scope.$apply(function(scope) {
             var photofile = element.files[0];
             var reader = new FileReader();
             reader.onload = function(e) {
                $scope.prev_img = e.target.result;
             };
             reader.readAsDataURL(photofile);
         });
    });
    

    我更深入地研究了它,发现 $scope.$apply 应该在 reader.onLoad 中,否则更改 $scope 变量将不起作用,所以我做了以下并且它起作用了:

    $scope.file_changed = function(element) {
    
            var photofile = element.files[0];
            var reader = new FileReader();
            reader.onload = function(e) {
                $scope.$apply(function() {
                    $scope.prev_img = e.target.result;
                });
            };
            reader.readAsDataURL(photofile);
     };
    

    【讨论】:

    • 在视图中预览图片,添加:
    • 修改后的代码才是真正需要的,刚开始想知道为什么我的模型没有更新。
    【解决方案3】:

    Teemu 解决方案不适用于 IE9。

    我为不支持 HTML5 FormData 的浏览器编写了一个简单的带有 Flash polyfill 的 angular 指令,您还可以监听上传进度事件。

    https://github.com/danialfarid/ng-file-upload 演示:http://angular-file-upload.appspot.com/

    <script src="angular.min.js"></script>
    <script src="ng-file-upload.js"></script>
    
    <div ng-controller="MyCtrl">
      <input type="text" ng-model="additionalData">
      <div ngf-select ng-model="files" >
    </div>
    

    控制器:

    Upload.upload({
        url: 'my/upload/url',
        data: additionalData,
        file: files
      }).then(success, error, progress); 
    

    【讨论】:

    • 这是一种更全面的方法。
    【解决方案4】:

    以下是我使用指令的方法。

    指令

    angular
      .module('yourModule')
      .directive('fileChange', function() {
        return {
         restrict: 'A',
         scope: {
           handler: '&'
         },
         link: function (scope, element) {
          element.on('change', function (event) {
            scope.$apply(function(){
              scope.handler({files: event.target.files});
            });
          });
         }
        };
    });
    

    HTML

    <input type="file" file-change handler="fileSelect(files)">
    

    控制器

    fileSelect = function (files) {
          var file = files[0];
          //you will get the file object here
    }
    

    【讨论】:

      【解决方案5】:

      使用上面的Madura's answer,这是读取本地 JSON 文件的完整流程:

      创建指令:

      angular
        .module('app.services')
        .directive('fileChange', function() {
          return {
           restrict: 'A',
           scope: {
             handler: '&'
           },
           link: function (scope, element) {
            element.on('change', function (event) {
              scope.$apply(function(){
                scope.handler({files: event.target.files});
              });
            });
           }
          };
      });
      

      HTML:

      <input type="file" file-change handler="fileSelect(files)">
      

      Javascript:

      $scope.fileSelect = function(files) {
        var file = files[0];
        var reader = new FileReader();
        reader.onload = function(e) {
          console.log("on load", e.target.result);
        }
        reader.readAsText(file);
      }
      

      【讨论】:

      • 当我尝试它时,它不会在this.fileSelect() 中断点。当我将其更改为$scope.fileSelect() 时,它会执行断点,但只报告文件名,而不是完整路径。你能帮我吗?有很多极其复杂的 AngulrJs 文件选择器示例,这是唯一一个看起来简单到可以理解的示例。如此接近,然而……
      • 你是对的,它应该是 $scope 而不是 this。至于文件路径,我认为您看到的行为是正确的行为。根据获得的结果,您应该仍然能够读取文件。
      • 这对我有用,是迄今为止我发现的最简单的实现。
      【解决方案6】:

      这是我为解决这个问题而编写的一个轻量级指令,它反映了附加事件的角度方式。

      你可以像这样使用指令:

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

      【讨论】:

      • html 必须使用 ngModel。 &lt;input type="file" file-change="yourHandler($event, files)" ng-model="myFiles"/&gt;
      【解决方案7】:

      我已作出指示。这是fiddle
      该应用程序适用于挑选 csv 并将它们显示为 html 表格。
      使用 on-file-change 指令,您将能够在控制器本身中定义文件读取和解析(可能是服务)逻辑,这将提供更大的灵活性。请注意,传递给 on-file-change 属性的 ac.onFileChange 函数将成为指令内输入更改事件的处理程序。

      (function (angular, document) {
      
         angular
            .module("app.directives", [])
            .directive("onFileChange", ["$parse", function ($parse) {
               return {
                  restrict: "A",
                  link: function (scope, ele, attrs) {
                     // onFileChange is a reference to the same function which you would define 
                     // in the controller. So that you can keep your logic in the controller.
                     var onFileChange = $parse(attrs.onFileChange.split(/\(/)[0])(scope)
                     ele.on("change", onFileChange)
                     ele.removeAttr("on-file-change")
                  }
               }
            }])
      
         angular
            .module("app.services", [])
            .service("Parse", ["$q", function ($q) {
               var Parse = this
               Parse.csvAsGrid = function (file) {
                  return $q(function (resolve, reject) {
                     try {
                        Papa.parse(file, {
                           complete: function (results) {
                              resolve(results.data)
                           }
                        })
                     } catch (e) {
                        reject(e)
                     }
                  })
               }
            }])
      
         angular
            .module("app", ["app.directives", "app.services"])
            .controller("appCtrl", ["$scope", "Parse", function ($scope, Parse) {
               var ac = this
               ac.fileName = ""
               ac.onFileChange = function (event) {
                  if (!event.target.files.length) {
                     return
                  }
                  Parse.csvAsGrid(event.target.files[0]).then(outputAsTable)
               }
      
               ac.clearInput = function (event) {
                  var input = angular.element(event.target)
                  input.val("")
                  document.getElementById("output").innerHTML = ""
               }
      
               function outputAsTable(grid) {
                  var table = ['<table border="1">']
                  grid.map(function (row) {
                     table.push('<tr>')
                     row.map(function (cell) {
                        table.push('<td>' + cell.replace(/["']/g, "") + '</td>')
                     })
                     table.push('</tr>')
                  })
                  table.push('</table>')
                  document.getElementById("output").innerHTML = table.join("\n")
               }
            }])
      
      })(angular, document)
      table {
        border-collapse: collapse;
      }
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/4.1.2/papaparse.min.js"></script>
      
      <div ng-app="app" ng-controller="appCtrl as ac">
        <label>Select a comma delimited CSV file:-</label>  
        <input id="filePicker" type="file" on-file-change="ac.onFileChange(event)" ng-click="ac.clearInput($event)"/>{{ac.fileName}}  
      </div>
      <div id="output"></div>

      【讨论】:

        【解决方案8】:

        使用 ng-model-controller 的指令:

        app.directive("selectNgFiles", function() {
          return {
            require: "ngModel",
            link: function postLink(scope,elem,attrs,ngModel) {
              elem.on("change", function(e) {
                var files = elem[0].files;
                ngModel.$setViewValue(files);
              })
            }
          }
        });
        

        用法:

        <input type="file" select-ng-files ng-model="fileArray"
               ng-change="pickimg()" multiple>
        

        有关详细信息,请参阅Working Demo of Directive that Works with ng-model

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-07-03
          • 1970-01-01
          • 2015-05-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多