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