0

Assume the following AngularJS service factory:

function Constants($http) {
    var constants = {
        someFields: someValues
    };
    $http({method: "GET", url: baseUrl + "/constants.json"}).success(
        function (data, status) {
            angular.forEach(data, function (value, key) {
                constants[key] = value;
            });
        }
    );
    return constants;
}

It creates a service Constants with a set of constants in there. The constants are partly hardcoded and partly loaded from the constants.json, located on server (this logic is not discussable). This service is then used in a controller:

function SettingsController($scope, $rootScope, Constants) {
    $scope.someProperty = Constants.somePropertyFromConstantsJson;
    ...

I understand that $http calls are asynchronous. It means that there is no guarantee that the properties from constants.json in the example will be loaded and put to Constants before they are read in SettingsController.

How could I provide such a guarantee? How could I make SettingsController initialized only after the $http call in Constants is over?

UPD: The case I am talking about is a part of a bigger project. I want to avoid bigger changes all over the code base, so I need Constants to remain an object, properties of which are accessible through . or []. That's why I'll consider options which doesn't fit this desire as a last resort.

UPD2: I checked the question marked as duplicate. Actually, I understand that $http is asynchronous operation, and I don't have intention to extract the local state of callbacks to the Constants factory constructor. The code of Constants constructor does that, but I am ok with refactoring it (until the type of Constants service and its properties is kept). I just wanted a way to make Angular create Constants service after the Http call is done. Some sort of asynchronous service initialization. I think my desire doesn't contradict with async nature of stuff, correct me please if I am wrong.

skapral
  • 1,128
  • 9
  • 26
  • 1
    You return the promise from the service instead of the data. Then the controller can use the promise and wait for it to resolve. This is a very common pattern and there should be ample examples here on SO. – Lex May 25 '18 at 15:33
  • It's not a desired option for me. I'd need to replace an object with promise in 100500 dependent modules over the application, which looks not good for a hotfix + I don't like an idea to have a promise for just a stupid set of constants, loaded once. I'd better avoid that. – skapral May 25 '18 at 15:35
  • Possible duplicate of [How to return value from an asynchronous callback function?](https://stackoverflow.com/questions/6847697/how-to-return-value-from-an-asynchronous-callback-function) – mhodges May 25 '18 at 15:43
  • @skapral set a property on `constants` equal to your AJAX call, then you can say `constants.myFunction().then(...)`. It won't change how the rest of your `constants` work across your modules – mhodges May 25 '18 at 15:45

1 Answers1

0

Make the access to the constants async, instead of exposing the constants directly hide it behind a promise.

Something like that:

function Constants($http, $q) {
  var constants = {
    someFields: someValues
  };

  const defer = $q.defer();

  $http({
    method: "GET",
    url: baseUrl + "/constants.json"
  }).success(
    function(data, status) {
      angular.forEach(data, function(value, key) {
        constants[key] = value;
      });
    }
  ).then(() => defer.resolve(constants));


  return {
    whenLoaded: () => defer.promise;
  };
}


function SettingsController($scope, $rootScope, Constants) {
  Constants.whenLoaded().then((constants) => $scope.someProperty = constatns);
}
felixmosh
  • 32,615
  • 9
  • 69
  • 88
  • Don't even need to use a deferred, you can just treat `$q` as a regular `Promise` with `resolve` and `.then()` – mhodges May 25 '18 at 15:43
  • As I said in the comments above, I'd better avoid making Constants a promise. Thanks for the answer though. If nobody proposes more alternatives, I will try it. – skapral May 25 '18 at 15:44
  • You can't get guarantee of async operation without making the access to it async. – felixmosh May 25 '18 at 15:46
  • @skapral This isn't making constants a promise.. it's making a *property* on constants a function that returns a promise – mhodges May 25 '18 at 15:47
  • @felixmosh sure, I was just thinking of some way to make a service from promise value once it is resolved. See the question's update. – skapral May 25 '18 at 16:08