4

I'm sure this will be an easy one for any Angular/Javascript experts. I have a service that makes a call to an API to get some data:

app.service("GetDivision", ["$http", function($http){

  this.division = function(divisionNumber){
    $http.post("/api/division", {division:divisionNumber}).success(function(data){
      return data;
    });
  } 

}]);

Now I call this in one of my controllers like so:

$scope.division = GetDivision.division(1);

However, my service isn't quite right. It doesn't return the value outside of the http request function, so the data doesn't reach the controller calling it. How do I return the data out of both the http request and the function called?

CaribouCode
  • 13,998
  • 28
  • 102
  • 174
  • Because it is asynchronous, the $scope is getting the data before the ajax call is complete. You could use $q in your service to create promise and give it back to controller, and controller obtain the result within then() call against promise. In your service, – AngularLover Dec 18 '14 at 12:42

2 Answers2

8

You need to understand that you can't return from asynchronous operations. Response is simply not yet available when you try to return data. Typically in Javascript you would use callback or promise pattern. In Angular promise is a very natural choice.

app.service("GetDivision", ["$http", function($http) {
  this.division = function(divisionNumber){
    return $http.post("/api/division", {division:divisionNumber}).success(function(data){
      return data;
    });
  }
}]);

and in controller:

GetDivision.division(1).then(function(data) {
    $scope.division = data;
});

Make sure you read this very popular thread on the subject: How do I return the response from an asynchronous call?

Community
  • 1
  • 1
dfsq
  • 191,768
  • 25
  • 236
  • 258
  • @Rasalom Your example was also fine, just a little redundant, since `$http.post()` already returns a Promise object, so no need of one more `$q.defer()`. – dfsq Dec 18 '14 at 12:44
  • [newbie] Will the `$scope` accessible from the anonymous `then` method? I mean the scope has changed right? – Mr_Green Dec 18 '14 at 12:44
  • @Mr_Green Yes. `$scope` object comes from outer scope, so it will be accessible in inner one too. – dfsq Dec 18 '14 at 12:45
  • @dfsq that's why I removed it, no need to create new promise as far as $http already returns one. – Rasalom Dec 18 '14 at 12:45
1

You can return empty object from your service and populate it once you receive the response. That is the way $resource service do it. It also allows to avoid additional logic in controller.

This will work because reference to data object that is put into the scope remains the same all the time and $http service starts digest cycle once response is returned. Therefore the change in the scope.division will be immediately detected and view will be updated accordingly.

JavaScript

var app = angular.module('app',[]);

app.service("GetDivision", ["$http", function($http){

  var data = {};

  this.division = function(divisionNumber){
    $http.get("division", {division:divisionNumber}).success(function(responseData){
      angular.extend(data, responseData);
    });
    return data;
  } 

}]);

app.controller('ctrl', ['$scope', 'GetDivision', function ($scope, GetDivision) {
  $scope.division = GetDivision.division(1);
}]);

HTML

<body ng-controller="ctrl">
  <h1>{{division.text}}</h1>
</body>

Live Demo: http://plnkr.co/edit/H31mSaXiiCiVG9BA9aHK?p=preview

Vadim
  • 8,701
  • 4
  • 43
  • 50
  • This is interesting idea too. However I find it pretty confusing, since it's not obvious why it works (for beginner, who is not familiar with digest loop). At the same time proper promise usage makes it clear and will also work in non-Angular world, because it's just promise pattern. But nice idea! – dfsq Dec 18 '14 at 12:51