2

I'm having trouble with getting my custom angular service to work.

My service is as follows:

app.service('userService', function(){
  var user = [];

  var setUser = function(newObj){
    user.push(newObj);
  };

  var getUser = function(){
    return user;
  };

  return {
    setUser: setUser,
    getUser: getUser
  };
});

app.config(function($routeProvider,$locationProvider) {

  $routeProvider
    .when('/', {
      templateUrl : 'views/home.html',
      controller  : 'indexController'
    })
    .when('/me.html', {
      templateUrl : 'views/me.html',
      controller  : 'meController'
    });

    // Get rid of the additional default pound sign during routing
    $locationProvider.html5Mode(true);
});

I call it as follows

app.controller('indexController', function($http,userService) {  
                                ...
    $http.post("somewhere")
        .then(function(response){
           userService.setUser({test: "test});
           location="/me.html";
        }
        .then(function(response){
           userService.setUser({test: "test});
           location="/me.html";
        })
});

When I do a console.log in another controller, it returns an empty array. What am i messing up?

app.controller('meController',function($http,userService){
   console.log(userService.getUser()); => returns an empty array []

});

EDIT Some of my code was vague, added my routes section, should be a little more clear. EDIT 2 Forgot to mention, if I stick in console.log(userService.getUser()) in the line after I setUser(), it correctly displays the data.

i.e.

$http.post("somewhere")
    .then(function(response){
       userService.setUser({test: "test});
       location="/me.html";
    }
    .then(function(response){
       userService.setUser({test: "test});
       location="/me.html";
       console.log(userService.getUser()); => correct output
    })
miquelarranz
  • 876
  • 11
  • 26
ecy
  • 39
  • 1
  • 9
  • You have a syntax error in your code. and are you sure the second controller is requesting data after the AJAX request is complete? Please share [mcve] showing when the controllers are initialized. – T J Jun 28 '16 at 07:03
  • 1
    That `console.log` is going to execute when the controller is created, and only that one time, when your app runs. At that point you don't have anything in the array. Your app may be fine but you just aren't accessing the array properly. – Tim Hobbs Jun 28 '16 at 07:06
  • Hmm I'm using angular-routing though, doesn't that mean the second controller is instantiated later? – ecy Jun 28 '16 at 07:12
  • 1
    @TimHobbs *"when the controller is created, and only that one time, **when your app runs**"* - no. controllers are initialized in an *on demand* manner. If the second controller is required upon switching to a different route (maybe after the AJAX request), it'll be initialized at that point. – T J Jun 28 '16 at 07:13
  • 1
    @intrinsiciwnl read my above comment. And as I mentioned in my earlier comment, show how the controllers are initialized in a [mcve]. – T J Jun 28 '16 at 07:15
  • @TJ - thanks for correcting me – Tim Hobbs Jun 29 '16 at 02:31

2 Answers2

0

Maybe because console.log instruction is run before de assignation from the other controller.

You can fix it by:

  1. implementing a callback system
  2. $scope.watching service.getUser()
  3. implementing promises in the service
  4. implement a signaling system with $broadcast/$emit

For case 1. you can do like this:

app.service('userService', function(){
  var user = [],callbacks = [];

  var setUser = function(newObj){
    user.push(newObj);
    for(var i = 0;i< callbacks.length;++i)
      callbacks[i](user);
  };

  var getUser = function(){
    return user;
  };

  var onUserChange = function(callback) {
    callbacks.push(callback);
  };

  return {      
    onUserChange:onUserChange,
    setUser: setUser,
    getUser: getUser
  };
});

and try call it in your second controller.

For case 2.

app.controller('meController',function($http,$scope,userService){
  $scope.$watch(function(){ return userService.getUser(); },
                function(newvalue){ console.log(newvalue); },
                true);

});
morels
  • 2,095
  • 17
  • 24
  • It depends. In very large web apps it is discouraged to watch everything in order to shorten the $digest cycle and keep the application fast. The most common practice here is to watch changes from a controller to the service or implement a signaling system with `$broadcast`/`$emit` – morels Jun 28 '16 at 07:17
  • I said no 3, which is implementing promises. – T J Jun 28 '16 at 07:19
  • Your method is still printing an empty array. – ecy Jun 28 '16 at 07:34
  • 1. what method? 2. Is there any console information you are not providing? – morels Jun 28 '16 at 07:54
  • The first method prints an object with no values but inherited values from default Object I think. The second one just prints an empty array. – ecy Jun 28 '16 at 08:04
  • Following MVC model, data acquiring should be handled inside "model" layer, which is represented as service in angular. So yeah No.3 is the way to go. – Icycool Jun 28 '16 at 08:58
  • -.- both 1 and 4 are at model level... the execution context is always the one calling the function i.e. the service. – morels Jun 28 '16 at 09:01
0

For my projects I normally put all of my $https in respective services, and expose function if it needs to be invoked outside.

app.service('userService', function(){
  var user = [];
  var isReady = false;

  var setUser = function(newObj){
    user.push(newObj);

    $http(...).then(function(){}, failHandler);

    function failHandler() {
      // undo the push and show error to user
    }
  };

  var getUser = function(){
    if (isReady) {
      return $q.when(user);
    }
    else {
      return $http(...).then(function(data){
        user = data.xxx;
        isReady = true;
      });
    }
  };

  return {
    setUser: setUser,
    getUser: getUser
  };
});

Note that getUser is now returning a promise, so you'll need to use .then to handle things.

You can change setUser to promise as well in order to show success or error on controller side.

Icycool
  • 7,099
  • 1
  • 25
  • 33