AngularJS:在任何部分页面控制器之前调用特定函数

11
我想在应用程序加载开始时调用一个特定的函数:GetSession()。该函数会进行一个$http调用并从服务器获取一个会话令牌GlobalSessionToken。这个会话令牌随后用于其他控制器逻辑并从服务器提取数据。我已经在主控制器MasterController中在$routeChangeStart事件中调用了这个GetSession(),但由于它是一个异步调用,我的代码会先移动到CustomerController再等待$http响应。
下面是我的代码:
var GlobalSessionToken = '';  //will get from server later 

//Define an angular module for our app 
var myApp = angular.module('myApp', ['ngRoute']); 

//Define Routing for app 
myApp.config(['$routeProvider', function ($routeProvider) { 
    $routeProvider. 
      when('/customer', { 
          templateUrl: 'partials/customer.html', 
          controller: 'CustomerController', 
          resolve: { 
            loadData: function($q){ 
                return LoadData2($q,'home'); 
            } 
          } 
      }). 
      otherwise({ 
          redirectTo: '/home'
      }); 
}]); 

//controllers start here and are defined in their each JS file 
var controllers = {}; 

//only master controller is defined in app.js, rest are in separate js files
controllers.MasterController = function($rootScope, $http){
    $rootScope.$on('$routeChangeStart', function(){

        if(GlobalSessionToken == ''){
            GetSession();
        }

        console.log('START');
        $rootScope.loadingView = true;
    });

    $rootScope.$on('$routeChangeError', function(){
        console.log('ERROR');
        $rootScope.loadingView = false;
    });
};

controllers.CustomerController = function ($scope) { 
    if(GlobalSessionToken != ''){
        //do something
    }
} 

//adding the controllers to myApp angularjs app 
myApp.controller(controllers); 
//controllers end here 


function GetSession(){
    $http({
        url: GetSessionTokenWebMethod,
        method: "POST",
        data: "{}",
        headers: { 'Content-Type': 'application/json' }
    }).success(function (data, status, headers, config) {
        GlobalSessionToken = data;
    }).error(function (data, status, headers, config) {
        console.log(data);
    });
}

我的HTML有以下几个部分:

<body ng-app="myApp" ng-controller="MasterController">
    <!--Placeholder for views-->
    <div ng-view="">
    </div>
</body>

如何确保我的应用程序始终在任何其他控制器调用之前并且仅被调用一次时,在应用程序启动的最开始调用这个GetSession()方法?

编辑:根据Maxim的答案,我添加了run方法。仍需找出等待$http调用返回后再继续控制器的方法。

//Some initializing code before Angular invokes controllers
myApp.run(['$rootScope','$http', '$q', function($rootScope, $http, $q) {
   return GetSession($http, $q);
}]);

function GetSession($http, $q){
    var defer = $q.defer();

    $http({
        url: GetSessionTokenWebMethod,
        method: "POST",
        data: "{}",
        headers: { 'Content-Type': 'application/json' }
    }).success(function (data, status, headers, config) {
        GlobalSessionToken = data;
        defer.resolve('done');
    }).error(function (data, status, headers, config) {
        console.log(data);
        defer.reject();
    });

    return defer.promise;
}
3个回答

12

虽然这里提供的一些解决方案是完全有效的,但在我看来,在路由定义中使用resolve属性是最好的方法。在每个控制器中编写应用逻辑并放在session.then中有点过于繁琐了,我们在一个项目中也采用了这种方法,但效果并不好。

最有效的方法是使用内置的resolve延迟控制器的实例化。唯一的问题是,您必须为每个路由定义添加类似的resolve属性代码,这会导致代码重复。

为了解决这个问题,您可以像这样在帮助函数中修改路由定义对象:

function withSession(routeConfig) {
  routeConfig.resolve = routeConfig.resolve || {};

  routeConfig.resolve.session = ['getSessionPromise', function(getSessionPromise) {
     return getSessionPromise();
  }]

  return routeConfig;
} 

然后,你可以像这样定义你的路由:

$routeProvider.when('/example', withSession({
  templateUrl: 'views/example.html',
  controller: 'ExampleCtrl'
}));

这是我尝试过的众多解决方案中最喜欢的之一,因为它干净而且DRY。


10

您不能延迟控制器的初始化。

您可以将控制器代码放置在会话Promise回调函数内:

myApp.factory( 'session', function GetSession($http, $q){
    var defer = $q.defer();

    $http({
        url: GetSessionTokenWebMethod,
        method: "POST",
        data: "{}",
        headers: { 'Content-Type': 'application/json' }
    }).success(function (data, status, headers, config) {
        GlobalSessionToken = data;
        defer.resolve('done');
    }).error(function (data, status, headers, config) {
        console.log(data);
        defer.reject();
    });

    return defer.promise;
} );

myApp.controller( 'ctrl', function($scope,session) {
   session.then( function() {
      //$scope.whatever ...
   } ); 
} );

替代方案: 如果您不想使用这样的回调函数,您可以将会话请求设置为同步,但那将是一件可怕的事情。


我不得不使用 session.then 而不是 Session.then - chipit24

0

您没有提供任何与GetSession相关的详细信息。对于这种情况,您应该在$routeProvider中定义路由时使用resolve属性。我看到您已经在使用resolve了。

现在您可以将GlobalSessionToken封装到一个Angular服务中,例如GlobalSessionTokenService,并在解析时调用它以在路由加载之前获取令牌。像这样:

resolve: { 
            loadData: function($q){ 
                return LoadData2($q,'home'); 
            },
            GlobalSessionToken: function(GlobalSessionTokenService) {
                 return GlobalSessionTokenService.getToken()  //This should return promise
            }
         } 

然后,您可以使用以下代码将其注入到控制器中:

controllers.MasterController = function($rootScope, $http,GlobalSessionToken){


我没有将我的MasterController放在$routeProvider中,因为我的第一个页面是客户页面,我在第一页加载时路由。所以,我将MasterController添加到我的body标签中,以便它像父控制器一样运行。那么,在这种情况下如何使用resolve和promise,请告诉我。另请参阅我的EDIT,基于Maxim的响应,它可以工作,但不会等待$http响应。 - Pawan Pillai
GetSession() 只是通过 $http 调用从服务器获取会话令牌(用户登录时已在服务器上生成)。 - Pawan Pillai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接