0

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.

3
  • 1
    I think this is better done in the run phase as opposed to the configuration phase as all services will have been loaded by the time the run phase starts Commented Jun 2, 2016 at 16:52
  • I choose not to do this in the run phase, because the app continues to load and it sometimes loads faster then my configuration service can return, leaving some features in the app broken because they rely on the configuration to be set up Commented Jun 2, 2016 at 17:08
  • you will have to bootstrap the data into a javascript variable on the page server-side (and put it into an angular constant) or you will have to put the configuration into a service and everything that depends on it will have to access it through a promise (since you can't depend on it being there). It doesn't matter if you put it in the config phase or run phase. Commented Jun 2, 2016 at 17:12

3 Answers 3

4

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); });
Sign up to request clarification or add additional context in comments.

3 Comments

This is a great solution to do it outside of the angular startup cycle. You can combine this with the $q blocking mechanism from my answer to provide everything the OP needs. +1
$http.get returns a promise. So my solution does wait to for the http call to finish before bootstrapping.
Is there a way for the asker to accept a new answer? I like yours a lot more; it achieves what my answer was attempting to do @StevenWexler
1

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.");
  });
 }
]);

7 Comments

I choose not to do this in the run phase, because the app continues to load and it sometimes loads faster then my configuration service can return, leaving some features in the app broken because they rely on the configuration to be set up
@gh9 Please see my update. I have added blocking in the run method until the API call completes.
Wish I could give you more than just an upvote and an accepted answer
$http.get returns a promise. So your deferred variable is redundant. You can remove it.
@StevenWexler It needs to be deferred as we invoke the http call iin the run phase. If we did not defer it, run phase would complete before the http call does and the app would not be configured correctly.
|
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.