获得' $申请已经在进行中'错误,即使我没有明确地调用$ apply()

时间:2014-09-12 19:08:09

标签: angularjs

我正在创建一个有助于上传CSV文件的指令。我使用<input type="file" ng-hide="true"/>和原生JavaScript FileReader()来执行此操作。当我使用原生input选择文件时,一切正常,但我尝试使用自己的&#34;浏览&#34;来自定义文件控件。按钮。问题是,当我尝试这样做时,我得到$ apply已经在进行中错误。

这是我的指示

app.directive('skUploader', function ($timeout) {
    return {
        restrict: 'A',
        replace: true,
        template: '<div>\
                    <input type="file" class="uploadControl" ng-hide="false" />\
                    <input type="text" ng-model="selectedFile" />\
                    <button type="button" class="btnPlain" ng-click="triggerBrowse()">Browse</button>\
                    <button type="button" class="btnOrange fRight" ng-click="uploadCSV()">Upload</button>\
                    <button type="button" class="btnPlain fRight" ng-click="cancel()">Cancel</button>\
                   </div>',
        link: function (scope, elem, attr, ctrl) {

            var uploadControl = elem.find('input[type=file]');
            var payload = null;

            var type = attr.skUploader;
            scope.selectedFile = null;

            scope.uploadCSV = function () {
                $timeout(function () {
                    alert('Upload Successful');
                }, 3000);
            }

            scope.cancel = function () {
                payload = null;
                scope.selectedFile = null;
                scope.model.showUploadPanel = false;

            }

            uploadControl.on('change',function () {
                var fileHandle = uploadControl[0].files[0];
                var reader = new FileReader();
                var buffer;

                scope.selectedFile = fileHandle.name;

                reader.onload = function(){
                    buffer = reader.result;

                    payload = parseCSV(buffer);

                    if (payload) {
                        // Make API call
                        if (type == 'mapping') {
                            console.log(payload);
                        } else {

                        }
                    }
                }

                if (fileHandle.name.indexOf('.csv') > 0) {
                    reader.readAsText(fileHandle);
                } else {

                }

                });

            function parseCSV(buff){

                var entries = [];
                entries = buff.split(/\n/);

                var json = [];


                if (type == 'mapping') {
                    var formatter = {
                        iRowNumber: null,
                        SourceName: null,
                        DestName: null
                    };
                } else if (type == 'passwords') {
                    var formatter = {
                        iRowNumber: null,
                        SourceCreds: {
                            EmailAddress: null,
                            Username: null,
                            Pwd: null
                        }
                    };
                } else {
                    return null;
                }


                for(var i = 1; i < entries.length - 1; i++){
                    var split = entries[i].split(',');

                    var obj = angular.copy(formatter);

                    var j = 0;
                    angular.forEach(formatter, function (value, key) {
                        if (key == 'iRowNumber') {
                            obj[key] = i;
                        } else if (key == 'SourceCreds') {
                            obj[key] = {
                                EmailAddress: split[0],
                                Username: split[1],
                                Pwd: split[2]
                            };
                        }else{
                            obj[key] = split[j];
                            j++;
                        }
                    });

                    json.push(obj);

                }

                return json;

            }

            scope.triggerBrowse = function () {
                uploadControl.trigger('click'); // THIS IS WHAT CAUSES THE ERROR
            }
        }
    };
});

当我使用&#34;浏览&#34;按钮到trigger('click') uploadControl,Angular吐出错误

Error: [$rootScope:inprog] $apply already in progress

但我没有在任何地方使用scope.$apply()所以我无法弄清楚问题。它应该只是触发<input type="file"/>上的click事件,这将启动操作系统本机文件浏览器。

更新

这可能与this issue有关吗?​​

更新2

经过一些研究后,我发现this Git issue vojta(一个核心AngularJS dev)回应说我们需要将事件包装在$timeout中。我这样做了,它确实有效!有人可以向我解释为什么需要这么详细吗?谢谢

1 个答案:

答案 0 :(得分:24)

当您在不使用Angular函数的情况下更改范围时(例如,使用jQuery click事件处理程序而不是ngClick),它会变得非常脾气暴躁。有时它只会忽略你的更改,直到它再次运行,有时你会在一个特别脾气暴躁的时刻抓住它,因为它已经忙于其他事情并且它会给你你得到的错误。

要成为Angular友好者,您需要使用其事件系统来更新范围。如果您不能这样做,您可以告诉它您正在进行更改,以便它可以按照自己的计划处理这些更改。这种方式是$timeout - 一个与更新循环集成的JavaScript timeout的角度友好版本。在没有持续时间参数的情况下调用它只意味着&#34;下次您准备好更改范围时,请执行此操作。&#34;

顺便打电话$apply不是个好主意,只需使用$timeout