0

I've been trying to make a pretty and user friendly app in angular and i'm at the part where i let the user know stuff is loading when i make ajax requests. What i'm trying to accomplish : i want to create a service that handles the initialization data fetching and pass the data as an object to my controller when it becomes available. Of course, the view isn't displayed until all the initial data my controller needs is available. Here's what i have so far:

app.js

var Application = angular.module('ReporterApplication', ['ngRoute']);

Application.service('Initialize', ['$http', '$q', '$timeout', function($http, $q, $timeout)
{   

    this.serverData = function() 
    {
        $http.get('/packing/scan.action')
            .success(function(data)
            {                         
                return data;
            })
            .error(function(data)
            {
                return data;
            });

    };   

}])


Application.config(['$routeProvider', '$interpolateProvider',

    function($routeProvider, $interpolateProvider) {

        $interpolateProvider.startSymbol('<%');
        $interpolateProvider.endSymbol('%>');

        $routeProvider

            .when('/packing/scan.html', {
                templateUrl: 'packing/scan.html',
                controller: 'PackingScanController as packer',
                resolve: {

                    initData : 'Initialize'   .... etc more code

scan.js

Application.controller('PackingScanController', ['$scope', '$http', 'initData', function($scope, $http, initData) {

    var packer = this;

    packer.packedToday = initData.serverData(); ... etc more code
1

3 Answers 3

1

If you want your controller to wait, what you need to do is to configure the resolve option correctly. For this, you need to have your service return a promise, otherwise the router will not wait.

resolve - {Object.<string, function>=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated.


Here's a simplified example:

app.service('Initialize', ['$http', function($http) {
  this.serverData = function()
  {
    // return the http promise
    return $http
      .get('config.json')
      // use success/error if needed
      .then(function (response) {
        // make sure, only the response data is used
        return response.data;
      })
    ;
  };
}]);

app.config(['$routeProvider', function($routeProvider) {
  $routeProvider
    .when('/', {
      template: '<pre>{{ main.initData|json }}</pre>',
      controller: 'MainCtrl as main',
      resolve: {
        // configure with DI
        initData: ['Initialize', function (Initialize) {
          // call the service method, and return the promise
          return Initialize.serverData();
        }]
      }
    })
  ;
}]);

app.controller('MainCtrl', function (initData) {
  // initData will be loaded
  this.initData = initData;
});

demo: http://plnkr.co/edit/sOwyHz4yMvwujBWYzOn0?p=preview

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

4 Comments

Thanks alot man, your example was well explained and pretty straightforward. Could i somehow pass the said promise directly into the resolve object? Cause i'd like to keep as little logic in the config as possible, because there are gonna be more and more routes. Also, why isn't possible to work with success and error instead of get? I tried it and it returns an object all the way
@JonathanSzekely A few notes: Personally I use ui-router, with that, there are some syntactic shortcuts with regards to how dependencies can be resolved. As of $http I must say, that I'm not quite fond with it not returning a normal promise object. Because of that, I usually tend to hide the http call inside a service, which only ever returns real promises (if one could call them that).
what's the deal with the "real" and "fake" promise objects?
@JonathanSzekely That's just words I use. I would call a real promise, promises created with $q. The problem with $http is, that it returns a decorated promise (success/error methods). But I don't necessarly want to expose the implementation detail that whatever async. service provides a response, actually uses $http.
0

You can inject your service in any controllers so you don't need to pass it via resolve.

Application.controller('PackingScanController', ['$scope', '$http', 'Initialize', function($scope, $http, Initialize)

For loader normally what I do is I check the availability of the data in template using ng-show

<div ng-show="!data">Loading...</div>
<div ng-show="!!data" ng-repeat="d in data">...</div>

Any time when you need the loader to show, just set your $scope.data to null and proceed to call your API service.

By the way your service won't work without promise as suggested by other user in your comment.

Comments

0

All these errors come from the fact that we’re trying to transform an asynchronous call into a synchronous one. That is simply not possible.

serverData() doesn’t return anything anymore. The return data statement is not returning from serverData(). It’s returning from the anonymous callback function passed to then().

The anonymous callback is not called immediately. It’s called when the response is available. And that happens long after serverData() has returned.

So, 1. one way: use callbacks

Application.service('initdata',function($http){
    var serverData = function(callback){
        $http.get('/packing/scan.action').then(function(response){
            callback(response.data);
        });
   };

   return {
       serverData: serverData
   };
});

Application.controller('PackingScanController',function(initdata){
    initdata.serverData(function(serverdata){
       $scope.serverdata = serverdata;
    });
});

2. Second way simply return the HTTP promise, and let the controller deal with it.

Application.service('initdata',function($http){
    var serverData = function(){
        return $http.get('/packing/scan.action');
    };

    return {
        serverData: serverData
    };  
});

Application.controller('PackingScanController',function(initdata){
    initdata.serverData().then(function(response){
        $scope.serverdata = response.data;
    });
});

3.You can also return serverData promise instead of returning http response promise.

Application.service('initdata',function($http){
    var serverData = function() {
        var defer = $q.defer();

        $http.get('/api/ponies').then(function(response) {
            defer.resolve(response.data);
        });

        return defer.promise;
    };

    return {
        serverData: serverData
    };  
});

Application.controller('PackingScanController',function(initdata){
    initdata.serverData().then(function(response){
        $scope.serverdata = response.data;
    });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.