Force AngularJS service to return data before loading controller
Asked Answered
W

4

34

I have a service in Angular which uses my API to get user information and provides it to my controllers. It's set up like this:

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'ngResource', 'infinite-scroll', 'ui.bootstrap', 'ngCookies', 'seo'])
  .service('userInfo', function($http, $cookies){
    $http.get('/api/users/' + $cookies.id).
    success(function(data) {
      var userInfo = data.user[0];

      return userInfo;
    });
  }). // other stuff comes after this

In my controllers, I include it like:

function userProfile($scope, $cookies, userInfo, $http, $resource, $routeParams, $rootScope){
    $scope.user = userInfo;
    console.log('user info is')
    console.log(userInfo);

This is returning no data, while if I put the same service function in the controller itself it returns just fine. What am I missing here? Never used DI/Services in Angular before so might be a simple mistake somewhere.

I need to ensure that the service returns data before the controller loads. How can this be accomplished

Wanton answered 28/8, 2013 at 0:50 Comment(2)
you could simplify your console.logs like this: console.log('user info is ', userInfo);Slapbang
@EddieMongeJr really? had no idea, thanks.Wanton
D
42

You can make the factory to return a promise like this:

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'ngResource', 'infinite-scroll', 'ui.bootstrap', 'ngCookies', 'seo'])
    .service('userInfo', function ($http, $cookies) {
    var promise = $http.get('/api/users/' + $cookies.id).
    success(function (data) {
        var userInfo = data.user[0];
        return userInfo;
    });
    return promise;
}) // other stuff comes after this

And in your controller, do

function userProfile($scope, $cookies, userInfo, $http, $resource, $routeParams, $rootScope){
    userInfo.then(function(data){
        $scope.user = data;
    });
}

This can guarantee that whenever you use the service, it always gives you the data synchronously, you don't have to necessarily load any data before loading the controller.

Driskell answered 28/8, 2013 at 1:11 Comment(3)
Promises can be assigned directly to the $scope. When the promise resolves, Angular automatically updates the view.Othaothe
Note that promises are not automatically unwrapped in angular 1.2+.Ooze
is this really working? i see promise injected not the actual data from responseIntended
O
17

Your userInfo service has to return the promise returned by $http. That promise will then be injected into your controller, and the view will be updated as soon as the promise successfully resolves.


If you don't want the view to be rendered at all before userInfo resolves, you should set a resolve property on your route, and inject your service there:

$routeProvider.when('/profile', {
    templateUrl: 'profile',
    controller: userProfile,
    resolve: {
         userInfoData: function ($q, userInfo) {
             return userInfo;
         }
    }
});

Then just inject userInfoData into your controller in place of the userInfo service.

Othaothe answered 28/8, 2013 at 0:51 Comment(6)
I need this data available to all controllers/routes, is there a particular way to inject it once so I don't have to do it 20+ times for each route?Wanton
@Jascination - Not that I know of. I think you'll just have to set that resolve: resolveUserInfo for all the routes that you need it for.Othaothe
Interesting. I've tried your solution but it's still doing the same thing - loading controller before data resolves. I know this probably shouldn't be the case due to resolve, I'm assuming it's an async/callback issue as the resolveUserInfo function isn't finishing loading data before returning.Wanton
@Jascination - Have you returned the $http promise from the service?Othaothe
Any chance of accomplishing this without using routes? https://mcmap.net/q/451505/-angularjs-is-there-a-way-to-inject-data-into-a-controller-without-using-routes/4341650Thorstein
This is a nice tip. Helped me get a smooth transition between pages.Cyrstalcyrus
G
0

You can assign the resource object to your variable like that return $http.get(url)

Gloat answered 1/9, 2015 at 7:35 Comment(0)
B
0

I've faced the same problem in which I had to load data into a service using $resource and get that data into controllers $scope. I didn't wanted to directly return the promise and then use a resolver in the controller (as @zsong wrote) but to do all the magic into the service once and use the ability to assign a promise directly to a $scope as @Joseph Silber point out, so here is what I did :

return function($scope){promise.then(function(data){$scope.user = data})};

And used it in the controller like this :

userInfo($scope);

Not much of an improvement but it allowed me a clearer syntax in my controllers, hope it'll help even three years later !

Bisk answered 1/3, 2016 at 14:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.