Angularjs $http wait for response

23,498

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.

Share:
23,498
Anirudhan J
Author by

Anirudhan J

Graduate student in Machine Learning from Courant Institute of Mathematical Sciences, New York University. Also have hands-on experience as a Full stack developer in various technology stacks such as .Net, J2EE, ROR. SOreadytohelp

Updated on July 09, 2022

Comments

  • Anirudhan J
    Anirudhan J almost 2 years

    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