gh9
gh9

Reputation: 10703

manual bootstrap angular with http calls

I have a WebAPI service that returns dynamic configuration data. Before my angular app loads I would like to call that service and load the config data into angular. JSFiddle of my attempt at doing that. My question is, after seeing the string test in the console I am seeing nothing else written into the console. How do I get test 2 and wierd wierd to appear into the console

var app = angular.module('app', [])

app.provider("ConfigService", function () {
    var self = this;
    self.Settings = {};

    self.config = function (data) {
            console.log(data);
    };
    this.$get =
        function($http) {
            return self;
        };
});


angular.element(document).ready(function($http) {
        console.log('test')
        angular.module('app').config([
            'ConfigServiceProvider', 
            function(configService) {
                console.log('test 2')
                $http.get('http://www.google.com').then(function(result) {
                    console.log('wierd wierd')

                    configService.config(result);

                    angular.bootstrap(document, ['app']);
                })
            }
        ]);
    });

EDIT

In response to the question, why I do not run this in app.run phase instead. In the app.run phase the app is still initializing and sometimes it loads up prior to my configuration section being completed. I wanted 100% guarantee that my config section is loaded first before any of the app is.

Upvotes: 0

Views: 434

Answers (3)

Andonaeus
Andonaeus

Reputation: 872

EDIT: Please use @StevenWexler 's answer: https://stackoverflow.com/a/37599857/5670592. It is much more correct, uses a nifty angular feature ($inject), and will provide configuration before the beginning of the bootstrap cycle.

I have updated the application with your constraints regarding blocking execution until API call is complete.

Try this: https://jsfiddle.net/6svnemu8/3/

I moved the code to the module.run(...) block. This is where all providers are available and you can use $http and your ConfigService. I kept the bootstrap call in the document ready function, and I also added the $q service so you can block execution of the application until the API call is complete. You can verify this by looking at the order of the test outputs in the console:

angular.module('app').run([
'ConfigService', '$http', '$q', 
function(configService, $http, $q) {
  console.log('test 2');
  var deferred = $q.defer();
  $http.get('/6svnemu8/2/').then(function(result) {
    deferred.resolve(result);
  }, function(result){
    deferred.reject(result);
  });
  console.log("test 3");
  deferred.promise.then(function(result){
     console.log('wierd wierd');

    configService.config(result);

  }, function(result){
    console.log("call failed.");
  });
 }
]);

Upvotes: 1

Steven Wexler
Steven Wexler

Reputation: 17289

You can use $http outside of your angular module with angular.injector. With $http you can request the config from your server and bootstrap your app when $http's promise resolves.

JS Fiddle

Create module

var app = angular.module("app", []);
app.provider("configService", function () {
  var configService = {
    config: {}
  };
  this.setConfig = function (config) { configService.config = config; };
  this.$get = function() { return configService; };
});

Function that fetches config from server

function fetchConfig() {
  var $http = angular.injector(["ng"]).get("$http");
  return $http.get("http://www.google.com");
}

Function that bootstraps app

function bootstrap(config) {
  app.config(["configServiceProvider", function (configServiceProvider) {
    configServiceProvider.setConfig(config);
  }]).run(["configService", function (configService) {
    //Not necessary, just to confirm everything worked
    console.log("YAY! You have a config:", configService.config);
  }]);

  angular.bootstrap(document, ["app"])
}

Put it all together!

fetchConfig().then(
  /*sucess*/function (config) { angular.element(document).ready(function () { bootstrap(config); }); },
  /*fail*/ function (err) { console.log("UH OH could not retrieve config!", err); });

Upvotes: 4

DerekMT12
DerekMT12

Reputation: 1349

Option 1 -- if you have an MVC app

In your main razor view, use JSON.Net to serialize your Model (or a property on it) to JavaScript.

<script>
   window.configuration = @(Html.Raw(JsonConvert.SerializeObject(Model)))
</script>

Then put it into an angular constant so you can inject it anywhere you need it, and it's guaranteed to be there. This is the most convenient way to do it.

angular.module('YourModule').constant('configuration', window.configuration);

Option 2 -- loading it asynchronously

This service will load the configuration and cache the promise.

angular.module('YourModule').factory('configuration', ['$http', function($http) {
   var configurationLoaded;
   var service = {
      get: get
   };

   function get() {
      if(configurationLoaded) return configurationLoaded;

      configurationLoaded = $http.get( ... );

      return configurationLoaded;
   }

   return service;
}]);

Then anywhere you need it, you'll have to pull out properties from it like this:

angular.module('YourModule').controller('SomeController', ['configuration', function(configuration) {
   var vm = this;

   configuration.get().then(function(config) {
      vm.someSetting = config.someSetting;
   });
}]);

Upvotes: 1

Related Questions