2

I am trying to use $q and $http to get data from server and wait until data is retrieved. After data is retrieved i would want to manipulate it.

This is my service with method which fetches data from server:

application
.factory('service', function ($http,$q) {
  return { 
     fetch: function(url) {
         var deferred = $q.defer();
         $http.get(url).success(function(data){             
             deferred.resolve(data);
         }).error(function(){               
           deferred.reject("An error occured while fetching items");
         });    
         return deferred.promise;
      }        
  };
}); 

Controller that consumes data looks like this:

service.fetch(url).then(function(data){
  $scope.data = data;
  },
function(errorMessage){
  $scope.error=errorMessage;
});
$scope.data = ...manipulate data

Problem is that angular manipulates data before it is fetched. Any suggestions how to solve this would be much appreciated.

3
  • Step through the code with Chromes Debugger and see where it's not handling it appropriately. Commented Jan 7, 2014 at 21:42
  • I would use transformResponse for this. egghead.io/lessons/angularjs-transformresponse Commented Jan 7, 2014 at 22:14
  • The returned value of calling the $http function is a promise. So you can directly return the $http.<METHOD>. Commented Jan 7, 2014 at 22:37

3 Answers 3

4

If you ever need to manipulate data from a service call, a good option is to use the transformResponse option in the $http service. It basically allows you to completely mess with the response before resolving the promise.

See this video for a walkthrough. https://egghead.io/lessons/angularjs-transformresponse

Here's an example transform function.

app.factory('searchResponseTransformer', function () {
    return function (data) {
        data = JSON.parse(data);
        if (data.length) {
            // Do data manipulation here
        }
        return data;
    };
});

Then you can use this transform function from within your service.

app.factory('searchService', function ($http, searchResponseTransformer) {
  return { 
    search: function(searchTerm) {
      return $http({
        method: "GET",
        url: "/search",
        params: { q: searchTerm },
        transformResponse: searchResponseTransformer
      });
    }
  }        
}); 

Then you can use this service from your controller.

app.controller('searchCtrl', function ($scope, searchService) {
  $scope.searchTerm = "";
  $scope.searchResults = [];

  $scope.search = function() {
    searchService
      .search($scope.searchTerm)
      .success(function(data) {
        $scope.searchResults = data;
      });
  }
}); 

And your view would look something like this..

<div ng-controller="searchCtrl">
  <p><input type="text" ng-model="searchTerm" ></p>
  <ul>
    <li ng-repeat="item in searchResults">{{item.description}}</li>
  </ul>
</div>
Sign up to request clarification or add additional context in comments.

Comments

2

The problem is that $scope.data = ... runs just after service.fetch(url) is called not after it returns. The AJAX call service.fetch(url) returns with data when the success callback is called, so the correct code is:

service.fetch(url).then(
    function(data) {
        $scope.data = data; // keeping it for anyone else that needs it, e.g. the view
        $scope.data = ... // manipulate data HERE
    },
    function(errorMessage) {
        ...
    }
);

Comments

1

You shouldn't manipulate the data before it is fetched. Promises return immediately, so your manipulation would be overriden by the then callback. What you should do is manipulate the data in the then callback:

service.fetch(url).then(function(data){
    $scope.data = ...manipulate data
  },
  function(errorMessage){
    $scope.error=errorMessage;
});

However, to improve on this code as it is not very readable, you can create a service method to do that for you. As a replacement of $http I suggest you use Restangular. It is slighty more object oriented. Check this blog post I wrote:

http://ath3nd.wordpress.com/2013/08/05/15/

Restangular, just like $http, has the notion of promises, so you can do:

function fetch(url){
   var deferred = $q.defer();
   Restagular.one(url).get({})
   .then(function(result){
       //do your manipulation, e.g.
       result.newlyCreatedProp = 'newlyCreatedProp';
       deferred.resolve(result);
   });
   return deferred.promise;
}

And then you can do that in your controller:

service.fetch().then(function(data){
   $scope.data = data;
});

Which would work immediately since the promise is returned immediately.

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.