AngularJS FileReader和$ scope的问题。$ watch?

时间:2015-01-26 00:58:19

标签: javascript angularjs filereader

我有一个奇怪的(至少对我来说),希望有人能够提供帮助。也许我只是在考虑这个错误的方式或者其他什么。尝试使用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错误?我已经没有$ watch了。我没有对upload_file中的$ scope做任何事情。 $ digest错误中没有堆栈跟踪来帮助我。我得到的只是:

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

这是做什么的?

2 个答案:

答案 0 :(得分:5)

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调用该方法并执行一个范围。$ apply()。你的手表第一次执行的原因是因为它一旦设置就会一直运行以启动脏检查,到那时async onload没有在范围上设置任何值,即使它确实设置了摘要周期也不是意识到这一点。

答案 1 :(得分:0)

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

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

我希望它有所帮助。