【问题标题】:Delaying excecution of a controller until all json files are loaded延迟控制器的执行,直到加载所有 json 文件
【发布时间】:2017-12-04 14:17:36
【问题描述】:

我仍然对 AngularJS 有所了解,所以我的问题可能存在缺陷,但是否可以延迟控制器的执行,直到加载 json 文件(在不同的函数中)?
这是控制器:

app.controller('progressCtrl', ['$scope', '$routeParams', '$http', 'content', function($scope, $routeParams, $http, content) {

    function initScope() {
        $scope.pageId = $routeParams.pageId; 
        $scope.totalPages = 0;

        content.success(function(data) {
        $scope.pages = data;
        angular.forEach($scope.pages, function(value, key) {
            if ($scope.totalPages <= value.id) $scope.totalPages = value.id;
            if ($scope.pageId == value.id) {
                value.class = "active";
            } else {
                value.class = "";
            }
            if (incompleteObjectives.length>0) {
                var matchFound = false;
                for (var i=0;i<incompleteObjectives.length-1;i++) {
                    if (incompleteObjectives[i].indexOf('page-'+value.id+'-')) {
                        matchFound = true;
                    }
                }
                if (!matchFound) {
                    value.class += " completed";
                }
            } else {
                value.class += " completed";
            }
        });
    });
}

    initScope();

}]);

这里是调用'getData'函数的工厂,这是加载json文件的地方......

/*Factory that runs http.get on content.json, used by multiple controllers */
app.factory('content', ['$http', function($http) {
    var url = 'json/content.js';
    return $http.get(url)
        .success(function(data) {

            for (var i = 0; i < data.length; i++) {
                numberOfPages += 1;
            }
            // load page .js files
            var tempData = getData(0);
            for (var i = 1; i < numberOfPages; i++) {
                (function(i) {
                    tempData = tempData.then(function() {
                        return getData(i);
                    });
                }(i));
            }

            return data;
        })
        .error(function(data) {
            return data;
        });
}]);


// loads individual page data, stores in pageData
function getData(id) {
    return $.ajax({
        url: 'json/page' + (id + 1) + '.js',
        dataType: 'json'
    }).done(function(d) {
        if(firstRun)
            addPageObjectives(id, d)
        // push data into pageData array, for use later when pages are routed to
        console.log("pushing page data into pageData for page "+id);
        pageData.push(d);
    }).fail(function() {
        // console.log('ERROR loading page index ' + id);
    });
}

为了完整起见,这里是 angular html,它具有 ng-class:

<div ng-if="content.titletext!=undefined" class="sidetitle" ng-controller="progressCtrl">
                <div class="container">
                    <div>
                        {{content.titletext}}
                    </div>
                    <div class="progress-buttons">
                        <div ng-repeat="(skey,page) in pages" class="button {{skey+1}}" ng-class="page.class" ng-attr-id="{{'nav-'+(skey+1)}}">
                            <div ng-switch="page.titletext || '_undefined_'">
                                <span ng-switch-when="_undefined_">{{skey+1}}</span>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="container">
                    <div ng-if="content.chapterTitleText!=undefined" class="side-chapter-title">
                        {{content.chapterTitleText}}
                    </div>
                    <div class="progress-pages">
                        <span>{{pageId}} of {{totalPages}}</span>
                    </div>
                </div>
            </div>

最终它是我想要访问的 pageData 数组,但此时进度控制器正在运行,只加载了第一个 json 文件,所以我只能从第一页访问详细信息。我希望在加载所有 json 文件时运行 progressController 中的代码 - 并且仍然可以访问 value.class 属性。

这可能吗?
谢谢。

更新
从下面的答案中,这就是我尝试过的。我知道以下内容是错误的,但如果可能的话,我想要一些关于错误的提示。

/*Factory that runs http.get on content.json, used by multiple controllers */
app.factory('content', ['$http', function($http) {
    var promise;
    var url = 'json/content.js';
    function init() {
        console.log("Good news! content factory init function does get called!");
        promise = $http.get(url).then(function success(data) {
            for (var i = 0; i < data.length; i++) {
                numberOfPages += 1;
            }
            console.log("data.length = "+data.length); // this outputs as 'undefined'; I'm not sure why, given loading was, presumably, successful
            // load page .js files
            var tempData = getData(0);
            for (var i = 1; i < numberOfPages; i++) {
                (function(i) {
                    tempData = tempData.then(function() {
                        return getData(i);
                    });
                }(i));
            }
            return data;
        }); // I took off the .error lines because they threw an error - I'm assuming the syntax didn't match the new 'success' syntax above
    }

    init(); // I added this - not sure if that's right, but otherwise the init function didn't run...

    function getData(id) {
        return $.ajax({
            url: 'json/page' + (id + 1) + '.js',
            dataType: 'json'
        }).done(function(d) {
            if(firstRun)
                addPageObjectives(id, d)
            // push data into pageData array, for use later when pages are routed to
            console.log("pushing page data into pageData for page "+id);
            pageData.push(d);
        }).fail(function() {
            // console.log('ERROR loading page index ' + id);
        });
        return promise;
    }

    // public API
    return {
        getData: getData
    }

}]);

在其他地方,在控制器中我使用了行,例如:

content.getData().then(function(data) {
    ....
}

...访问页面数据(我认为这不起作用)。我猜这个更新在很多地方都是错误的,但如果人们愿意提供帮助,那就太好了。再次感谢。

【问题讨论】:

  • (使用$http $.ajax 只是粗鲁)
  • 不出所料,我根本不知道你的意思。我认为这是某种角度的笑话。如果是这样 - 好一个!我得到它! (说清楚,我不明白)。
  • 我认为他们说你同时使用这两种方法而不是只选择一种方法很奇怪
  • 我们有一个团队正在为此工作,而我刚刚回到这个项目(这是一种有效的躲闪方式吗?)。我还是一个角度新手,所以我不明白为什么使用这两种方法很重要。如果有人对(我认为)更大的问题有答案,我很想听听。
  • @moosefetcher 使用 angularjs' $http 将触发范围摘要

标签: javascript html angularjs json


【解决方案1】:

在页面出现之前加载数据可以在你的配置中使用resolve 来完成。

只需添加一个函数并在配置中调用它,即可将您的工厂/服务转换为 解析器

$routeProvider
.when("/", {
    templateUrl : "...",
    controller : "...",
    resolve : {
        resolved_data: function resolveData($routeParams) {
            return content.resolveData($routeParams.pageId);
        }
    }
})

然后您的控制器可以从中获取结果:

app.controller('progressCtrl', ['resolved_data', ... function( resolved_data, ... ){
    $scope.some_content = resolved_data.data; 
    ...
}])'

由于它从$http返回数据,控制器应该处理一个promise:

app.controller('progressCtrl', ['resolved_data', ... function( resolved_data, ... ){
    resolved_data.then((res)=>{
        $scope.some_content = res.data; 
    });
    ...
}])'

【讨论】:

  • 天哪。每次我尝试学习有关 Angular 的知识时,都会有一个新的行话需要一个完整的背景故事来解释。但是谢谢你的回答。我怀疑,我不会纠缠你以获得更多外行风格的细节,而是会对你的答案进行一些研究并在几个月内回复你。 (除非你认为我可以复制并粘贴你的答案,它会神奇地工作。不,我不希望)。但老实说,再次感谢!
  • 如果您将“内容”工厂中的所有内容包装到一个函数中,它可能会起作用。尝试将其初始化为var service = {};,使用service.resolveData = function(pageId){...} 语法将函数附加到其中,并使用return service; 返回工厂。我的其余代码应该可以一起工作
  • 这无助于 OP 返回甚至进行解析所需的承诺
【解决方案2】:

我的建议是在您的内容工厂中公开一个 getData 函数,该函数返回一个带有数据结果的 Promise。您可以使用 $q 服务来聚合您的 pageData http 调用。

app.factory('content', ['$http', '$q', function($http, $q) {
var promise;
var url = 'json/content.js';
function init() {
    promise = $http.get(url).then(function success(data) {
        var numberOfPages = 0;
        var dataRequests = [];
        for (var i = 0; i < data.length; i++) {
            numberOfPages += 1;
        }
        // load page .js files
        for (var i = 0; i < numberOfPages; i++) {
            dataRequests.push(fetchPageData(i));
        }

        return $q.all(dataRequests).then(function (pageData) {
            /* pageData is an array of results from fetchPageData calls */
            return pageData;
        });
    });
}

function fetchPageData(id) {
    return $http({
        method: 'GET',
        url: 'json/page' + (id + 1) + '.js',
        responseType: 'json'
    }).then(function(d) {
        return d;
    }, function(error) {/*handle error*/});
}

function getData () {
    return promise; /* this is the pageData result from $q.all above */
}

init();

// public API
return {
    getData: getData
}
...

在您的控制器中:

content.getData().then(function(data) {
    //do stuff here
})

【讨论】:

  • $http 已经返回一个承诺时,使用$q.defer$http 是一种反模式。 What is the explicit promise construction antipattern and how do I avoid it?
  • 感谢您的回答。我很想了解这可能是如何工作的,但我认为这里还不足以让我猜测其余的内容。我有太多关于当前代码应该去哪里以及需要如何更改的问题。但是当我阅读 Angular 文档或教程时,它们似乎都太抽象了,以至于看不出它们是如何实际应用的。我不认为我一开始就对抽象有所了解。哦,好吧。
  • @charlietfl 是的,我认为它会更容易编写,但我会编辑以返回 $http
  • @moosefetcher 只需在...http calls &amp; data processing 部分填写您需要获取控制器所需的正确数据 - 获取页面并加载页面数据。处理完成后,在 $http 成功函数中返回结果,注入“内容”工厂的控制器可以使用 getData() 访问它们。
  • @JusMalcolm 我已经尝试实施您的建议。我怀疑它不起作用的原因是我没有对原始“getData”函数中的代码做任何事情。是否应该在内容工厂的新“getData”功能中使用?如果是这样,我该如何处理“return $.ajax”部分?如何包含代码以遍历页面并返回“承诺”。如果您有任何建议,您将为我节省一天的痛苦。谢谢。
猜你喜欢
  • 2014-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-01
  • 1970-01-01
  • 2015-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多