AngularJS FileReader 和 $scope.$watch 有问题?

AngularJS Problems with FileReader and $scope.$watch?

我有一个奇怪的(至少对我来说)希望有人能够提供帮助。也许我只是在以错误的方式思考这个问题。尝试使用 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

这是做什么的?

Watch 似乎从来没有注意到,因为您正在更新 angular 上下文之外的范围。您需要使用 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 ...
    }
});

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

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

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

希望对您有所帮助。