This question already has answers here:
AngularJS : returning data from service to controller
(2 answers)
Why are Callbacks from Promise `.then` Methods an Anti-Pattern
(2 answers)
Why are AngularJS $http success/error methods deprecated? Removed from v1.6?
(2 answers)
Closed 4 years ago.
About 1 in every 10 times I refresh my project, the data from my database is not retrieved and I have to refresh the page again to get it. I tried adding a 101ms timeout onto the function which retrieves the data, and this worked- but also removed a lot of other functionality of the site.
The problem is that sometimes the page loads before the data is retrieved.
Is there a better way to solve this than using a timeout? My code for the POST is below:
get_data: function (kpi, date_from, date_to, callback) {
var config = {
method: 'POST',
url: '/getData',
data: {
kpi: kpi,
date_from: date_from,
date_to: date_to
}
};
$http(config)
.success(function (data) {
callback(data);
})
.error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
}
And then where this method is called (currently with the timeout, which is breaking other site functionality):
$scope.update_all_data = $timeout(function(){
$scope.show_loading = true;
var date_from = findDateUnix($scope.myDateFrom, $scope.availableDates);
var date_to = findDateUnix($scope.myDateTo, $scope.availableDates);
UpdateSvc.get_data($scope.kpi_selected, date_from, date_to, function(res){
raw_data = [];
if(res.raw_data != null) {
if(res.raw_data.length > 0){
raw_data = res.raw_data;
skey_data = [];
if(res.skey_data != null) {
skey_data = res.skey_data;
}
var num = $scope.show_tab.indexOf(true);
$scope.filtering = generateCategories();
$timeout(function(){
$scope.display_nodata = false;
resizeObjects();
$('.md-datepicker-input').prop('disabled', true);
//Take out loading
$scope.show_loading = false;
}, 100);
}else{
$scope.display_nodata = true;
$timeout(function(){ $scope.show_loading = false;}, 100);
}
}else{
$scope.display_nodata = true;
$timeout(function(){ $scope.show_loading = false;}, 100);
}
});
}, 101);
The problem is that sometimes the page loads before the data is retrieved.
I'm not sure what you mean by this. In an AngularJS application, the entire page must be loaded before AngularJS even begins to work. So your data (retreived with $http) will ALWAYS be loaded after the page loads, unless it is injected server-side.
I don't think you are having any issues retrieving the data from $http. You can easily confirm this by eliminating or commenting out all the code that does not deal directly with data retrieval. I would also strongly recommend the following changes:
First, using callbacks with $http is an antipattern: Why are Callbacks from Promise `.then` Methods an Anti-Pattern
(Note: newer versions of AngularJS use then/catch instead of success/error)
You should, instead use the built-in promise system. Calls to $http return a promise object that can be used directly like so:
get_data: function (kpi, date_from, date_to) {
var config = {
method: 'POST',
url: '/getData',
data: {
kpi: kpi,
date_from: date_from,
date_to: date_to
}
};
// return the $http call
return $http(config)
.error(function (data, status, headers, config) {
console.log(data, status, headers, config);
});
}
There is not really any reason to call $timeout in update_all_data function. You are adding 101 seconds AFTER your $http call has already returned. All that does is delay the display of the data further. Here, we are also calling the success function on the promise returned from the $http call in the service:
$scope.update_all_data = function(){
var date_from = findDateUnix($scope.myDateFrom, $scope.availableDates);
var date_to = findDateUnix($scope.myDateTo, $scope.availableDates);
UpdateSvc.get_data($scope.kpi_selected, date_from, date_to).success(function(data) {
console.log(data);
});
});
If this works and your data is logged 10/10 times then the issue is not with data retreival, it is with the rest of the code in your update_all_data function.
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I know questions like this have been asked before with Angular, unfortunately I can't get any of those solutions to work with my specific implementation so I am asking here.
I have a function scope.getOrders() here:
$scope.lastOrderFetch,$scope.orders;
$scope.getOrders = function(){
if(moment().diff(moment($scope.lastOrderFetch))>5000||$scope.lastOrderFetch==null){
$http({
method: 'GET',
url: 'http://www.example.com/getOrders',
}).then(function successCallback(html) {
$scope.orders = html.data;
$scope.lastOrderFetch = new Date();
return html.data;
});
}
else{
return $scope.orders;
}
};
I simply want the orders from the backend to popuplate a variable here:
var orders = $scope.getOrders();
Which is in another function. I gather my problem is orders is always undefined because it is trying to assign to it while $http is still working it's magic, and it is async so the $http data is not available right away.
I also gather I am supposed to use a service and then a promise or something, but my issue is that the $http should only fire based on a conditional in the controller. Also a lot of solutions mix callbacks and promises so I'm not sure what to do here. Also, doesn't $http itself return a promise? I'm not sure I need a service which is why I'm posting here to see how I would do this.
Any help would be appreciated.
This might help you too (using promise):
$scope.getOrders = function(){
var deferred = $q.defer();
if(moment().diff(moment($scope.lastOrderFetch))>5000||$scope.lastOrderFetch==null){
$http({
method: 'GET',
url: 'http://www.example.com/getOrders',
}).then(function successCallback(response) {
$scope.orders = response.data;
$scope.lastOrderFetch = new Date();
deffered.resolve(response.data);
});
} else {
deferred.resolve($scope.orders);
}
return deferred.promise;
};
and call it like:
$scope.getOrders().then(function(response){
// code after you get the response
});
I think you have problem with the workflow controlling, try to use callback function way:
$scope.getOrders = function(callback){
if(moment().diff(moment($scope.lastOrderFetch))>5000||$scope.lastOrderFetch==null){
$http({
method: 'GET',
url: 'http://www.example.com/getOrders',
}).then(function successCallback(html) {
$scope.orders = html.data;
$scope.lastOrderFetch = new Date();
return html.data;
});
}
else{
return callback($scope.orders);
}
};
$scope.getOrders(function dataReady(data){
var orders = = data; // data === $scope.orders
// do your logic here
});
First of all my apologies if the question is repeated.
I am making ajax requests using $q service.
UtilityService.requestCall('/test/encrypt-data', {'json_string' : data_to_encrypt, 'encryption_key' : window.localStorage.getItem("Mi_Encryption_Key")})
.then(function(encrypt_response) {
var requestConoce = parseInt(window.localStorage.getItem("Mi_Cnonce")) + 1;
window.localStorage.setItem("Mi_Cnonce", requestConoce);
requestData['cnonce'] = requestConoce;
requestData['encrypted_data'] = encrypt_response.data;
return UtilityService.requestCall($scope.apiDetails.url, requestData, 'GET');
})
.then(function(api_response) {
var requestConoce = parseInt(window.localStorage.getItem("Mi_Cnonce")) + 1;
window.localStorage.setItem("Mi_Cnonce", requestConoce);
return UtilityService.requestCall('/test/decrypt-data', {'encrypted_string' : api_response.encrypted_data, 'encryption_key' : window.localStorage.getItem('Mi_Encryption_Key') });
})
.then(function(decrypt_response) {
$scope.serverResponse = JSON.stringify(decrypt_response);
return;
})
.catch(function(error) {
alert("Some Error");
})
MyApp.factory('UtilityService', ['$http', '$q', function($http, $q) {
return {
requestCall: function(requestUrl, requestData, methodType) {
var deferred = $q.defer();
var serverUrl = window.localStorage.getItem("Mi_Server_Url");
$http({
method: (methodType) ? methodType : "POST",
url: serverUrl + requestUrl,
data: requestData
})
.then(function(result) {
deferred.resolve(result.data);
},
function(error) {
deferred.reject(error);
});
return deferred.promise;
}
};}]);
I am making requests using the above code. It is working fine for the request "/test/encrypt-data"
But then request for $scope.apiDetails.url is not working. Request is made without any parameters But, I am sending all required parameters in requestData.
This code working for other (even I am sending the data) but not working for this request.
It seems angularjs requests two time for the same request once without data and other with data.
Please help for this strange issue. Please take a look on these images these are showing two different requests once with data and other without data.
First of all you are getting two requests because one of them is the OPTIONS call and one is the actual POST call. This is the normal behaviour and nothing to worry about.
The second request you are making is a GET request which cannot contain any POST-Data. This is just not supported by HTTP.
So depending on what the backend is expecting you could either turn that request into a POST request or you could add the data as GET parameters which is described here: in the Angular docs
I have the following service which I eventually want to cache. However, I can't seem to figure out how set a variable within the service to the response data from the REST call using $http in Angular.
routerApp.service('activityService', function($http) {
delete $http.defaults.headers.common['X-Requested-With'];
this.temp = null;
this.setTemp(x) {
this.temp = x;
}
this.getActivities = function(x) {
var promise = $http({
method: 'GET',
url: 'my url is here...'
}).then(function(response) {
//setTemp(response.data); //definitely wont work
return response.data;
});
//how to set temp to response.data???
setTemp(promise.data); //doesn't work -
};
});
I don't know JS (or angular for that matter) very well. What is the best way to do this?
There is no need to cache the angular service, it's guaranteed to be a singleton.
If you mean to cache the response data, you will create a cache object in your service.
Now about the main question. In this code there is misuse of promises not angular services. Promises are asynchronous, meaning that a callback provided to .then() will be executed some time later, when request is finished. Besides .then returns another promise, which you should return from activityService.getActivities method.
this.getActivities = function(x) {
var promise = $http({
method: 'GET',
url: 'my url is here...'
}).then(function(response) {
setTemp(response.data); //meaning that setTemp somehow modifies it's argument
return response.data;
});
return promise;
};
Then in one of your controllers you will use this service's method attaching .then to it's return value.
.controller('someController', function (activityService) {
activityService.getActivities()
.then(function (data) {
doSomethingWithResponse(data);
}
});
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I'm currently writing a JS class for my webshop application to access product information which is stored on the server. The information should be loaded asynchronously. For this I use dojo.xhrPost(). I want to rewrite a function for this class, called getProductName(productId:int):String which returns the product name for a given product id. The function uses intern dojo.xhrPost(). But whould should I return the loaded product name due to the fact that I have to pass new function to load and error in dojo.xhrPost()?
Usage of this function: Other JS functions call this to load the product name to update the cart webpage without reloading the whole page. The data returned is in JSON format due to the fact that some additional information is transferred for error handling (the client function has to know if any error occurred on server-side or not).
The code for the function getProductName(productId:int):
function getProductName(productId) {
dojo.xhrPost({
url: '/shop/dataProduct.php',
handleAs: 'json',
content: {productId:productId},
load: function(response, ioArgs) {
if (!response.error) {
// here I would like to return response.data to the caller of myObj.getProductName(productId)
} else {
// here I would like to return "error" to the caller of myObj.getProductName(productId)
}
return response;
},
error: function(response, ioArgs) {
// here I would like to return "error" to the caller of myObj.getProductName(productId)
return response;
}
});
}
The usage:
var productName = myObj.getProductName(5);
The problem with the following line of code is that it is assuming the the retrieval of the product name is synchronous. But you are making an ajax call to get the name and therefore the code is asynchronous.
var productName = myObj.getProductName(5);
You need to use dojo/Deferred to handle the asynchronous call.
function getProductName(productId) {
var d = new dojo.Deferred();
dojo.xhrPost({
url: '/shop/dataProduct.php',
handleAs: 'json',
content: {productId:productId},
load: function(response, ioArgs) {
if (!response.error) {
var productName = ???;
d.resolve(productName);
} else {
d.reject(response.error);
}
},
error: function(err) {
d.reject(err);
}
});
return d;
}
Usage:
getProductName(1).then(
function(productName) {
// do something with product name
}, function(error) {
// handle error
});
http://dojotoolkit.org/reference-guide/1.8/dojo/Deferred.html
I'm working on wrapping my $resource requests in a simple wrapper. The main idea
is to be able to add some logic before the request is made. I've followed the nice article written by Nils.
Here you can see a service definition to access the REST API module.
resources.factory('Device', ['RequestWrapper', '$resource', 'lelylan.config', function(RequestWrapper, $http, config) {
var resource = $resource(config.endpoint + '/devices/:id', { id: '#id' });
return RequestWrapper.wrap(resource, ['get', 'query', 'save', 'delete']);
}]);
And here you can see the request wrapper definition.
resources.factory('RequestWrapper', ['AccessToken', function(AccessToken) {
var requestWrapper = {};
var token;
requestWrapper.wrap = function(resource, actions) {
token = AccessToken.initialize();
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) { request(wrappedResource, actions[i]); };
return wrappedResource;
};
var request = function(resource, action) {
resource['_' + action] = resource[action];
resource[action] = function(param, data, success, error) {
(AccessToken.get().access_token) ? setAuthorizationHeader() : deleteAuthorizationHeader()
return resource['_' + action](param, data, success, error);
};
};
var setAuthorizationHeader = function() {
$http.defaults.headers.common['Authorization'] = 'Bearer ' + token.access_token;
};
var deleteAuthorizationHeader = function() {
delete $http.defaults.headers.common['Authorization']
};
return requestWrapper;
}]);
Everything works just fine for the GET and DELETE methods (the ones that does not returns
a body seems), but I can't get $save working. What happens is that when the JSON of the
created resources returns it is not added. I have only the data I've set on the creation
phase. Let me make an example.
In this case we use the wrapped resource. If I try to get the #updated_at attribute I can't
see it. In the Chrome inspector I can see how the resource is successfully created.
$scope.device = new Device({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.device.$save(function(){ console.log('Device Wrapped', $scope.device.created_at) });
# => undefined
If I use $resource everything works fine.
// Suppose authorization is already set
var Resource = $resource('http://localhost\\:9000/devices/:id');
$scope.resource = new Resource({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.resource.$save(function(){ console.log('Device Base', $scope.resource.created_at); });
# => 2013-02-09T12:26:01Z
I started to check the angular-resource.js code but after few hours I couldn't really figure
it out. I can't get why the body is returned, but in the wrapper resource it is not accessible.
Any idea or help would be appreciated. Thanks.
While diving into AngularJS source code I've found the solution.
The problem was that the wrapper was returning a function instead of an object and this was giving some problems. The solution is to change the following row in the Wrapper:
return resource['_' + action](param, data, success, error);
with this one:
return resource['_' + action].call(this, params, data, success, error);
Why? The fast answer is because in the source code of angular-resource they use it. Actually #call run the function sending this to the calling object. It is often used to initialize an object. Learn more here.