确保一个AngularJS控制器在另一个之前加载

时间:2013-08-22 07:53:32

标签: javascript angularjs

我将在此之前提到这样一个事实:我真的不知道这是否是实现我正在做的事情的最佳方式,而且我对更好的建议持开放态度。

我有一个使用OAUTH2的用户帐户系统,它会在我的数据库中查找用户信息并将其另存为变量$rootScope.userInfo。这位于我的应用程序body附加的控制器中;在这里,我认为最高级别的控制器会在其中存在之前加载,但显然不是。

如果我在$rootScope.userInfo有机会从我的数据库加载此对象之前加载了一个试图访问此mainCtrl对象的视图,则会引发javascript错误并导致Angular中断。

供参考,以下是模板的概念:

<body ng-controller="mainCtrl">
    <header>Content</header>
    <div class='main-view' ng-controller="userProfile">
        <p>{{user.name}}</p>
        <p>{{user.email}}</p>
    </div>
</body>

我正在$rootScope.userInfo加载mainCtrl,如下所示:

$http.get('/api/users/' + $cookies.id).
    success(function(data) {
      $rootScope.userInfo = data.user[0];
      console.log("User info is:");
      console.log($rootScope.userInfo);
      $scope.status = 'ready';
    });

然后,对于我的userProfile控件,我这样做:

function userProfile($scope, $cookies, $http, $routeParams, $rootScope){
  if($scope.status == 'ready'){
    $scope.user = $rootScope.userInfo;
    console.log("Double checking user info, here it is:");
    console.log($rootScope.userInfo);
  }
}

如果我来自应用中不同于$rootScope.userInfo的其他页面,则API有足够的时间查找,我的userProfile页面正常工作。但是,如果进行整页刷新,则$rootScope.userInfo没有时间加载,我会收到错误。

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:14)

您描述的问题是不建议使用$rootScope在控制器之间共享数据的原因之一:它在两个控制器之间创建手动“不可见”依赖关系,您必须在结束时手动修复用户尚未通过其他控制器。

推荐的解决方案是将用户加载逻辑移动到服务中,例如userProfileService,您将其注入需要它的两个控制器中。然后它将被创建一次,并用于两个控制器。在这样的服务中,您可以在控制器请求时使用$http加载用户配置文件,并在下一个操作时从缓存中返回它。这样,依赖关系从两个控制器转到共享服务,而不是从一个控制器转到另一个控制器。

我不是AngularJS文档的忠实粉丝,但这些可能有所帮助:DICreating ServicesInjecting Services

答案 1 :(得分:0)

使用then代替成功并使用ng-include延迟加载子控制器:

<body ng-controller="mainCtrl">
    <header>Content</header>
    <ng-include src="templatePath"></ng-include>        
</body>

在新模板userprofile.html中移动HTML:

<div class='main-view' ng-controller="userProfile">
    <p>{{user.name}}</p>
    <p>{{user.email}}</p>
</div>

在mainCtrl中回调:

$http.get('/api/users/' + $cookies.id).
    then(function(data) {
        /* mainCtrl being the parent controller, simply exposing the 
           properties to $scope would make them available 
           to all the child controllers.*/
        $rootScope.userInfo = data.user[0];
        /* assign the template path to load the template and 
           instantiate the controller */
        $scope.templatePath = "userprofile.html";
    });