2

I'm a junior dev, so I might be missing something obvious, but I'm feeling a bit loony. I have a simple Angular webapp. I'm attempting to load a hash-dictionary of environment names that correspond to arrays of hosts. {development: ["dev.8090", "host.dev.9009"]} and then use that dictionary to find which host I'm currently on. I should be able to pass the location.host variable to the getEnv method and find the correlating key that will tell me which environment I'm in.

The dictionary loads, but when I try to access it inside of the getEnv method, it reverts to an empty object. Not undefined, mind you, but empty. Here's my code:

var app = angular.module('app', ['ngResource', 'ui.bootstrap', 'ui.router']);

app.config(['$httpProvider', function ($httpProvider) {

    $httpProvider.defaults.useXDomain = true;
    delete $httpProvider.defaults.headers.common['X-Requested-With'];

}]);

function AppController($scope, $http) {
    window.MY_SCOPE = $scope;

    $scope.env = "Local";
    $scope.dict = {};

    $scope.loadDict = function() {
      $http.get('api/call/').
        success(function(data){
          for (env in data.environment) {
          // data.environment = array of objects
          // [
          // {hosts: ["host1", "host2"], name: "string1"},
          // {hosts: ["host1", "host2"], name: "string2"}
          // ]
            var key = data.environment[env].name;
            $scope.dict[key] = data.environment[env].hosts;
          }
          console.log($scope.envDict)
          // in the console:
          // Object {string1: Array[2], string2: Array[2]}
        }).error(function(data){
            console.error(data);
        })
    };

    $scope.getEnv = function(host) {
      for (key in $scope.dict) {
        // never gets this far because $scope.dict is now = {}
        for (value in $scope.dict[key]) {
          if ($scope.dict[key][value] === host) {
            $scope.env = key;
          }
        }
      }
    };

    $scope.loadDict();
    $scope.getEnv("host1");
}

I can manually call each of these methods and get the results I want from the console, using the MY_SCOPE variable. If I hard-code the dictionary, it works. If I console.log $scope.dict from anywhere in the code except from inside of the $scope.getEnv function, I get the result I expect. As soon as $scope.getEnv is involved, $scope.dict = {}.

I've tried hard-coding the keys into the dictionary. I've tried moving the definition around in the code. I've tried exporting the loadDict method into a factory. All to no avail. Ideas?

4 Answers 4

1

The $http.get call in $scope.loadDict is asynchronous. getEnv is getting called before your dictionary has been loaded. You need to call getEnv once that data has come back.

Have loadDict return the $http.getcall which will give you a promise. You can then chain on to that promise a success callback.

You should also put your $http calls in some sort of service to do it the 'angular' way :)

Try this instead:

$scope.loadDict = function() {
      return $http.get('api/call/').
        success(function(data){
          for (env in data.environment) {
         var key = data.environment[env].name;
            $scope.dict[key] = data.environment[env].hosts;
          }
          console.log($scope.envDict)
          // in the console:
          // Object {string1: Array[2], string2: Array[2]}
        }).error(function(data){
            console.error(data);
        })
    };

$scope.loadDict().then(function(result){
     $scope.getEnv("host1");
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you so much. I obviously have a ton to learn - could you recommend a solid resource for learning about promises? I've scoured the docs and am still pretty fuzzy.
The $q page I linked below may help -- that is angular's promise implementation. A more general introduction that I like is the original Q library on which $q is based: github.com/kriskowal/q (but note that not all of the methods of Q are in $q).
0

Your problem is that you didn't deal with the fact that loadDict is async internally.

One way to solve this is to wait for it to complete by returning a promise from it and waiting for that promise to be resolved.

There are other ways to go about this, but this is probably one of the ways that is closest to what you already have:

// inject $q so you can make a promise
function AppController($scope, $http, $q) {
    window.MY_SCOPE = $scope;

    $scope.env = "Local";
    $scope.dict = {};

    $scope.loadDict = function() {
      // set up the deferred response
      var deferred = $q.defer();

      $http.get('api/call/').
        success(function(data){
          for (env in data.environment) {
          // data.environment = array of objects
          // [
          // {hosts: ["host1", "host2"], name: "string1"},
          // {hosts: ["host1", "host2"], name: "string2"}
          // ]
            var key = data.environment[env].name;
            $scope.dict[key] = data.environment[env].hosts;
          }
          console.log($scope.envDict)
          // in the console:
          // Object {string1: Array[2], string2: Array[2]}
          // all is well so resolve the promise
          deferred.resolve();
        }).error(function(data){
            console.error(data);
            // reject the promise
            deferred.reject(data);
        })

      return deferred.promise;
    };

    $scope.getEnv = function(host) {
      for (key in $scope.dict) {
        // never gets this far because $scope.dict is now = {}
        for (value in $scope.dict[key]) {
          if ($scope.dict[key][value] === host) {
            $scope.env = key;
          }
        }
      }
    };

    $scope.loadDict().then(
      function () {
        $scope.getEnv("host1");
      },
      function (err) {
        // whatever you want to do if the loadDict function failed to do its job
      }
    );
}

3 Comments

I see another answer already came in around the same time. Either will work. @link64 's obviously skips creation of the wrapper promise, so you might prefer that.
You should still learn $q, since you're wondering about promises.
@stu.salsbury You don't know for sure that he down voted you and he's a new user, so lets give him the benefit of the doubt =)
0

$scope.getEnv() is being called before $http.get() has returned data. You need to call $scope.getEnv() within the $http.get().success() block, like so:

$scope.loadDict = function() {
    $http.get('api/call/').success(function (data) {
        for (env in data.environment) {
            var key = data.environment[env].name;
            $scope.dict[key] = data.environment[env].hosts;
        }
        $scope.getEnv("host1");
    }).error(function(data){
        console.error(data);
    });
};

Comments

0

You need to treat things asynchronously . success is an asynchronous callback while getEnv is synchronous. The solution in this case is to define a promise in loadDict and resolve it on success call. Then , in the controller getEnv method you would write code after promise is resolved: Roughly the code will be like this, I have not tested it, just wrote to give you idea:

$scope.loadDict = function() {
        var deferred = $q.defer(); // to define a promise
      $http.get('api/call/').
        success(function(data){
                   deferred.resolve(data);//resolve the promise on success
          }
         }).error(function(data){
            console.error(data);
        })
     return deferred.promise;//return promise
    };

    $scope.getEnv = function(host) {
       $scope.loadDict().then(
        function(data) {

          for (env in data.environment) {
          // data.environment = array of objects
          // [
          // {hosts: ["host1", "host2"], name: "string1"},
          // {hosts: ["host1", "host2"], name: "string2"}
          // ]
            var key = data.environment[env].name;
            $scope.dict[key] = data.environment[env].hosts;

            for (key in $scope.dict) {
        // never gets this far because $scope.dict is now = {}
        for (value in $scope.dict[key]) {
          if ($scope.dict[key][value] === host) {
            $scope.env = key;
          }
        }
      }
     });

    };

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.