1

I am attempting to call multiple $http request in a factory which I Am using to flush out multiple inputs with data and set default selections. I Want to then call a promise once all 3 of these are done to call in the real form data (if there is any, sometimes there will not be in which case it will do nothing) to overwrite the defaults in place.

So here is my attempt at this -

the factory I set up a factory to call all 3 inputs and their defaults (I am being sent them individually, i cannot change this for now). It looks like this:

.factory("getDefaults", function() {

return {
    instructions: function() {
        $http({
        method: "GET",
        url: "/getStringDropdown/materials"
    })
    .success(function(data, status, headers, config){
        $scope.instructions.materials = data.text;
    });

    $http({
        method: "GET",
        url: "/getStringDropdown/reinforce"
    })
    .success(function(data, status, headers, config){
        $scope.reinforce = data.options;
        $scope.instructions.reinforce = $scope.reinforce[data.default];
    });

    $http({
        method: "GET",
        url: "/getStringDropdown/procedure"
    })
    .success(function(data, status, headers, config){
        $scope.procedures = data.options;
        $scope.instructions.procedures = $scope.procedures[data.default];
    });
     //return data here? 

    }
  };

})

My question here is - do i have to return the data here? And also can I define the scopes here that are being used (as apposed to in the actual controller). I'm pretty sure this is wrong but I cant' find a good example of how to structure something like this properly.

The call in the contoller

So i the controller my thinking is i would then try something like this -

    getDefaults.instructions().then(function(){
            //possible return data here
            //AFTER data is flushed out call farm data

    $http({
        method: "GET",
        url: "/getSavedFormData/formID"
    })
    .success(function(data, status, headers, config){
        $scope.instructions.materials= data.materials;
        $scope.instructions.procedures = $scope.procedures[data.procedure];
        $scope.instructions.reinforce = $scope.reinfoce[data.reinforcement];
    });

        });

So big picture - I am trying to get these 3 calls to run and populate and THEN the second one. I'm not sure what might or might not be the best approach, factory seemed to make sense based on the trying to consolidate the 3 calls into 1 place with a promise when they are all done. I'm thinking i need to return the data, but it would be pretty nice if i could define the scopes for the controller in the factory. I am still getting my bearing with angular so any/all guidance would be much appreciated. Thanks for reading!!

1

3 Answers 3

3

Your service is not aware of your $scope out of the box, nor do you probably want it to be as the whole point of services is to assist with the modularity of your code.

What you probably want to do is actually return the $http promise from your service so that your .success() callback can actually set models on the $scope via closure (being inside the controller).

So your factory would be more like this:

.factory("getDefaults", function() {
  return {
    instructions: $http({ method: "GET", url: "/getStringDropdown/materials" })
  }
});

If you really think you'll never need those http calls separately and you only care about when they all resolve. You could return a $q.all() promise that will resolve when they all resolve:

.factory("getDefaults", function($http, $q) {
  var promise1 = $http({ method: "GET", url: "/getStringDropdown/materials" });
  var promise2 = $http({ method: "GET", url: "/getStringDropdown/materials" });
  var promise3 = $http({ method: "GET", url: "/getStringDropdown/materials" });
  return {
    data: $q.all([promise1,promise2,promise3]),
    anotherCall: $http.get('/anothercallUrl')
  }
});

So now from your controller you could just do:

function myCtrl($scope,getDefaults){
   getDefaults.data.then(function(data){
     //access all three of your promises, their data, and set $scope models here
     getDefaults.anotherCall.success(function(data){
       //or another http call
     });
   };
}

$q.all documentation here: https://docs.angularjs.org/api/ng/service/$q

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

4 Comments

This is great, thanks for your help. Is there a way do something like another .then after I set the scopes to then call the last $http?
Sure, you can just make another service call that returns another promise. getDefaults.anotherCall.success(function(data){}));
Awesome, thank you. Could I also just drop the actual $http inside there instead of another call to a service?
Sure can, that is the beauty of dependency injection. Just add $http to your controller dependancies and run it from there.
1

Using scopes in factories is not a good idea , my suggestion to not use, about multiple http calls you should use $q.all

example here in fiddle

for each call you should create defered object push it into array then use

$q.all(deferesArray).then(
      function(resp){
         // all requests are resolver , resp is array with resolved datas
        }
  )

Comments

1

You may want to have a look at angular's q$ (https://docs.angularjs.org/api/ng/service/$q)

1.) Create the first three promises that must finish "first"

 var deferred = $q.defer();
$http({
    method: "GET",
    url: "/getStringDropdown/procedure"
})
.success(function(data, status, headers, config){
    deferred.resolve(data);
});
var promise1 = deferred.promise;

2.) Use all method of q$, then call a "then" to the result to execute the second part of your logic

q$.all([promise1, promise2, promise3])
.then(function(results) {
 $http({
    method: "GET",
    url: "/getSavedFormData/formID"
  })
  .success(function(data, status, headers, config){
    $scope.instructions.materials= data.materials;
    $scope.instructions.procedures = $scope.procedures[data.procedure];
    $scope.instructions.reinforce = $scope.reinfoce[data.reinforcement];
 });
});

2 Comments

Ok this looks really awesome. Where in this do I put the data in the first 3 scopes? For example - the deffered call you have shown with procedures, I need to use the data to do this $scope.procedures = data.options; $scope.instructions.procedures = $scope.procedures[data.default];. Where would i place that? Thanks!!
The "results" parameter received by the promise contains an array of result objects, in the order of how the first 3 promises were passed in. So, $scope.procedures = results[2].options; for example...

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.