1

I'm learning angular, and I'm trying to use a service to store data from an HTTP request, and be able to access it later.

Problem: Data object is empty every time I try to retrieve it, which causes it to make a new call. I'm using this in the context of a ui-router resolve(does this cause the service to re-instantiate and lose the data)?

Service:

evaApp.factory('userService', ['$http', '$q', function ($http, $q) {
  var user = {};
  return {

    makeRequest   : function(url, uid) {
      var deferred = $q.defer();

      if (!uid) { uid = ''; };

      $http.get(url, { params : { userId : uid } }).then(function (res) {
        deferred.resolve(res.data);
      });

      return deferred.promise;
    },

    getUser       : function(userId) {
      console.log(user); // user is always empty
      if(!user || !user._id) { 
        user = this.makeRequest('/api/user/get', userId);
      };

      return user;
    }
  }
}]);

Addition:

Data storage is working using PSL's solution. Data retrieval is not: Link to question.

Community
  • 1
  • 1
dmr07
  • 1,388
  • 2
  • 18
  • 37

2 Answers2

1

this.makeRequest returns a promise and it does not have a _.id property which is causing it to make the ajax call again (due the condition if(!user || !user._id) {). just return the promise itself from getUser and use it. Remember you are not assigning the user instead assigning a promise by doing user = this.makeRequest('/api/user/get', userId);

Instead just do:-

var user = {};
getUser : function(userId) {
  return user[userId] || (user[userId] = this.makeRequest('/api/user/get', userId)
                .catch(function(){ user = null })); //nullify in case of error for retry

}

and in make request just do:

makeRequest   : function(url, uid) {

  if (!uid) { uid = ''; };

  return $http.get(url, { params : { userId : uid } }).then(function (res) {
    return res.data;
  });
},

and while making call from controller you would do:-

   mySvc.getUser(userId).then(function(user){
         myCtrlInstance.user = user;
   });

Note: Avoid using deferred anti-pattern when you already have an operation that returns a promise.

Community
  • 1
  • 1
PSL
  • 123,204
  • 21
  • 253
  • 243
  • Thanks PSL, solution is perfect. I ran into a small problem hooking everything up... Please see question addition. – dmr07 May 18 '15 at 05:51
  • @danm07 That is unrelated in the context of the question. Also even if without this answer how you would have handled. You should accept the answer and this should be a different question. However [`this` is not the same `this` inside a callback](http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback).. Just cache `this` to another variable before the call and use it inside the callback. – PSL May 18 '15 at 13:11
  • `var that = this` was used but omitted above. I applied your implementation in both parent and child state's resolve, and it seems something in the `makeRequest` made the parent resolve ineffectual, so child was racing ahead of parent. I'm still looking into this. – dmr07 May 18 '15 at 18:17
  • @danm07 you must be doing something different. it does not matter who makes the call as long as they all go through the same. One mor ething is i just updated my answer to do `user[userId] = user[userId] |...` to cache only for a specific userId. Anyways answer is not to solve your specific issue but to give you an idea of issue in your displayed code and how you can avoid deferred anti pattern. – PSL May 18 '15 at 18:22
  • Added new question. [link](http://stackoverflow.com/questions/30311075/angular-service-injection-not-working) – dmr07 May 18 '15 at 19:13
0

You can make something like this:

evaApp.factory('userService', ['$http', '$q', function ($http, $q) {
  var user = {};
  return {

    makeRequest   : function(url, uid) {
      var deferred = $q.defer();

      if (!uid) { uid = ''; };

      $http.get(url, { params : { userId : uid } }).then(function (res) {
        user = res.data;
        deferred.resolve(user);
      });

      return deferred.promise;
    },

    getUser       : function(userId) {
      console.log(user); // user is always empty
      if(!user || !user._id) { 
        return this.makeRequest('/api/user/get', userId);
      };

      var deferred = $q.defer();
      deferred.resolve(user);
      return deferred.promise;
    }
  }
}]);

And then get the user details like this (the 1 is just for the example):

userService.getUser(1).then(
   function( data ) {
      console.log(data);
   }
);
Alon Eitan
  • 11,997
  • 8
  • 49
  • 58
  • 1
    You dont even need to use [deferred anti-pattern](http://stackoverflow.com/questions/23803743/what-is-the-deferred-antipattern-and-how-do-i-avoid-it) when you already have an async operation that returns a promise... – PSL May 15 '15 at 19:27