简体   繁体   English

AngularJS FileReader和$ scope。$ watch有问题吗?

[英]AngularJS Problems with FileReader and $scope.$watch?

I've got a weird one (for me at least) that hopefully someone will be able to help with. 我有一个奇怪的人(至少对我来说),希望有人能够提供帮助。 Maybe I'm just thinking about this the wrong way or something. 也许我只是在想这是错误的方式。 Attempting to work with files in javascript is completely new to me. 尝试使用javascript中的文件对我来说是全新的。

I've got a directive whose template looks like this: 我有一个指令,其模板如下所示:

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

In the controller for the directive, I am doing this: 在指令的控制器中,我正在这样做:

$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 ...
    }
  });
};

I choose a file, then click the Upload button. 我选择一个文件,然后单击“上载”按钮。 The console.log of (file_contents) is undefined. (file_contents)的console.log未定义。 Only when I click the button the second time, does it have a value. 只有当我第二次单击该按钮时,它才具有值。

If I uncomment the debugger, $scope.file_contents has a value by the time I'm checking it. 如果取消注释调试器,则$ scope.file_contents在我检查它时具有一个值。

So, file_contents takes a moment to get set (which is expected), but the $watch never seems to notice? 因此,file_contents需要花一点时间进行设置(这是预期的),但是$ watch似乎从未注意到吗? Is there some strange reason for this? 有什么奇怪的原因吗? Does $watch not work with FileReader objects? $ watch是否不适用于FileReader对象?

Edit 1: 编辑1:

Okay. 好的。 Here's some more information. 这是更多信息。 Following PSL's advice, I now have the code as such: 遵循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>
};

Why am I still getting $digest errors? 为什么我仍然会收到$ digest错误? I have no $watches any more. 我没有美元的手表了。 I don't do anything with $scope within upload_file. 我在upload_file中对$ scope不执行任何操作。 There's no stack trace from the $digest error to help me, either. 也没有$ digest错误的堆栈跟踪来帮助我。 All I get is: 我得到的是:

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

What's doing this? 这是在做什么

Watch never seems to notice because you are updating the scope outside of angular context. Watch似乎永远不会注意到,因为您正在更新角度范围之外的范围。 You would need to manually invoke digest cycle using scope.$apply() or any other way like using a $timeout etc inside onload async function. 您将需要使用scope.$apply()或任何其他方式(例如在onload异步函数中使用$ timeout等scope.$apply()来手动调用摘要循环。 and better move the watch outside of the onFileSelect method otherwise you will keep on adding up watched every upload click. 并最好将手表移到onFileSelect方法之外,否则,您将继续累加每次上传点击的观看次数。

Try: 尝试:

$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 ...
    }
});

Not sure about the complete scenario but you probabaly don't even need to create a watch just wrap the file processing inside a method and invoke the method from onload and perform a scope.$apply(). 不确定完整的场景,但是您甚至不需要创建手表,只需将文件处理包装在方法内部,然后从onload调用该方法并执行scope。$ apply()。 Reason why your watch executes for the first time is because it always runs once it is set up inorder to kick off dirty check and by that time async onload has not set any value on the scope and even if it does set up digest cycle is not aware of it. 您的手表首次执行的原因是,它始终在设置好后开始运行,以启动脏检查,并且到那时异步onload尚未在示波器上设置任何值,即使它确实设置了摘要周期也没有意识到这一点。

There is a much nicer approach to this.. You define a directive like this.. 有一种更好的方法。.您定义一个这样的指令..

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

                    });
                });     
            }
        };
    }]);
})();

Then in your controller(or some other directive) you create a model like this.. 然后在您的控制器(或其他指令)中创建这样的模型。

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

and finally in you html. 最后在你的HTML。

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

I hope it helps. 希望对您有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM