0

I am trying to run an $http function when my AngularJS application first loads.

This $http function needs to finish before any of the controllers in my application could properly function. How would I go about doing this? This sounds like a promise, but it sounds like I would be creating a promise in each controller...

I currently have the function that I want to run first like this:

app.run(function() {

    $http.get('link').success(function(data) {

      // success function. The data that I get from this HTTP call will be saved to a service.

    }).error(function(error) {

    });

});

However, sometimes the controller will load before the http call finishes.

Kevin Brown-Silva
  • 40,873
  • 40
  • 203
  • 237
iO2
  • 3
  • 1
  • 5

2 Answers2

2

The problem

Angular is not dynamic, you cannot add controller dynamically neither factory, etc. Also you cannot defer controller bootstrap, angular loads everything together, and it's quite disadvantage (will be fixed in Angular 2)

The cure

But javascript itself has very important feature - closure, which works anywhere, anytime.
And angular has some internal services that can be injected outside of angular ecosystem, even into browser console. Those services injected as shown below. We technically could use anything else (jQuery.ajax, window.fetch, or even with XMLHttpRequest), but let's stick with total angular solution

var $http_injected = angular.injector(["ng"]).get("$http");

The act

First of all, we defer whole angular app bootstrap, inject http service. Then you make your needed request, receive data and then closure get's to work, we pass received data into some service, or we could also assign in to some angular.constant or angular.value but let's just make demo with angular.service, so when your service has data, bootstrap whole app, so that all controllers get initialized with your needed data
Basically that kind of tasks solved like this

<body>
  <div ng-controller="Controller1">
    <b>Controller1</b>
    {{text}}
    {{setting.data.name}}
  </div>
  <hr>
  <div ng-controller="Controller2">
   <b>Controller2</b>
    {{text}}
    {{setting.data.name}}
  </div>
  <script>
    //define preloader
    var $http_injected = angular.injector(["ng"]).get("$http");
    $http_injected.get('http://jsonplaceholder.typicode.com/users/1').then(function(successResponse) {

      //define app
      angular.module('app', []); 

      //define test controllers
      //note, usually we see 'controller1 loaded' text before 'settings applied', because controller initialized with this data, but in this demo, we will not see 'controller1 loaded' text, as we use closure to assign data, so it's instantly changed
      angular.module('app').controller('Controller1', function($scope, AppSetting) {
        $scope.text = 'controller1 loaded';
        $scope.setting = AppSetting.setting;
        $scope.$watch('setting', function(e1 ,e2){
          $scope.text = 'settings applied'
        });
      });
      angular.module('app').controller('Controller2', function($scope, AppSetting) {
        $scope.text = 'controller2 loaded';
        $scope.setting = AppSetting.setting;
        $scope.$watch('setting', function(e1 ,e2){
          $scope.text = 'settings applied'
        });
      });

      //define test services, note we assign it here, it's possible
      //because of javascript awesomeness (closure)
      angular.module('app').service('AppSetting', function() {
        this.setting = successResponse;
      });

      //bootstrap app, we cannot use ng-app, as it loads app instantly
      //but we bootstrap it manually when you settings come
      angular.bootstrap(document.body, ['app']);
    });
  </script>
</body>

Plunker demo

Community
  • 1
  • 1
Medet Tleukabiluly
  • 11,662
  • 3
  • 34
  • 69
0

You can do this when you configure your routes

app.config(['$routeProvider', function ($routeProvider) {
  $routeProvider
    .when('/', {
      controller: 'MainCtrl',
      templateUrl: 'main.html',
      resolve: {
        data: ['$http',
          function($http)
          {
            return $http.get('/api/data').then(
                function success(response) { return response.data.rows[0]; },
                function error(reason)     { return false; }
              );
          }
        ]
      }
    });
}]);

Similar question: AngularJS - routeProvider resolve calling a service method

AngularJS: $routeProvider when resolve $http returns response obj instead of my obj

Heres a plunkr I found using a service, which is what I would recommend. http://plnkr.co/edit/XKGC1h?p=info

Community
  • 1
  • 1
Dustin Clark
  • 92
  • 1
  • 9
  • This will have to be the topmost state – Dustin Clark Aug 22 '15 at 17:08
  • I don't think so he wants data to be loaded..he wants to load configuration setting at run phase – Pankaj Parkar Aug 22 '15 at 17:09
  • I am trying to figure out if this is the best way to solve my problem. While this solution would work, I want to avoid writing this for every controller. I only need the data loaded once, which I will save to a service (I need to put that in my original question...) – iO2 Aug 22 '15 at 17:31
  • Now I see. You have one controller above the others. This makes sense. – iO2 Aug 22 '15 at 17:33