AngularJS仅在父控制器完成异步调用时加载控制器

Posted

技术标签:

【中文标题】AngularJS仅在父控制器完成异步调用时加载控制器【英文标题】:AngularJS Load controllers only when parent controller finishes async call 【发布时间】:2013-12-07 11:29:17 【问题描述】:

我正在使用 AngularJS 和 Google Cloudendpoint 构建网站

架构如下。我有:

1 - 管理整个站点显示(顶部菜单、模式等...)的 MainCtrl

<body ng-controller="MainCtrl" ng-cloak main-directive>
    ...
    <div ng-view></div>
    ...
</body>

2 - 每个页面都有一个控制器的 ng-view,每个页面都有自己的控制器,由 $routeprovider 管理

main.js

.config([
    '$routeProvider',
    '$locationProvider', 
    '$httpProvider',
    function($routeProvider, $locationProvider, $httpProvider) 

                    ...
        $routeProvider.when('/example', 
            templateUrl : 'partials/example.html', 
            controller  : 'ExampleCtrl',
            access      : accessLevels.PUBLIC
                    
                    ...
        $routeProvider.otherwise(redirectTo: '/');
    

MainCtrl 需要对 Google Cloud Endpoint 进行异步调用才能使会话看起来像这样。

gapi.client.myapi.app.init().execute(function(resp));

到目前为止,我所做的是将代码包装在 MainCtrl 调用的 Service 函数中,然后在每个子控制器中监视 initDone 值,该值在异步调用完成时在 MainCtrl 中更新。

主控件

SessionService.init().then(function(data)
    $scope.initDone = SessionService.getInitDone();
);

服务

angular.module('myapp.services').service('SessionService', function ($http, $q, $rootScope) 

this.initDone = false
this.session = 

this.init = function()
    var s = this; 
    var deferred = $q.defer();
    gapi.client.myapi.app.init().execute(function(resp)
        deferred.resolve(resp);
        s.initDone = true;
    );
    return deferred.promise;


this.getInitDone = function()
    return this.initDone;

);

ExampleCtrl

angular.module('myapp.controllers').controller('ExampleCtrl', function MainCtrl($scope, $timeout, $log, $location, SessionService) 

$scope.$watch('initDone', function(newVal, oldVal)
    $scope.testValue = SessionService.getInitDone()
)

);

虽然这项工作我不确定它是否是正确的解决方案。我希望能够让 ng-view 等到 MainCtrl 完成他的工作。

因为按照我的方式进行操作意味着一旦对端点的调用得到更新,从用户的角度来看这看起来不太好,就会显示 ng-view。

我尝试在 $routeProvider 中使用 resolve 并从那里进行 API 调用,但是我的 MainCtrl 没有正确获取会话值。

我的解决方案是:

   $routeProvider.when('/example', 
templateUrl : 'partials/example.html', 
controller  : 'ExampleCtrl',
access      : accessLevels.PUBLIC,
    resolve     : MainCtrl.init

但 MainCtrl 在 $routeProvider 中不可用

另一个想法是拦截 ng-view 调用,但我不确定在哪里执行此操作并同时访问 Service。

感谢您的任何想法/帮助

编辑

我试图在我的 main.js 中做这样的事情

        $routeProvider.when('/example', 
            templateUrl : 'partials/example.html', 
            controller  : 'ExampleCtrl',
            access      : accessLevels.PUBLIC,

            resolve: 
                sessionData: ["$q", "SessionService", function($q, SessionService) 
                    //console.log(SessionService.getTest())

                    var deferred = $q.defer();
                    gapi.client.myapi.app.init().execute(function(resp)
                        deferred.resolve(resp);
                    );
                    return deferred.promise;
                ]

        );

如果我希望每个子控制器调用 api,这会起作用。不过,我想要的是让子控制器使用 MainCtrl 准备就绪后初始化的任何内容。基本上只在 MainCtrl 准备好时才加载视图和关联的控制器(包括异步调用

【问题讨论】:

【参考方案1】:

根据下面的 cmets 编辑

这个想法是用 ng-if 阻止视图,直到 api 调用返回:

// Session service
 this.init = function()
      if (!this.initDone)
          var s = this;
          var deferred = $q.defer();
          gapi.client.myapi.app.init().execute(function(resp)
              deferred.resolve(resp);
              s.initDone = true;
          );
          return deferred.promise;
      
 




// MainController.js :
 $scope.initDone = false;
 SessionService.init().then(function(
     $scope.initDone = SessionService.getInitDone();
  );


// index.html: 
<div ng-if="initDone"> 
    <div id="view" ng-view> </div>
</div> 

旧答案: 您能否将对 Google 的异步调用移至一项服务并让该服务返回一个承诺

     myApp.service('DataService', ['$rootScope', '$http', function ($rootScope, $http) 

     var promise = $http.get("http://google.com/theservice").success(function (data) 
             ......
      );
      this.promise = promise

然后在您的路由设置中进行解析,该解析将在调用 Google 服务返回后等待唯一加载控制器

    when('/example', templateUrl: 'partials/example.html', 
                   controller: 'ExampleCtrl', 
         resolve: 
            'MyServiceData': function (DataService) 
                return DataService.promise;
            
        ).

编辑:看看这里如何返回一个 API 调用的承诺 https://***.com/a/16117041/514463

【讨论】:

我实际上尝试了与 defer 和 resolve 类似的东西(参见我上面的编辑),但我的问题是我仍然需要 MainCtrl 来获得调用的结果。我不希望子控制器调用该服务,我希望他们能够在 MainCtrl 准备好后使用它调用的内容。所以想法是修改 ng-view 以便仅在 MainCtrl 准备好时加载视图和控制器 我的代码有点不同,因为据我所知,Cloud Endpoint 不支持 Promise Facebook 示例展示了如何通过不直接支持它们的 API 调用来使用 Promise - 请参阅他在帖子中的 plunker 示例 您能否在主控制器的 google api 成功函数中简单地设置一个 $scope.initialised = true ,并且在您的 example.html 中有一个大块围绕它的全部或大部分:... 我已经修改了上面的原始答案。祝你好运。

以上是关于AngularJS仅在父控制器完成异步调用时加载控制器的主要内容,如果未能解决你的问题,请参考以下文章

如何仅在用户完成键入时才调用 angularjs ngChange 处理程序

循环函数中的异步调用。仅在异步完成时返回

仅在 firebase 调用完成时加载布局

使用 AngularJS 调用异步控制器操作方法

加载 angularjs 视图后加载 jquery 模块?

ng-bind-html 不更新记录 angularjs 1.6