【问题标题】:AngularJS Problems with FileReader and $scope.$watch?FileReader 和 $scope.$watch 的 AngularJS 问题?
【发布时间】:2015-01-26 00:58:18
【问题描述】:

我有一个奇怪的问题(至少对我而言),希望有人能提供帮助。也许我只是在想这个错误的方式或什么的。尝试在 javascript 中处理文件对我来说是全新的。

我有一个指令,其模板如下所示:

<input type="file" ng-file-select ng-model="files">
<button ng-click="onFileSelect()">Upload</button>

在指令的控制器中,我正在这样做:

$scope.onFileSelect = function() {
  reader = new FileReader();
  reader.onload = function() {
    $scope.file_contents = this.result;
  };
  reader.readAsArrayBuffer($scope.files[0]);

//debugger;
  $scope.$watch('file_contents',function(file_contents) {
    console.log(file_contents);
    if (angular.isDefined(file_contents)) {
      ... bunch of code attempting to deal with file_contents ...
    }
  });
};

我选择一个文件,然后单击上传按钮。 (file_contents) 的 console.log 未定义。只有当我第二次点击按钮时,它才有值。

如果我取消注释调试器,$scope.file_contents 在我检查它的时候就有了一个值。

所以,file_contents 需要一点时间来设置(这是意料之中的),但 $watch 似乎从来没有注意到?这有什么奇怪的原因吗? $watch 不能与 FileReader 对象一起使用吗?

编辑 1:

好的。这里有更多信息。按照 PSL 的建议,我现在有了这样的代码:

$scope.onFileSelect = function() {
  reader = new FileReader();
  reader.onload = function() {
    file_contents = this.result;
    upload_file($scope.files[0],file_contents);
  };
   reader.readAsArrayBuffer($scope.files[0]);
};

upload_file = function(file,file_contents) {
  <lots of cool file_contents-y stuff>
};

为什么我仍然收到 $digest 错误?我没有 $watches 了。我没有对upload_file 中的$scope 做任何事情。也没有来自 $digest 错误的堆栈跟踪来帮助我。我得到的只是:

Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.3.0-beta.10/$rootScope/inprog?p0=%24digest

这是怎么回事?

【问题讨论】:

    标签: javascript angularjs filereader


    【解决方案1】:

    Watch 似乎从来没有注意到,因为您正在更新角度上下文之外的范围。您需要使用 scope.$apply() 或任何其他方式(例如在 onload 异步函数中使用 $timeout 等)手动调用摘要循环。并且最好将手表移到 onFileSelect 方法之外,否则您将继续添加每次上传点击的观看次数。

    试试:

    $scope.onFileSelect = function() {
      reader = new FileReader();
      reader.onload = function() {
        $scope.file_contents = this.result;
        $scope.$apply(); /<-- here
      };
      reader.readAsArrayBuffer($scope.files[0]);
    
    };
    
    $scope.$watch('file_contents',function(file_contents) {
        console.log(file_contents);
        if (angular.isDefined(file_contents)) {
          ... bunch of code attempting to deal with file_contents ...
        }
    });
    

    不确定完整的场景,但您可能甚至不需要创建手表,只需将文件处理包装在方法中并从 onload 调用该方法并执行 scope.$apply()。您的手表第一次执行的原因是因为它总是在设置后运行以启动脏检查,并且到那时 async onload 尚未在范围上设置任何值,即使它确实设置了摘要周期也不是意识到这一点。

    【讨论】:

    • 这是行之有效的方式。但是,我收到了这些愚蠢的 $digest 错误。我可以忽略它们吗?我已经尝试了 1,000 种不同的方法来摆脱它们,但没有一个有效。 $timeout 不是解决方案。如果由于某种原因我决定单击上传,请等待它上传,然后再次单击上传,在某些时候,我会收到 $digest 错误。我将在下面发布更多代码的后续内容。我不再用 $scope 做 ANYTHING 并且无法弄清楚为什么它仍然给我带来问题。
    • @JimMacKenzie 你不应该得到摘要错误,你有演示吗?
    • 我从来没有设置过 jsfiddle 或类似的东西。可能需要一段时间。
    • 您没有范围。$apply 对吗?如果是,请删除它并尝试。我不知道您使用的所有插件。所以必须假设事情
    • 没有。在我的代码中,我没有使用 $scope.$apply。在你提到它之前,我什至都不知道它。我已经用我的新代码更新了这个问题。没有 $scope.$apply.
    【解决方案2】:

    对此有更好的方法.. 你定义一个这样的指令..

    //directive.js
    (function() {
        angular.module('<yourApp>').
        directive('fileread', ['$parse', function($parse){
            // Runs during compile
            return {
                restrict: 'A',
                link: function($scope, iElm, iAttrs, controller) {
    
                    var model = $parse(iAttrs.fileread);
                    var modelSetter = model.assign;
    
                    iElm.bind('change', function(){
                        $scope.$apply(function(){
                            modelSetter($scope, iElm[0].files[0]);
    
                        });
                    });     
                }
            };
        }]);
    })();
    

    然后在你的控制器(或其他指令)中创建一个这样的模型..

    //controller.js
    ...
    $scope.photo.data = null;
    ...
    
    $scope.photo.upload = function() {
        fd = new FormData();
        fd.append('<name_of_form_field_the_photo_should_be_sent_as>', $scope.photo.data);
    
    $http.post('<url>', fd, {
         transformRequest: angular.identity,
         headers: {'Content-Type': undefined}
    })
    

    最后在你的 html 中。

    <input type = 'file' fileread = "photo.data">
    <button ng-click = "photo.upload"> </button>
    

    希望对你有帮助。

    【讨论】:

    • 不幸的是,没有。我必须发送多部分/混合,如果不创建自己的代码来发送数据,就无法做到这一点。我不能使用 'Content-type': undefined,因为它默认为 multipart/form-data。
    猜你喜欢
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-28
    • 2015-05-03
    相关资源
    最近更新 更多