Angularjs $http wait for response

All we need is an easy explanation of the problem, so here it is.

I am a newbie to javascript/angularjs. I want to show a bootstrap popover when mouseover is done on some elements.

I created a directive for that

(function(angular, app) {
  app.directive('popOver',["$window","$http",function($window,$http){
    return function(scope,elem,attrs){
      elem.on('mouseover',function(){
        console.log('mouseover');
        var data = scope.$apply(attrs.popOver);
        console.log('in directive again');
        console.log(data);
      });
    };
  }]);
})(angular, app);

the value of the directive is a function

<span class="asdf" pop-over="getVCard(id)">name</span>

the function vcard(id) is defined in my angularjs controller. It checks if the data is already there in localstorage and if present, returns the data. otherwise it does a $http get request and stores the data in localstorage.

$scope.getVCard = function(id){

  var vcardKey = vcardKeyPrefix+id;

  var vCardFromLS = localStorageService.get(vCardKey);
  if(vCardFromLS){
    return localStorageService.get(vCardKey);
  }

  $http.get(app.common.vcardUrl(id)).
    success(function(data){
      localStorageService.set(vCardKey,data);
    }).
    error(function(error){
      F1.common.error(data,function(){});
    });

    return localStorageService.get(vCardKey);
};

Since $http returns promise, i am getting a value of undefined in my directive. How can i make sure that the function getVCard returns synchronously?

I read a number of results after googling. But the suggested idea is to use callbacks. But i am not sure how callbacks will make this synchronous. Any help is appreciated.

UPDATE 1 (in response to Foo L’s comment)
i was planning to use the same directive in multiple places. popovers for vcards and product information and so on. The only thing that will differ is the argument to the pop-over directive. That way the directive will bother about just displaying the popup. Where to get the data will be in a separate function that is passed to the directive

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

You can’t force $http.get() into synchronism. Since $http.get() is unavoidably asynchronous, you can only return a promise of a value not the value itself.

And because you need to do this when $http.get() is called, you must also return a promise under the other condition – when vCardFromLS is successfully retreived from localstorage. This ensures that the object returned to any call of $scope.getVCard() has a .then() method, ie it is a promise, regardless of whether $http.get() was called or not.

So the code should be something like this :

$scope.getVCard = function(id) {
    var vcardKey = vcardKeyPrefix + id;
    var vCardFromLS = localStorageService.get(vCardKey);
    var dfrd = $q.defer();
    if(vCardFromLS) {
        dfrd.resolve(localStorageService.get(vCardKey));
    }
    else {
        $http.get(app.common.vcardUrl(id)).success(function(data) {
            localStorageService.add(vCardKey, data);//.add() rather than .set() ?
            dfrd.resolve(data);
        }).error(function(error) {
            F1.common.error(data, function() {
                dfrd.reject('$http.get(app.common.vcardUrl(id)) failed');
            });
        });
    }
    return dfrd.promise;
};

Now you need to ensure that the response to $scope.getVCard() is handled appropriately, eg :

$scope.getVCard(id).then(function(data) {
    //success - do whatever is required with data
}, function(reason) {
    //failure - log and/or display `reason` as an error message
})

EDIT:

My “… you must also return a promise under the other condition …” is an overstatement.

I should have said, “… you can, to make things simple, return a promise under the other condition …”.

Another possibility is to branch depending on whether a Promise or another type of object/value was returned. However, this is messy and will typically lead to some duplication of code.

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply