1

If I have a service that looks like this:

app.factory('User', function($http, User) {
    var User = function(data) {
        angular.extend(this, data);
    };
    User.prototype.create = function() {
        var user = this;

        return $http.post(api, user.getProperties()).success(function(response) {
            user.uid = response.data.uid;
        }).error(function(response) {

        });
    };

    User.get = function(id) {
        return $http.get(url).success(function(response) {
            return new User(response.data);
        });
    };

    return User;
});

How do I, in a controller, get the User that was created in the get() function?

Currently what I have is:

app.controller('UserCtrl', function($scope, User) {
    $scope.user = null;

    User.get($routeParams.rid).success(function(u) {
        $scope.user = new User(u.data);
    });
});

The issue is that the UserCtrl is getting the api response, not the value returned from the success() in the factory. I'd prefer to be making the new user in the factory, as opposed to passing the api response to the controller.

Lucas Raines
  • 1,315
  • 3
  • 15
  • 24

3 Answers3

5

Edit: May 5th 2015

I learnt about the deferred anti-pattern while trying to answer this question.

So we can avoid using $q.defer() and use the following code to achieve the same result.

Service

User.get = function(id) {
    return $http.get(url).then(function(response) {
        return new User(response.data);
    });
};

Controller

User.get($routeParams.rid).then(function(u) {
    $scope.user = u;
});

Original Answer below:

I think what you are looking for is the $q service which allows you to do some post processing on the response from the asynchronous $http service and then return it to the controller as a promise. Check out the documentation here https://docs.angularjs.org/api/ng/service/$q

So inject the $q service in your factory and change your User.get function like below.

User.get = function(id) {
    var deferred = $q.defer();

    $http.get(url).success(function(response) {
        deferred.resolve(new User(response.data));
    }).error(function(error) {
        deferred.reject(error);
    });

    return deferred.promise;
};

And then in your controller you can do

User.get($routeParams.rid).then(function(u) {
    $scope.user = u;
}, function(error) {
    //log error
});
Community
  • 1
  • 1
0

I think your returns are a bit messed up. Personally I would have written it something like this

app.factory('User', function ($http) {

    var user = {
        new: function (data) {
            return $http.post(api, data);
        },
        get: function (id) {
            $http.get(url).success(function (response) {
                return user.new(response.data);
            });
        }
    };

    return user;

});

Then from inside your controller..

app.controller('UserCtrl', function($scope, User) {
    $scope.user = null;

    User.get($routeParams.rid).success(function(u) {
        $scope.user = u.data;
    });
});
Ed Knowles
  • 1,925
  • 1
  • 16
  • 24
  • This is what I did above. It doesn't work. What happens is that the data returned from the promise is what is passed to the `UserCtrl` – Lucas Raines Apr 25 '15 at 22:42
  • I've just realised what you've done, by using `new User` you're creating an instance of that factory and returning it. Unless you have another factory called User. If you do can you put up some more code? – Ed Knowles Apr 25 '15 at 22:48
  • I realized I made a typo above. `app.factory('User')` returns `User`. I could put more code up, but it's the same as above. Just more functions. Some are `.prototype`, others, like `get`, aren't. – Lucas Raines Apr 25 '15 at 22:53
  • I assumed that was a typo :) my point is do you have a function that creates a user? maybe something like `User.prototype.create`...? – Ed Knowles Apr 25 '15 at 22:58
  • Added example of `User.prototype.create`. – Lucas Raines Apr 25 '15 at 23:01
  • Updated my answer with a cleaner way to write your service, not sure if this solves your particular issue but it might help! Best of luck! – Ed Knowles Apr 25 '15 at 23:18
  • The problem with what you have above is that you can't do `User.get().success()` in `UserCtrl` if you didn't return `$http`, the actual promise, in `User.get()`. I think I managed to figure it out though, and it's based on the difference between `success()` and `then()` with promises. If both the factory and controller used `then()`, it seems to work. – Lucas Raines Apr 25 '15 at 23:21
  • Using the $q service is a much cleaner way of writing it and helps you easily understand what you are returning from the factory. Checkout my answer. – Rathish Cholarajan Apr 25 '15 at 23:37
0

Had similar problem, when i had 'response.data' passed to def.resolve, from controller it throws as undefined. So passed the response directly and it worked.

var def = $q.defer();
$http.get('http://localhost:3000/api/services')
      .success(function (response) {
          ***def.resolve(response);***              
      })
      .error(function (error) {              
          def.reject(error);
      });
      return def.promise;
Siva Dorai
  • 63
  • 5