12

I am developing an angularjs app as a part of my angularjs learning. I have controllers and from there I am calling service layers.

leagueManager.service("teamsService", function($http){
    var teams = {};
        $http.get('data/teams.json').then(function(data) {
        teams = data;
    });
    this.getTeams = function(){
        return teams;
    };

});

I noticed that because of the asynchronous nature of $http.get.then stuff, the data is not retrieved immediately and hence I would not get the "teams" when I would call getTeams() from the controller (teamsController), I would get nothing.

Any idea how do I resolve this?

Second Attempt: After reading about deferred and promises on angular as suggested by the post below, I tried following but it still has no effect. My variable teams is not being populates as I want and they are populated afterwards and that is not helping in my UI:

My controller teamsController.js

leagueManager.controller('teamsController', function($scope, $location, teamsService, $routeParams){
//init function to initialize data when controller is called everytime.
var init = function(){
        $scope.teams = [];
        var promise = teamsService.getTeams();
        promise.then(
        function(data){
            console.log("teams after promise:="+data);
            $scope.teams = data;
        }
        ,function(reason)
        {
                alert('Failed: ' + reason);
        }
        );
        console.log("teams in the scope:="+$scope.teams);
};

init();
});

And here is my ServiceLayer teamsService.js

leagueManager.service("teamsService", function($http, $q){
this.getTeams = function(){
  var deferred = $q.defer();
     var url = 'data/teams.json';
     $http.get(url).success(function(data, status) {
         // Some extra manipulation on data if you want...
         deferred.resolve(data);
     }).error(function(data, status) {
         deferred.reject(data);
     });
     return deferred.promise;
}
});

Can you or someone help me what I am doing wrong? This is what is printed in the browser console upon execution:

teams in the scope:= teamsController.js:27

teams after promise:=[object Object],[object Object],[object Object],[object Object],[object Object]

This simply shows that I do get the json object but not at the time I want. For some reason this deffered/promise thing had no impact.

PLEASE HELP THIS NEW ANGULAR ENTHUSIAST

Community
  • 1
  • 1
cooler
  • 753
  • 4
  • 9
  • 18
  • 1
    I created a JsFiddle sample, that is almost similar to what you are looking for: http://jsfiddle.net/sravikiran/HYxC8/3/. Check it and compare it with your code. I will check your issue, but need some time. – S. Ravi Kiran Sep 13 '13 at 09:46
  • THanks a lot S. Ravi Kiran, your jsfiddle helped me debug the problem. The promise and defer logic was fine but it was my other part of the code that was giving me problems which I fixed. – cooler Sep 15 '13 at 02:40

2 Answers2

19

Yeah you will need to use a promise interface. So instead of returning a teams object, directly you'll have to return a promise:

Promise Resources:

In the service:

leagueManager.service("teamsService", function($http){
    var deferred = $q.defer();
    $http.get('data/teams.json').then(function(data) {
        deferred.resolve(data);
    });
    this.getTeams = function(){
        return deferred.promise;
    };
});

Then in the controller:

$scope.team = {};

var promise = teamsService.getTeams();
promise.then(function(data) {
    $scope.teams = data;
});
hajpoj
  • 13,299
  • 2
  • 46
  • 63
  • Hello @hajpoj thanks for your reply and I did read about deferred and promises but it has not helped me so far. I don't know what I am doing wrong. Please see what I am doing now again at the bottom of my question. – cooler Sep 12 '13 at 21:16
  • 1
    Hey i read over your edit. It appears you are now getting the data, but it loads in later. This is as expected. As far as I know, you **can't** get the data back when you call `teamService.getTeams()` (cuz $http calls are asynchronous) unless you make it a blocking call, which you don't want to do anyways. The controller, gets initialized first and you definitely don't want to block and wait for the data. The rest of the page won't load. What weird effects are you running into? – hajpoj Sep 13 '13 at 00:05
  • hey hajpoj thanks for your response but I am kinda disappointed. After all I thought that is why we are using promises and deferred so that we can get the objects at a time we want? If the asynchronous behavior is the desired one, then how can any application will be able to display a list of items that would be retrieved in real time from the backend service via an $http.get call? In my case that list is **teams** that I wanna display when the user loads the page for the first time. Anf for your information, I was getting exactly same behavior without the promises and deferred. – cooler Sep 13 '13 at 06:33
  • What I meant was - why do we even need to use promises and deferred at all if the same behaviors I could achieve with simple $http.get call? – cooler Sep 13 '13 at 06:34
  • oh is it the same behavior? I thought you were not able to get the team data in your controller before? – hajpoj Sep 13 '13 at 06:43
  • NO, I was able to get the team data in my controller even with simple $http.get but that happened asynchronously and hence the team list will be shown as empty on the page. I want to show the team list real time on the page. Any idea? I think this thread discusses about the same, but I am not sure: http://stackoverflow.com/questions/18477711/force-angularjs-service-to-return-data-before-loading-controller?rq=1 – cooler Sep 13 '13 at 06:46
  • I see. I had originally misunderstood your question, I had thought you were not able to get the data from $http.get. So what you are saying is true, asynchronously getting the data will leave your page blank until the data arrives, **however** that should only be for a couple milliseconds, or however long it takes to get your data. Once the data arrives for the back end it should show it. If the call is taking a long time (many seconds) then you might want to show a loading icon, but its still better to do this asynchronously so the rest of the page loads, and isn't waiting on the data... – hajpoj Sep 13 '13 at 18:10
  • I guess I don't understand what you mean by "real time". It should be pretty close to realtime. The page should load, then the data should load and appear soon after. If there is a long delay, then that is a network/server problem, most likely not an angular problem...Just another idea... Is your data set really large? Like 1000's of teams, and 1000's of data bindings? If so maybe your are running into too many data binding slow down described here http://stackoverflow.com/questions/9682092/databinding-in-angularjs. Sorry I wasn't able to solve your issue. – hajpoj Sep 13 '13 at 18:17
  • hey hajpoj, no problem at all. Thanks a lot for helping me here dude. I am still learning so no fault of yours. May be I have not been able to explain it properly. My data set is not large at all, it is only 5 teams as of now and the teams.json is stored in the local server (where the angular code is deployed) only for the time being. So network problem can be ruled out. The page is loading, then the data is also loading, but it doesn't appear on the page soon after as you think it should. That is the problem I am having currently. – cooler Sep 13 '13 at 19:41
  • Check the network tab in developer tools of your browser, there you will be able to see if the AJAX call is made to the server and also if the data is received – S. Ravi Kiran Sep 14 '13 at 04:56
  • 2
    @cooler if the data is returning properly, but not showing up it sounds like it could be a data binding issue. Maybe check your html to make sure the mark up is correct and corresponds to your javascript data structure. To be sure, your data is a json object correct? – hajpoj Sep 14 '13 at 18:48
  • @hajpoj thanks a lot. Actually I observed today that everything was working properly but I had some problems with my login after I was getting data and hence it was not showing up. Thanks a lot for helping me. The jsfiddle provided by Ravi Kiran helped me confirm that the promise and defer approach was fine and my logic was the problem. Thanks to both of you. – cooler Sep 15 '13 at 02:39
1

This should work fine:

myApp.factory('mainFactory',['$http',function($http){

var mainFactory = {};

mainFactory.getRandomUser = function(){
    var promise;
    if(!promise){
        promise = $http.get('http://api.randomuser.me/').success(function(d){
            return d;   
        });
        return promise;
    }
};

mainFactory.getRandomImage = function(){
    var promise;
    if(!promise){
        promise = $http.get('http://lorempixel.com/400/200/').success(function(d){
            return d;   
        });
        return promise;
    }
};

return mainFactory;

}]);
Kurt Du Bois
  • 7,550
  • 4
  • 25
  • 33
Yaniv Vova Alterman
  • 1,660
  • 1
  • 13
  • 11