简体   繁体   English

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

[英]AngularJS: Call a particular function before any partial page controllers

I want to call a particular function: GetSession() at the beginning of my application load. 我想在我的应用程序加载开始时调用一个特定的函数: GetSession() This function makes a $http call and get a session token: GlobalSessionToken from the server. 此函数进行$http调用并从服务器获取会话令牌: GlobalSessionToken This session token is then used in other controllers logic and fetch data from the server. 然后,此会话令牌将用于其他控制器逻辑,并从服务器获取数据。 I have call this GetSession() in main controller: MasterController in $routeChangeStart event but as its an asynchronous call, my code moves ahead to CustomerController before the $http response. 我在主控制器中调用了这个GetSession()$routeChangeStart事件中的MasterController但是作为一个异步调用,我的代码在$http响应之前向前移动到CustomerController

Here is my code: 这是我的代码:

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

And my HTML has following sections: 我的HTML有以下部分:

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

How can I make sure this GetSession() is always called at the very beginning of my application start and before any other controller calls and also called only once. 我怎样才能确保在应用程序启动的最开始和任何其他控制器调用之前始终调用此GetSession() ,并且只调用一次。

EDIT: This is how I added run method as per Maxim's answer. 编辑:这是我根据Maxim的答案添加run方法的方法。 Still need to figure out a way to wait till $http call returns before going ahead with controllers. 在继续使用控制器之前,还需要找出等待$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;
}

Even though some of the solutions here are perfectly valid, resolve property of the routes definition is the way to go, in my opinion. 虽然这里的一些解决方案完全有效,但在我看来,路由定义的resolve属性是可行的方法。 Writing your app logic inside session.then in every controller is a bit too much , we're used such approach too in one of the projects and I didn't work so well. session.then中编写你的应用程序逻辑在每个控制器中有点太多了,我们在其中一个项目中也使用了这样的方法,但是我没有那么好用。

The most effective way is to delay controller's instantiation with resolve , as it's a built-in solution. 最有效的方法是使用resolve来延迟控制器的实例化,因为它是一个内置的解决方案。 The only problem is that you have to add resolve property with similar code for every route definition, which leads to code duplication. 唯一的问题是你必须为每个路由定义添加类似代码的resolve属性,这会导致代码重复。

To solve this problem, you can modify your route definition objects in a helper function like this: 要解决此问题,您可以在辅助函数中修改路由定义对象,如下所示:

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

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

  return routeConfig;
} 

And then, where define your routes like this: 然后,在这里定义你的路线:

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

This is one of the many solutions I've tried and liked the most since it's clean and DRY. 这是我尝试过并且最喜欢的众多解决方案之一,因为它干净且干燥。

You can't postpone the initialisation of controllers. 你不能推迟控制器的初始化。

You may put your controller code inside a Session promise callback: 您可以将控制器代码放在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 ...
   } ); 
} );

Alternative : If you don't want to use such callbacks, you could have your session request synchronous, but that would be a terrible thing to do. 替代方案 :如果您不想使用此类回调,则可以使您的会话请求同步,但这将是一件非常糟糕的事情。

You have not provided any details related to GetSession. 您尚未提供与GetSession相关的任何详细信息。 For scenarios like this you should use the resolve property while defining your routes in $routeProvider . 对于这样的场景,您应该在$routeProvider定义路由时使用resolve属性。 I see you are using resolve already. 我看到你已经使用了resolve

What you can do now is to wrap the GlobalSessionToken into a Angular service like GlobalSessionTokenService and call it in the resolve to get the token before the route loads. 您现在可以做的是将GlobalSessionToken包装到像GlobalSessionTokenService这样的Angular服务中,并在解析中调用它以在路由加载之前获取令牌。 Like 喜欢

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

This can then be injected in your controller with 然后可以在控制器中注入

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM