4

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.

Jakub Kuchar
  • 1,665
  • 2
  • 23
  • 39
  • Can you be a bit more specific? What does exactly "behaving strangely" mean? – Alessandro Santini Jan 10 '13 at 15:16
  • If you are concern about data being available before rendering the view (to avoid flickering) check this [question (and answer)](http://stackoverflow.com/questions/11972026/delaying-angularjs-route-change-until-model-loaded-to-prevent-flicker). – bmleite Jan 10 '13 at 16:08

1 Answers1

5

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;
});
asgoth
  • 35,552
  • 12
  • 89
  • 98
  • thank you for your answer, i will check it. In time when you have posted your answer i have updated my question to be more clearly... – Jakub Kuchar Jan 10 '13 at 18:25
  • still stuck on this, could you please help me out regarding to my updated question? – Jakub Kuchar Jan 12 '13 at 09:55
  • yes, you are correct. Checking in firebug setting up breakpoints i don't see any pastEvents in time of return `Notes.index({subject_id: 1 }, Search);`. Object is there but empty pastEvents. Yes, i can switch resolving ` resolve:{ notes: notes_promise,} ` and resolving Search instead. But how since Search is just "pure" JS object? – Jakub Kuchar Jan 12 '13 at 11:11
  • I've added a $q example. You need to return a promise in your Search service. – asgoth Jan 12 '13 at 11:21
  • thank you i made it work. Am I asking in right in more generic way: How to resolve two factories A B before route change in controller and resolve factory B in help of resolved A? To describe the remaining doubts.. – Jakub Kuchar Jan 13 '13 at 17:08
  • Imho a factory should return a promise and if another factory needs it, that factory will need to wait for the data by using the then() function. – asgoth Jan 13 '13 at 17:16