Jakub Kuchar
Jakub Kuchar

Reputation: 1665

AngularJS factory with $http call should i care when response is ready?

I am having similar factory with $http call inside like following:

appModule = angular.module('appModule', []);

appModule.factory('Search', function($rootScope, $http) {
  var Search;
  Search = {};
  Search.types: ["bacon"];
  Search.pastEvents = null;
  $http.get('api/highlights').success(function(response) {
    return Search.pastEvents = response.data;
  });
  return Search;
});

var notes_module = angular.module('notes', []);

notes_module.config(['$routeProvider', function ($routeProvider) {

  var notes_promise = ['Notes', '$route', 'Search', function (Notes, $route, Search) {
    //suspect that Search not having pastEvents ready in time of calling index method
    //Notes resource  
    return Notes.index({subject_id: 1 }, Search);    
  }];

  $routeProvider.when('/notes', {
    templateUrl:'index.tpl.html',
    controller:'NotesCtrl',
    resolve:{
      notes: notes_promise,
    }
  });

}]);

Should i care about when data from $http call is ready and when this factory is initialized/injected? Will pastEvents be ready? If i should care how do i do?

I suspect that Search object not having pastEvents ready in time of calling index method of Notes resource.

Upvotes: 4

Views: 7065

Answers (1)

asgoth
asgoth

Reputation: 35829

It depends:

If you immediately place in in $scope to be used e.g. in a ng-repeat, then no.

If you need in another function in your controller, then yes. E.g. if you use your pastEvents in a filter function on your controller. In such case it is best to keep all manipulations internally within your service and use $q to solve the asynchronuous riddle.

(This is just an example)

appModule.factory('sharedApplication', function($rootScope, $http, $q) {
  var deferred = $q.defer();

  $rootScope.$on('data:loaded', function(e, data) {
    deferred.resolve(data);
  });

  return {
     getApp: function(filter) {
        if (!filter) { filter = function() { return true; } }

        var filtered = {};
        deferred.promise.then(function(data) {
           filtered.pastEvents = _.filter(data, filter); 
        };
        return filtered;
     }
  };
});

A little explanation. The data arrives with an event in the service. At that point getApp() may already have been called. But that doesn't matter, because $q will make sure that the data will only be filtered when it later arrives. The controller doesn't need to know that, as long as it does not try to do things like:

$scope.app = service.getApp();
for(var i = 0; i < $scope.app.pastEvents.length; i++) {
   ...
} 

If you really need to evaluate the content in the controller, use $scope.$watch(), e.g:

$scope.$watch('app', function(value) {
   ...
}, true);

Edit:

In my opinion, in your situation Search is not yet resolved when you use it in your $routeProvider:

Notes.index({subject_id: 1 }, Search)

So try to resolve Search instead and use your Notes resource in your controller.

You need to return a promise in your Search service. Two options:

  • return the $http promise, but that probably not what you want, if you need to do something with the data first
  • use a $q and return its promise

$q example:

appModule.factory('Search', function($rootScope, $http, $q) {
  var deferred = $q.defer();
  $http.get('api/highlights').success(function(response) {
    deferred.resolve({
       type: ["bacon"],
       pastEvents: response.data)};
  });
  return deferred.promise;
});

Upvotes: 5

Related Questions