14

I am having a problem getting data from a service populated into my view. I have a service defined as such

app.factory('nukeService', function($rootScope, $http) {
    var nukeService = {};

    nukeService.nuke = {};

    //Gets the list of nuclear weapons
    nukeService.getNukes = function() {
        $http.get('nukes/nukes.json')
            .success(function(data) {
                nukeService.nukes = data;
            });

        return nukeService.nukes;
    };

    return nukeService;
});

and my controller

function NavigationCtrl($scope, $http, nukeService){



    /*$http.get('nukes/nukes.json').success(function(data) {
        $scope.nukes = data;
    });*/

    $scope.nukes = nukeService.getNukes();

}

If I use the $http.get from the controller the data populates fine, however, if I try to call the data from the service, I get nothing. I understand that the query is asynchronous but I am having a hard time understanding how to populate the $scope variable once the data is returned. I could use $rootscope to broadcast an event and listen for it in the controller but this does not seem like the correct way to accomplish this. I would really appreciate any advice on how to do this the correct way.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
jamesamuir
  • 1,397
  • 3
  • 19
  • 41

3 Answers3

28

I think this should solve your problem

app.factory('nukeService', function($rootScope, $http) {
    var nukeService = {};

    nukeService.data = {};

    //Gets the list of nuclear weapons
    nukeService.getNukes = function() {
        $http.get('nukes/nukes.json')
            .success(function(data) {
                nukeService.data.nukes = data;
            });

        return nukeService.data;
    };

    return nukeService;
});

function NavigationCtrl($scope, $http, nukeService){

    $scope.data = nukeService.getNukes();

    //then refer to nukes list as `data.nukes`

}

This is a problem with object reference.

when you calls nukeService.getNukes() you are getting a reference to a object a then your variable $scope.nukes refers that memory location.

After the remote server call when you set nukeService.nukes = data; you are not changing the object a instead you are changing nukeService.nukes from referencing object a to object b. But your $scope.nukes does not know about this reassignment and it still points to object a.

My solution in this case is to pass a object a with property data and then only change the data property instead of changing reference to a

Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • That worked but can I ask why? I am assuming that it has something to do with .data being a container for the json data instead of trying to pass it directly? Btw, thank you for the prompt reply! – jamesamuir Mar 01 '13 at 16:09
  • 8
    This doesn't account for asynhcronous loading -- `$scope.data` comes back as null, since `getNukes()` returns immediately without waiting for a result from `$http`. – Engineer Oct 16 '13 at 12:40
  • 1
    @NickWiggill It worked earlier, since the view would resolve the promise. But using the data in the controller would possibly lead to errors. – Matsemann Oct 27 '13 at 15:01
  • As @NickWiggill stated this is not async. Is there another way to solve this? – Jan-Terje Sørensen Apr 14 '14 at 08:27
  • 3
    @arcone Broadly, you need to return the promise which `$http.get` returns, not return `.data` (which is `null` at this point), from `nukeService.getNukes`. In your calling code, you must use `promise.then` to provide a callback that will continue your code flow once the promise actually resolves. – Engineer Apr 14 '14 at 10:58
9

This should be as follows. As mentioned by NickWiggill's comment, undefined will be assigned to nukeService.data if we do not return promise.

app.factory('nukeService', function($rootScope, $http) {
    var nukeService = {};
    //Gets the list of nuclear weapons
    nukeService.getNukes = function() {
       return $http.get('nukes/nukes.json');
    };

    return nukeService;
});


  function NavigationCtrl($scope, $http, nukeService){
     nukeService.getNukes().then(function(response){

        $scope.data = response.data;
    });

   }
Ganesh Nemade
  • 1,504
  • 12
  • 17
4

What I do is that I expose the data straight from the service, and have a method which initializes this data. What is wrong with this?

Service:

app.factory('nukeService', function($scope, $http) {
    var data = {};
    data.nukes = [];

    //Gets the list of nuclear weapons
    var getNukes = function() {
        $http.get('nukes/nukes.json').success(function(data) {
                data.nukes = data;
        });
    };

    // Fill the list with actual nukes, async why not.
    getNukes();

    return {
        data : data
        // expose more functions or data if you want
    };
});

Controller:

function NavigationCtrl($scope, nukeService){
     $scope.data = nukeService.data;
     //then refer to nukes list as `$scope.data.nukes`
}
dalvarezmartinez1
  • 1,385
  • 1
  • 17
  • 26