Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS promise is resolved before data is loaded

In my app, I have to fetch some JSON data and assign it to an array before the page is loaded. This is my code for fetching the JSON using the CardService service:

cards = [];

var cs = {
...
fetchCards: function() {
      var d = $q.defer();
      $http.get("data/cards.php").success(function(data) {
                      cards = data;
                      d.resolve();
                }).error(function(data, status) {
                      d.reject(status);        
                 });
               return d.promise;
      },
getCards: function() { return cards; };
...
}

In the controller's resolve block, I have the following:

WalletController.resolve = {
        getCards: function(CardService) {
                CardService.fetchCards().then(loadView, showError);
        }
}

And in the actual controller, I have the following:

function WalletController($scope, CardService) {
    $scope.cards = CardService.getCards();
}

The problem is, the fetchCards function in the service seems to resolve the promise before the JSON data is assigned to the cards variable. This leads to my view loading with blank data until I refresh a couple times and get lucky.

I can confirm the late loading as when I log the cards variable in the console, I get an empty array at line 122 (when my view is loaded) and a full array at line 57 (when the JSON call is successful). Line 57's code somehow executes after the view is loaded.

How do I fix this?

like image 780
kshep92 Avatar asked Sep 04 '12 14:09

kshep92


1 Answers

I haven't used resolve but I'm throwing this out there just in case the issue you are having is related to binding to an array returned from a service.

If you are returning your cards array from a service and binding to it in the UI you may want to try to populate that same array instead of setting cards = data; (which will overwrite the local cards with a new array which is not bound to the UI).

Something like:

fetchCards: function() {
      var d = $q.defer();
      $http.get("data/cards.php").success(function(data) {
                      cards.length = 0;
                      for(var i = 0; i < data.length; i++){
                          cards.push(data[i]);
                      }
                      d.resolve();
                }).error(function(data, status) {
                      d.reject(status);        
                 });
               return d.promise;
      },

See this fiddle for a working example of what I'm trying to describe. Clicking the first button multiple times will update the view but once you click on the second button the binding will be broken.

The main difference between the two is:

  1. First button uses data.length = 0 and data.push() to retain the original array's reference
  2. Second button overwrites the original data array reference with a new one using data = newArray

Update: Also, as Mark Rajcok, mentioned below you can use angular.copy to retain the original array's reference by emptying it out and adding new ones from the source like this:

fetchCards: function() {
      var d = $q.defer();
      $http.get("data/cards.php").success(function(data) {
                      angular.copy(data, cards);
                      d.resolve();
                }).error(function(data, status) {
                      d.reject(status);        
                 });
               return d.promise;
      },
like image 102
Gloopy Avatar answered Oct 17 '22 21:10

Gloopy