14

How can I initialize my angularjs app contant with reponse of a GET request.

For example :-

    angular.module('A',[]);
    angular.module('A').run( function ($rootScope,$http){
      $rootScope.safeApply = function (fn) {

                $http.get('url').success(function(result){

                    // This doesn't work. I am not able to inject 'theConstant' elsewhere in my application
                    angular.module('A').constant('theConstant', result);
                });                   
                var phase = $rootScope.$$phase;
                if (phase === '$apply' || phase === '$digest') {
                    if (fn && (typeof (fn) === 'function')) {
                        fn();
                    }
                } else {
                    this.$apply(fn);
                }
            };
      });

I want to set the constant while my app get initialized and be able to share the constant across my components.

What's is the best approach to accomplish this?

Kumar Sambhav
  • 7,503
  • 15
  • 63
  • 86
  • what is `theValueFromHttpCall`? – akonsu Nov 07 '14 at 14:13
  • @akonsu Updated the question. In my case, the reponse would be a JSON object. – Kumar Sambhav Nov 07 '14 at 14:15
  • interesting. I would guess that injector already has all components defined at boot, so I think that it is necessary to work with it directly to enable your scenario. – akonsu Nov 07 '14 at 14:17
  • @akonsu Can you please elaborate. Some references/blog that I should refer to get this done.... – Kumar Sambhav Nov 07 '14 at 14:53
  • Get the json response from the server side and set in the app_config as follows : [Initialize angularJS app](http://stackoverflow.com/questions/16286605/initialize-angularjs-service-with-asynchronous-data) – Vaibhav Pachauri Nov 09 '14 at 13:00

3 Answers3

10

As explained in this blog post, you can init a constant before bootstrapping your app:

(function() {
    var app = angular.module("A", []);

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

    return $http.get("/path/to/data.json")
        .then(function(response) {
            app.constant("myData", response.data);
        })
        .then(function bootstrapApplication() {
            angular.element(document).ready(function() {
                angular.bootstrap(document, ["A"]);
            });
        });


}());
Benoît Guérout
  • 1,977
  • 3
  • 21
  • 30
5

The result of $http.get isn't available while the app is initialized. It is only available when the server delivers it. For this reason simply keeping that value in a module constant is impossible. You run the risk of

What you can do however, is wrap the call to $http.get in a service and inject that service wherever you want the constant. (Note that services can't be injected in config blocks.)

// grab the "constant"
angular.module('A').factory('almostConstant', function () {
  return $http.get('url').then(function(response) {
    return response.data;
  });
});

// use the "constant"
angular.module('A').controller('controller', function($scope, almostConstant) {
  almostConstant.then(function(data){
    $scope.almostConstant = data;
  });  
});

The slightly awkward mode to access the value of your almostConstant is due to its asynchronous nature. It simply is available at an unspecified time so trying to access it in a synchronous manner can introduce a lot of subtle timing bugs.


A very non angular-ish way of doing this would be to write your constant in the JS file directly. At the moment your server can answer to a request to 'url' with a value. Instead, you could make it answer to a request to 'url.js' with the following string:

angular.module('A').constant('theConstant', result);

where result is obviously your constant. For example if you were using php on the backend it could look something like this:

<?php
   header('Content-Type: application/javascript');
   $constant = retrieveMyConstant();
?>
angular.module('A').constant('theConstant', <?php echo $constant; ?>);

Make sure that the constant actually looks like a JavaScript value. If it's a string, wrap it in ', if it's a JSON object write its serialization, etc.

After this you simply include a script tag pointing to url.js in your index.html file.

Note that this solution is synchronous, so if retrieving the constant on the server takes a while, it will affect your page load time.

Tibos
  • 27,507
  • 4
  • 50
  • 64
  • In my case there are no server side web pages. So I think I will have to go with the first approach you suggested. – Kumar Sambhav Nov 09 '14 at 04:14
  • You are calling the server every time you want to read a constant value, not a good solution. – Abhijeet Ahuja Feb 08 '17 at 03:40
  • @Abhijeet The factory method is called a single time, so a single call to `$http.get` is made on the page, so a single request is sent to the server. Feel free to try it out in a simple example. – Tibos Feb 08 '17 at 08:38
1

I figured out that using 'resolve' properties either in standard angular router or while using UI-Router is a better way to initialize your app.

This is how did while using UI-Router:-

  1. Define a top level abstract state with empty inline template like this:-
$stateProvider.state('root',{
  abstract:true,
  template:'<ui-view/>',
  resolve : {
      securityContext : function($http){
          return $http.get("/security/context");
      }
  }
});
});

The property to be resolved are something that are required through out you application. Like - security token, currently logged in user etc.

  1. Define child state inheriting from above state. Each and every part of you application must be managed by a state.
$stateProvider.state('root.optosoft.home',{
  url:'/home',
  templateUrl : '/assets/home-module/partial/home/home.html',
  controller: 'HomeCtrl',
  resolve : {
       accounts : function(securityContext){
            // Child state wil first wait for securityContext to get resolved first
      }
  }
});
Kumar Sambhav
  • 7,503
  • 15
  • 63
  • 86