【问题标题】:AngularJS: Call a particular function before any partial page controllersAngularJS:在任何部分页面控制器之前调用特定函数
【发布时间】:2014-02-03 07:18:33
【问题描述】:

我想在我的应用程序加载开始时调用一个特定的函数:GetSession()。此函数进行$http 调用并从服务器获取会话令牌:GlobalSessionToken。然后,此会话令牌用于其他控制器逻辑并从服务器获取数据。我在主控制器中调用了这个GetSession()MasterController$routeChangeStart 事件中,但作为异步调用,我的代码在$http 响应之前移动到CustomerController

这是我的代码:

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;
}

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

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

    你可以把你的控制器代码放在一个 Session 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
    【解决方案2】:

    尽管这里的一些解决方案是完全有效的,但我认为路由定义的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'
    }));
    

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

    【讨论】:

      【解决方案3】:

      您尚未提供与 GetSession 相关的任何详细信息。对于这样的场景,您应该在$routeProvider 中定义路由时使用resolve 属性。我看到你已经在使用resolve了。

      您现在可以做的是将 GlobalSessionToken 包装到像 GlobalSessionTokenService 这样的 Angular 服务中,并在解析中调用它以在路由加载之前获取令牌。喜欢

      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。另请参阅基于 Maxim 响应的我的 EDIT,它有效但不等待 $http 响应。
      • 而 GetSession() 只是进行 $http 调用以从服务器获取会话令牌(用户登录时已在服务器上生成)。
      猜你喜欢
      • 2013-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 1970-01-01
      • 2015-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多