Pardon as I am new to AngularJS.
I have the following nested $http call,
$http({
method: 'GET',
url: host1,
params: {
'format': 'json',
}
}).then(function successCallback(response) {
for (var i = 0; i < response.data.length; i++) {
$http({
method: 'GET',
url: response.data[i]['url'] + "packet-trace/base",
params: {
'format': 'json',
}
}).then(function successCallback(response2) {
//Retrieve some information from first $http call
var doSomething = response.data[i]['information'];
var doSomething2 = doSomething + response2.data['information'];
}, function errorCallback(response2) {
//Error
});
}
}, function errorCallback(response) {
//Error
});
I need to retrieve data from the first $http call, and then retrieve data from the $http, and use both of these data as part of my logic. However, I am unable to access the data from the first $http call. The loop counter 'i' always equal the length of response.data.
How can I access the first the data of $http call?
Additionally, are there any specific coding conventions or specific API I can use to call $http sequentially? Nesting $http calls gets messy and difficult to maintain.
Thanks.
The reason why this happens is because the loop probably has finished iterating before the AJAX call succeeds. At this stage i will obviously equal response.data.length.
You could capture the value outside the AJAX call and then use it in the success callback so that you don't depend on the i variable inside the callback:
for (var i = 0; i < response.data.length; i++) {
// Capture the value you need here
var someValue = response.data[i]['information'];
$http({
method: 'GET',
url: response.data[i]['url'] + "packet-trace/base",
params: {
'format': 'json',
}
}).then(function successCallback(response2) {
// Use the captured value from the first call here
var doSomething = someValue;
var doSomething2 = doSomething + response2.data['information'];
}, function errorCallback(response2) {
//Error
});
}
Well that is because of asynchronous nature of javascript. I suggest using async for your use case. May look a bit complex, but once you understand async concepts, it is breeze to use. This what you can do:
async.waterfall(
[
function(callback){
$http({
method: 'GET',
url: host1,
params: {
'format': 'json',
}
}).then(function successCallback(response) {
callback(null, response);
});
},
function(callback, response){
async.times(response.data.length, function(i,next){
$http({
method: 'GET',
url: response.data[i]['url'] + "packet-trace/base",
params: {
'format': 'json',
}
}).then(function successCallback(response2) {
//Retrieve some information from first $http call
var doSomething = response.data[i]['information'];
var doSomething2 = doSomething + response2.data['information'];
next(null,doSomething, doSomething2);
}, function errorCallback(err) {
next(err, null);
});
});
}
],
function(err,doSomethingArr, doSomething2Arr){
// get your arrays here
});
Related
I am trying to update views on multiple components. For that reason I am using broadcast. If I use my code without the $apply() the views are not updating. If I use apply on multiple views I am getting '[$rootScope:inprog] $apply already in progress' error.
Changed code
service.prototype.setNewTopic = function (topic) {
var self = this;
var promise = $http(
{
method: 'POST',
url: self.baseUrl + 'Admin/setNewTopic',
contentType: 'application/json',
data: {
topicName: topic
}
});
return promise;
}
I changed to how your $on method behaves, it should recive the data from the $broadcast and I guess set it appropriately in the component.
// controller - I assume the $scope property in the controller is called $scope.newTopic
service.setNewTopic($scope.newTopic).then( function(data) {
$rootScope.$emit('testMonitor',$scope.newTopic)
})
// one of these per listening component
$rootScope.$on('testMonitor', function(data) {
$scope.newTopic = data;
});
I changed the service to only do http work
// service
service.prototype.setNewTopic = function (topic) {
return $http(
{
method: 'POST',
url: self.baseUrl + 'Admin/setNewTopic',
contentType: 'application/json',
data: {
topicName: topic
}
});
}
Here I'm using Angularjs1.x and here is my condition. If condition is success then show the table otherwise throw an error. I know some code if its Success.
AngCtrl.Js
$scope.BtnCall = function () {
ServiceCntrl.CallData().then(function (d) {
$scope.EmpData = d.data;
});
}
AngService.Js
eApp.service("ServiceCntrl", function ($http) {
var xx = '';
xx= $http({
data: formData,
method: 'post',
url: Url,
datatype: "json"
}).success(function (rsp) {
RspData = rsp;
return RspData;
}).error(function (rsp) {
console.log('Error');
});
return xx;
};
Your x.then receives two functions x.then(function(){}, function(){}); first function is called when promise is successfully resolved and second function is called if promise is rejected(failed).
If your service function is return $http promise then your first function can have a parameter named(anything you like) and it will have response data that you can use. Second function can receive error parameters if any.
you should look at angular $http service documentation.
If your service is returning the promise of the get request, then you can write
$scope.BtnCall = function () {
var x = ServiceCntrl.CallData();
x.then(function(response) {
//Success callback
//code on success goes here
//response.data
}, function(response) {
//error callback
//code on error goes here
// server returns response with an error status.
});
you can use the ng-show/ng-hide to show and hide the contents on the html page.
You can write your success/fail code as the following:
$scope.BtnCall = function() {
var x = ServiceCntrl.CallData();
x.then(function(result) {
// Success code here
// Do something and resolved the promise
}, function(reason) {
// Error code here
// You may handle/reject the reason in params
});
});
See also the Angular docs for $q.
The AngularJS $http service makes a request to the server, and returns a response
The example above executes the $http service with an object as an argument. The object is specifying the HTTP method, the url, what to do on success, and what to do on failure.
$scope.BtnCall = function () {
ServiceCntrl.CallData().then(function (d) {
$scope.EmpData = d.data;
});
}
AngService.Js :
eApp.service("ServiceCntrl", function ($http) {
var xx = '';
xx= $http({
data: formData,
method: 'post',
url: Url,
datatype: "json"
}).success(function (rsp) {
RspData = rsp;
return RspData;
}).error(function (rsp) {
console.log('Error');
});
return xx;
};
I need to call onSuccessLogin method from ajax success method, or put response as argument into onSuccessLogin.
Thank you for answers.
submit: function (form) {
$.ajax({
type: "post",
url: "/login",
data: {
login: $(form).find('#login').val(),
password: $(form).find('#password').val(),
deviceType: environmentInfo.browser,
sdkVersion: environmentInfo.browserVersion,
osVersion: environmentInfo.OS
},
success: function(res) {
localStorage.setItem("languagesList", res);
//how to call onSuccessLogin method from here?
},
//Another implementation
// success: this.onSuccessLogin(res), - dosen't work, if I want put response from ajax into this method call
error: this.onErrorLogin
});
},
onSuccessLogin: function () {
//localStorage.setItem("languagesList", res); get response from second implementation
window.location = "/";
}
By creating a reference (that can be seen from the scope of AJAX success callback) to the object holding onSuccessLogin.
Inside the submit method (as a sibling of onSuccessLogin) that object is this.
Also see MDN reference on ES6 Arrow functions - abstraction for binding this to enclosing scope.
submit: function (form) {
var self = this;
$.ajax({
type: "post",
url: "/login",
data: {
login: $(form).find('#login').val(),
password: $(form).find('#password').val(),
deviceType: environmentInfo.browser,
sdkVersion: environmentInfo.browserVersion,
osVersion: environmentInfo.OS
},
success: function(res) {
localStorage.setItem("languagesList", res);
self.onSuccessLogin();
},
//Another implementation
// success: this.onSuccessLogin(res), - dosen't work, if I want put response from ajax into this method call
error: this.onErrorLogin
});
},
onSuccessLogin: function () {
//localStorage.setItem("languagesList", res); get response from second implementation
window.location = "/";
}
I don't know this logic is correct or not , if you place the function outside of ajax loop it will work .
$.ajax({
url: 'test',
success: function(data) {
alert("received contents="+data);
onSuccessLogin();
}
});
function onSuccessLogin() {
alert("Successfully logged!!!");
}
I have the following code:
var User = {
get: function (options) {
var self = this;
$.ajax({
url: options.url,
success: function (data, response) {
self.nextPageUrl = data.pagination.next_page;
options.success(data, response);
}
});
},
nextPage: function (success) {
this.get({
url: this.nextPageUrl,
success: success
});
}
}
User.get({
url: 'https://cache.getchute.com/v2/albums/aus6kwrg/assets',
success: function (data, response) {
// Through `data.pagination.next_page` I can get the URL
// of the next page.
}
});
User.nextPage({
success: function (data, response) {
// Here I want to make the same request but using the next_page
// based on the next related to the previous' one.
}
});
The problem
Basically, I want to perform the nextPage() operation based on the antecessor request (User.get()), but due to its asynchronousity, the nextPage() method doesn't know the this.nextPageUrl property—it returns undefined as expected.
Finally, the question is: can someone think in a way to keep the current syntax flow but solving this approach? Actually, is there a way?
And no, I'm not available to make a synchronous request.
General knowledge
I thought to use an event mechanism to deal with this: when the request is made and .nextPage() is called, then try to listen to an event to be emitted for x seconds, then I expected the this.nextPageUrl property to be available in that event-based scope.
What do you guys think?
DISCLAIMER: The logic of next_page is preprocessed by the server and only then is sent to the client. I have no option to use an increment/decrement behavioral operation.
If you want to play with this problem, click here for the jsFiddle.
jsFiddle Demo
There are a couple of options. You could bind the setter of the property to also call the nextPage, you could poll from calling nextPage every n milliseconds until the nextPageUrl property was populated, you could use a promise, you could use a nextPageQueue.
I think that a queue may be the simplest form of completing this. I also think it may be useful to have User store some local variables in this situation, and that the use of a function object may be more inline with that.
It would look like this
var User = new function(){
var pageQueue = [];
var get = this.get = function (options) {
$.ajax({
url: options.url,
dataType: 'JSON',
success: function (data, response) {
options.success(data, response);
var nextPageUrl = data.pagination.next_page;
if( pageQueue.length > 0 ){
pageQueue[0](nextPageUrl);
pageQueue.splice(0,1);
}
}
});
};
var nextPage = this.nextPage = function (options) {
pageQueue.push(function(nextPageUrl){
get({
url: nextPageUrl,
success: options.success
});
});
};
};
and your calls would not change.
User.get({
url: 'https://cache.getchute.com/v2/albums/aus6kwrg/assets',
success: function (data, response) {
// Through `data.pagination.next_page` I can get the URL
// of the next page.
console.log('get');
console.log(data);
console.log(response);
}
});
User.nextPage({
success: function (data, response) {
// Here I want to make the same request but using the next_page
// based on the next related to the previous' one.
console.log('next');
console.log(data);
console.log(response);
}
});
You can grab a reference to your User object before making the asynchronous request.
var User = {
get: function (options) {
var self = this;
$.ajax({
url: options.url,
success: function (data, response) {
self.nextPageUrl = data.pagination.next_page;
options.success(data, response);
}
});
},
You can modify your get method and eliminate your nextPage method entirely:
var User = {
url: 'https://cache.getchute.com/v2/albums/aus6kwrg/assets',
get: function (options) {
var self = this;
self.pageXhr = $.ajax({
url: self.nextPageUrl ? self.nextPageUrl : self.url,
dataType: 'JSON',
success: function (data, response) {
self.nextPageUrl = data.pagination.next_page;
self.pageXhr = false;
options.success(data, response);
}
});
}
}
Then whenever you call User.get, it will either call the current page or the next page. I am not sure about the context of when you want to get the subsequent pages, but if you need the requests to be queued you can wait for the existing request to finish before triggering the next request. For example:
if (self.pageXhr) {
self.pageXhr = self.pageXhr.then(function() {
return $.ajax({
url: self.nextPageUrl ? self.nextPageUrl : self.url,
dataType: 'JSON',
success: function (data, response) {
self.nextPageUrl = data.pagination.next_page;
options.success(data, response);
}
});
});
}
I have a service that calls a URL for fetching details of a user.
...
this.getUserDetail = function (userId) {
// deal cache
var request = $http({
method: "get",
url: "/users/"+userId
});
return request.then(successFn, errorFn);
};
But at another place, I am fetching the user details too and creating a map of newly fetched users. I want to reuse an already fetched user from my JavaScript object.
this.getUserDetail = function (userId) {
if (userMap[userId]) {
return $q.resolve({
'result': userMap['userId']
});
}
var request = $http({
method: "get",
url: "/users/"+userId
});
return request.then(successFn, errorFn);
};
But this doesn't work. I have included $q. I don't get JavaScript errors, except that at a place where I am using this.getUserDetail(userId).then(..., it throws error, as I am may be not returning a succesFn from the way I am doing it.
Am I doing it properly?
The function that you call is using AJAX.
Now from your question, since you are using then, this.getUserDetail(userId).then(), it means getUserDetail must return a promise itself.
Now if I understand it correctly, you want to resolve a promise with random data, without making AJAX call when an item is cached.
In that case, make your function to conditionally use promise object.
this.getUserDetail = function (userId) {
var cachedUser = userMap(userId),
deferredData = $q.defer();
var request = cachedUser ? deferredData.promise : $http({
method: "get",
url: "/users/" + userId
});
if (cachedUser) {
deferredData.resolve({
'data': {
'result': cachedUser
}
});
}
return request.then(successFn, errorFn);
};
Edit:
And then use it in your controller as:
this.getUserDetail.then(function(response){
// this response object is same object with which
// promise was resolved.
// Doesn't matter whether the promise was AJAX or your own deferred.
});
Doesn't matter whether the promise was AJAX or your own deferred.
You can use AngularJs built in cache:
var request = $http({
method: "get",
url: "/users/"+userId,
cache: true
});
this.getUserDetail = function (userId) {
if (userMap[userId]) {
return $q.when(userMap[userId]);
} else {
return $http({
method: "get",
url: "/users/"+userId
})
.then(function (result) {
// add to userMap;
return result;
});
}
};