5

I'm using a service to make user data available to various controllers in my Angular app. I'm stuck trying to figure out how to use the $http service to update a variable local to the service (in my case "this.users"). I've tried with and without promises. The server is responding correctly.

I've read several excellent articles for how to use $http within a service to update the scope of a controller. The best being this one: http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html. That does not help me though because it negates the benefits of using a service. Mainly, modifying the scope in one controller does not modify throughout the rest of the app.

Here is what I have thus far.

app.service('UserService', ['$http', function($http) {
    this.users = [];

    this.load = function() {
        var promise = $http.get('users.json')
            .success(function(data){
                // this.users is undefined here
                console.log(this.users);
            }
    };

    promise.then(function() {
        // this.users is undefined here
        console.log('this.users');
    });
}]);

Any help is greatly appreciated. Thank you.

4 Answers 4

3

Try using

var users = [];

rather than

this.users = [];

and see what

console.log(users);

outputs in each of those cases.

Sign up to request clarification or add additional context in comments.

5 Comments

You are correct. However, how then would I bind a controller $scope variable to users?
you wouldn't, in your controller, you would have to make an explicit call to get that from the user service
What about 2-way binding. Lets say I have $scope.users in my controller that I update with a getter method from the service. I want to be able to update the service users value when $scope.users changes. I realize I could implement a setter method on the service but then I have to handle binding manually everywhere. That seems to reduce the value of Angular's built-in binding. Am I going about this the entirely wrong way?
you create a method in the service that returns the var users this.get = function(){ return users } then in your controller you set $scope.users = usersService.get(); and then your two way data binding will work
This is the correct way of using observer pattern in angular controller-service interaction.
3

Your service is oddly defined, but if you have a return in it you can access it from any controller:

app.service('UserService', ['$http', function($http) {
    var users = [];
    this.load = function() {
        var promise = $http.get('users.json')
            .success(function(data){
                // this.users is undefined here
                console.log(users);
                users = data.data;
            }
    };
    return {
        getUsers: function(){
            return users;
        }
    }  
}]);

so in your controller, you can use:

var myUsers = UserService.getUsers();

UPDATE to use a service correctly here, your service should return a promise and the promise should be accessed in the controller: Here's an example from another answer I gave

// your service should return a promise
app.service('PickerService', [$http', function($http) {
    return {
        getFiles: function(){ 
            return $http.get('files.json'); // this returns a promise, the promise is not executed here
        }
    }
}]);

then in your controller do this:

PickerService.getFiles().then(function(returnValues){ // the promise is executed here as the return values are here
    $scope.myDirectiveData = returnValues.data;
});

6 Comments

I'm new to Angular. Can you let me know what you mean by oddly defined?
It's not wrong, but it doesn't return anything, normally when you have a service, it will do something, but as it behaves like a singleton, it is used to store data and return it. Your service runs when it is instantiated, but then does nothing with that data.
I thought that you only return something if you're using the factory method to generate a service. app.factory('ServiceName', function() { return {...}}.
See my updated code. In your example, you are doing an $http call to get data, and you want to access that data in your controller. So that means you need to call it from a controller. If you want to save the data you get back to the service, define a setData function in your service and call it after the .then() once you get data, then it will be accessible via any controller. The service will not broadcast to all your controllers.
factory and service are VERY similar in how you can use them, but here's a simple article: stackoverflow.com/questions/14324451/… to help out
|
0

this does not have scope anymore where you are trying to use it do this instead:

app.service('UserService', [$http', function($http) {
var users = [];

this.load = function() {
    var promise = $http.get('users.json')
        .success(function(data){
            console.log(users);
        }
};

    promise.then(function() {
    console.log(users);
    });
}]);

all local variables to a service should just be vars if you assign them to this as a property than they will be included every time the service is injected into a controller which is bad practice.

Comments

0

I think what your asking for is a solution along the lines of defining your service like this:

angular.module('app')
  .service('User', function($http, $q) {
    var users = null;
    var deferred = $q.defer()

    return {
      getUsers: function() {
        if(users) {
          deferred.resolve(users);
        } else {

          $http.get('users.json');
            .success(function(result) {
              deferred.resolve(result);
            })
            .error(function(error) {
              deferred.reject(error);
            });
        }
        return deferred.promise;
      }
    };
  });

Then in one Each controller you would have to do this:

angular.module('app')
  .controller('ACtrl', function($scope, User) {
    User.getUsers().then(function(users) {
      // Same object that's in BCtrl
      $scope.users = users;
    });
  });

angular.module('app')
  .controller('BCtrl', function($scope, User) {
    User.getUsers().then(function(users) {
      // Same object that's in ACtrl
      $scope.users = users;
    });
  });

NOTE: Because the deferred.promise the same promise passed to all controllers, executing deferred.resolve(users) in the future will cause all then success callbacks in each of your controllers to be called essentially overwriting the old users list.

All operations on the list will be noticed in all controllers because the users array is a shared object at that point. This will only handle updates to the user list/each individual user on the client side of your application. If you want to persist changes to the server, you're going to have to add other $http methods to your service to handle CRUD operations on a user. This can generally be tricky and I highly advise that you check out ngResource, which takes care of basic RESTful operations

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.