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!!
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
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
}
)
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];
});
});
Related
I created an AngularJS service which does ajax request to the server and returns the response object to the controller. Below is my service
app.factory('ajaxService', function() {
return {
ajaxcall: function(url, type, data, handler) {
$.ajax({
url: url,
type: type,
data: data,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-OCTOBER-REQUEST-HANDLER", handler);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
}
})
.done(function(response) {
console.log("ajaxService done: ");
console.log(response);
return response;
})
.fail(function(response) {
console.log("in onCheckUser-error: ajaxService ");
});
}
}
});
The controller is defined as below
var app = angular.module('starter', [])
app.controller('myCtrl', function(ajaxService) {
var res = {};
res = ajaxService.ajaxcall("https://www.travelmg.in/check-login",'POST','',"onCheckLogin");
console.log(res);
});
Here, i get the expected response in console in the ajaxService service. When i return the response, i see an "undefined" value in res variable in console.
I don't understand why the res variable is undefined. Please suggest
Thats because your making an asynchronous call, which means it will not return the result immediately.
only way to resolve this is to receive the promise object returned from $.ajax & use the .done() function on it to receive the successful data.
What you need to do:
Move the done() & fail() outside service factory.
return the ajax promise object all the way to the consumer, i.e controller.
JS CODE:
//service factory code
return {
ajaxcall: function(url, type, data, handler) {
return $.ajax({
url: url,
type: type,
data: data,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-OCTOBER-REQUEST-HANDLER", handler);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
}
});
}
}
//controller code
app.controller('myCtrl', function(ajaxService) {
var res = {};
ajaxService.ajaxcall("https://www.travelmg.in/check- login",'POST','',"onCheckLogin")
.done(function(response) {
console.log("ajaxService done: ");
console.log(response);
//return response; // dont return instead process the result here
})
.fail(function(response) {
console.log("in onCheckUser-error: ajaxService ");
});
});
Note:
I would personally dont prefer mixing jquery and angularjs library unless jquery is really needed for some third party library. both are two different frameworks with different ideology,so dont mix them.
also if your referring to jquery only for $.ajax api ? then i would suggest you to use $http, whose API is same as $.ajax. ref: https://docs.angularjs.org/api/ng/service/$http
You have built the application in angular so it would be convenient to use $http directive to make ajax calls. Inject $http is your service, then you can handle the response as such:
ajaxService.ajaxcall("https://www.travelmg.in/check-login",'POST','',"onCheckLogin").then(function(response) {
console.log(response.data);
});
Working on my first Angular app here so excuse me if question not clear.
I have a service which gets from my api using a group variable in the header.
var theReq = {
method: 'GET',
url: API + '/homeq',
headers: {
'group': 'mobile'
}
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
self.getResults = function() {
return deferred.promise;
}
The issue I'm facing is with using the group variable that i specify rather than that preset one.
I can surely pass it into that function (i.e. self.getResults = function(groupToGet)) but how would it get from there to theReq that I process?
Any help appreciated.
Thanks.
You need to modify your function in following manner. $q is the service to create deferred objects. You need to inject it.
self.getResults = function(groupToSet) {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/homeq',
headers: {
'group': groupToSet
}
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
and you can use promise as
self.getResults("mobile").then(function(data) {
//success function here.
}).catch(function(error) {
//error function here
});
I have 2 $http functions that I call using ng-init because the data they return populates the page.
ng-init = "getOpen(); getClosed();"
Is this the best way?
First function;
$scope.getOpen = function () {
$http({
method: 'post',
url: "http://www.example.co.uk/php/open-get.php",
data: $.param({ 'location' : $scope.l,
'time' : $scope.t,
'day' : $scope.d,
'type' : 'get_restopen' }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).
success (function(data, status, headers, config){
if(data.success && !angular.isUndefined(data.data) ){
$scope.open = data.data;
} else {
$scope.open = [];
}
}).
error(function(data, status, headers, config) {
//$scope.messageFailure(data.message);
});
}
Second function;
$scope.getClosed = function () {
$http({
method: 'post',
url: "http://www.example.co.uk/php/closed-get.php",
data: $.param({ 'location' : $scope.l,
'time' : $scope.t,
'day' : $scope.d,
'type' : 'get_restopen' }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).
success (function(data, status, headers, config){
if(data.success && !angular.isUndefined(data.data) ){
$scope.closed = data.data;
} else {
$scope.closed = [];
}
}).
error(function(data, status, headers, config) {
//$scope.messageFailure(data.message);
});
}
Everything works great. My question I guess is this an efficient way of doing things in AngularJS? I'm new to angular so just seeking guidance.
1 - Are my $http executed simultaneously? Or is one completed before the other one is started?
2 - Is there any need to introduce $q or promises into my code? The functions are independent of each other
3 - How can I detect when all $http request have completed regardless of if successful or not
Is the below code correct?
$q.all([$scope.getOpen, $scope.getClosed]).then(function() {
//Both requests have been completed and I shall now set a bolean
$scope.compelete = true;
});
Assuming you call both methods yourself somewhere, yes. $http calls are async by default
Already done, $http actually returns a promise!
promise.all() is an elegant way to do so without modifying the return of the promise. It is effectively a completion watcher. More details over on the promise reference
I have 2 $http functions that I call using ng-init because the data
they return populates the page.
ng-init = "getOpen(); getClosed();"
Is this the best way?
As said into angular documentation :
This directive can be abused to add unnecessary amounts of logic into
your templates. There are only a few appropriate uses of ngInit, such
as for aliasing special properties of ngRepeat, as seen in the demo
below; and for injecting data via server side scripting. Besides these
few cases, you should use controllers rather than ngInit to initialize
values on a scope.
You can find it here : https://docs.angularjs.org/api/ng/directive/ngInit
you should avoid using ng-init into your templates. The recommended usage for initiating something into a controller is to call a private function directly inside your controller.
I also recommend to read that style guide about angular :
https://github.com/johnpapa/angular-styleguide
1 - Are my $http executed simultaneously? Or is one completed before
the other one is started?
The 2 calls are started almost simultaneously. Then, javascript holds a stack of callbacks he has to execute when he gets answers from your server. Just press F12 into your browser and find a "network" tab to see all your requests being launched.
Furthermore, ´.success´ is deprecated now. You should rather use ´.then´
https://docs.angularjs.org/api/ng/service/$http#deprecation-notice
2 - Is there any need to introduce $q or promises into my code? The
functions are independent of each other
Not in this case. You could use $q.all([promise1, promise2]) if you wanted to execute something after both calls had been executed
3 - How can I detect when all $http request have completed regardless
of if successful or not
Have a look at $httpInterceptors. Let share you a piece of code I implemented
angular.module("myModule").factory("myHttpInterceptor", [
"LoadingService",
"$q",
"Alert",
function (LoadingService, $q, Alert) {
function requestInterceptor(config) {
LoadingService.increaseCounter();
return config;
}
function responseInterceptor(response) {
LoadingService.decreaseCounter();
return response;
}
// ...
Is the below code correct?
$q.all([$scope.getOpen, $scope.getClosed]).then(function() {
//Both requests have been completed and I shall now set a bolean
$scope.compelete = true;
});
nope because you still the promise to be returned by your function. Also, ´.success´ does not implement the chaining of promises. You now HAVE to use ´.then()´
$scope.getOpen = function () {
return $http({
// config
}).
then(function(data, status, headers, config){
//handling success
},(function(data, status, headers, config) {
//handling error
});
Summary :
angular.module("yourApp").controller("someController", ["$scope", "$q"
function($scope, $q){
// init
init();
// Shared functions
$scope.getOpen = function () {
return $http({
method: 'post',
url: "http://www.example.co.uk/php/open-get.php",
data: $.param({ 'location' : $scope.l,
'time' : $scope.t,
'day' : $scope.d,
'type' : 'get_restopen' }),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
$scope.getClosed = function () {
return $http({
// config
});
// private functions
function init(){
var promises = [];
promises.push($scope.getOpen());
promises.push($scope.getClosed());
$q.all(promises).then(function(datas){
// Here, you know both calls have been successfull
// datas[0] is the result of $scope.open()
$scope.open = datas[0].data;
$scope.complete = true;
}, function(errors){
$scope.complete = true;
// You fall here if one of your calls has failed
});
}
}
]);
$http is asynchronous, so your 2 calls should be executed in parallel. If you really need to boil the 2 functions down to one, and/or you need to check all $http requests have completed, you can use $q.all Angularjs $q.all
I am doing some http calls in Angular and trying to call a different service function if an error occurs. However, regardless of my original service call function return, the promise it returns is always "undefined". Here is some code to give context:
srvc.sendApplicantsToSR = function (applicant) {
var applicantURL = {snip};
var promise = $http({
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
url: applicantURL,
data: applicant
})
.success(function (data) {
return data;
})
.error(function (error) {
return [];
});
return promise;
};
Then, in the controller:
for (var applicant in $scope.applicants) {
$scope.sendATSError($scope.sendApplicantsToSR($scope.applicants[applicant]), applicant);
}
$scope.sendATSError = function (errorCheck, applicantNumber) {
if (angular.isUndefined(errorCheck)) {
console.log(errorCheck);
AtsintegrationsService.applicantErrorHandling($scope.applicants[applicantNumber].dataset.atsApplicantID);
}
};
However, it is always sending errors because every response is undefined. How can I differentiate between the two returns properly? Thank you!
Looking at angular documentation, the sample code is
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
based on that - your first code snippet should be
srvc.sendApplicantsToSR = function(applicant) {
var applicantURL = {
snip
};
return $http({
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
method: 'POST',
url: applicantURL,
data: applicant
});
};
As others have said, $http's .success() and .error() are deprecated in favour of .then().
But you don't actually need to chain .then() in .sendApplicantsToSR() as you don't need (ever) to process the successfully delivered data or to handle (at that point) the unsuccessful error.
$scope.sendApplicantsToSR = function (applicant) {
var applicantURL = {snip};
return $http({
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
method: 'POST',
url: applicantURL,
data: applicant
});
};
Now, in the caller (your line of code in the for loop), a promise is returned (not data) and that promise will, on settling, go down its success path or its error path. Exactly what happens on these paths is determined entirely by the callback functions you write in one or more chained .thens .
So what you need to write is a kind of inside-out version of what's in the question - with $scope.sendApplicantsToSR() on the outside and $scope.sendATSError() on the inside - and linked together with a .then().
for (var prop in $scope.applicants) {
var applicant = $scope.applicants[prop];
$scope.sendApplicantsToSR(applicant).then(null, $scope.sendATSError.bind(null, applicant));
}
// Above, `null` means do nothing on success, and
// `function(e) {...}` causes the error to be handled appropriately.
// This is the branching you seek!!
And by passing applicant, the error handler, $scope.sendATSError() will simplify to :
$scope.sendATSError = function (applicant) {
return AtsintegrationsService.applicantErrorHandling(applicant.dataset.atsApplicantID); // `return` is potentially important.
};
The only other thing you might want to know is when all the promises have settled but that's best addressed in another question.
You should return your promisse to be handled by the controller itself.
Simplifying:
.factory('myFactory', function() {
return $http.post(...);
})
.controller('ctrl', function(){
myFactory()
.success(function(data){
// this is your data
})
})
Working example:
angular.module('myApp',[])
.factory('myName', function($q, $timeout) {
return function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve('Foo');
}, 2000);
return deferred.promise;
}
})
.controller('ctrl', function($scope, myName) {
$scope.greeting = 'Waiting info.';
myName().then(function(data) {
$scope.greeting = 'Hello '+data+'!';
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="ctrl">
{{greeting}}!
</div>
I have the following function named getvalue. It is inside an AngularJS module together with a controller. I am trying to call this function on click event invoking another function in a controller.(I hope I am clear)
function:
function getvalue(Data, $http) {
var value=undefined;
$http({
url: /myurl,
method: "GET",
params: {
tmp: Data.tmp,
pressure: Data.pressure
}
}).success(function (data, status, headers, config) {
value=parseFloat( console.log(data));//console working here
return value;
});
return value;
}
Code inside Controller
value= getvalue(formData, $http );
alert(value);//undefined here. Seems like value is never changed.
I have not getting the value on alert but console is printing the value. I need help if possible for two issues.
How can I change the value from inside success and return to controller?
Is there any way that I don't have to inject $Http from controller to function?
-----It would be nice if I can do that for unit testing.
you would ideally want to pull the $http service out of the controller and make a factory to do those calls.
in the factory have a function that accepts the data you are wanting to send and have it return the promise back to the controller
something like this
Repo
app.factory("fooRepo", ["$http", function($http){
return {
getValue: function(data){
return $http({
method: "POST",
url: "/myUrl"
});
}
};
}]);
Serivce
app.factory("foo", ["$q", "fooRepo", function($q, fooRepo){
return {
getValue: function(data){
var deferred = $q.defer();
fooRepo.getValue(data)
.success(function(results){
//do some work
deferred.resolve(results);
})
.error(function(error){
// do some work
deferred.reject(error);
});
return deferred.promise;
}
}
}]);
here is the controller
app.controller("fooCtrl", ["foo", function(foo){
foo.getValue(dataHere)
.then(function(results){
// do something here
});
}]);
Added Plunkr
As you are calling a method which is doing async call, you must return promise from there as you don't know when data will get back from the ajax. On success of that ajax you should update your variable. Outside of ajax function you will get undefined value because value is returned in async manner.
Function
function getvalue(Data) {
var value=undefined;
return $http({
url: /myurl,
method: "GET",
params: {
tmp: Data.tmp,
pressure: Data.pressure
}
})
}
Controller
getvalue(formData).success(function (data, status, headers, config) {
console.log(data);//you will get data here
});
A $http-Request is async. That means that alert will be called before the .success(..) callback is executed.
You can see a result on the console because it reflects changes made after the call of console.log().
Calling alert(value); in the .success()-Callback will shield the desired result.
Replace:
}).success(function (data, status, headers, config) {
value=parseFloat( console.log(data));//console working here
return value;
});
with:
}).success(function (data, status, headers, config) {
alert(parseFloat(data));
});