AngularJS restangular post callback - javascript

I am using restangular with AngularJS, and I need to use some function, which will trigger callback function/promise after POST response.
I tryed setResponseInterceptor which did not helped in this situation.
I need to use this function to lock form from submission until response will come back to client, and then unlock it to future use.
Do you know some magic for this?
I am using this code, and want to fire custom function when POST response will come to the client, something like .then()
reportAPI.createReport = function (apiData) {
return baseReport.customPOST(apiData, 'createreport');
};
var unlockForm = function () {
console.log(new Date().getTime());
};
Restangular.setResponseInterceptor(function (data, operation, what, url, response, deferred) {
if (operation === 'post') {
console.log(new Date().getTime());
console.log(operation);
console.log(what);
console.log(response);
if (response.data) {
unlockForm();
}
return data;
}
});

Related

JavaScript: Retry request on fail and continue normal app flow

Quick context:
The user has a problem with the network and I need to repeat requests to the server to continue loading app instead of showing an error.
Existing code in the app:
ajaxPostWithRootData = function (url, rootData) {
//prepare request
var request = decoratorFunction(
$.ajax({
//standard data required to send request
})
)
.done(function (result) {
// normal flow
})
.fail(function (e) {
//I want to repeat call after 1 second
});
return request;
};
Example call:
return ajaxPostWithRootData(url, data)
.done(function (result) {
//do some stuff
})
.fail(function (e) {
//hide loading, show error message
});
};
I thought to write in fail() something like that:
//repeatOnFail is a new function's param to prevent infinite loop
if (shouldRepeatOnConnectionProblem(repeatOnFail, e)) {
setTimeout(() => ajaxPostWithRootData(url, rootData, false), 1000);
}
Unfortunately JS works asynchronously here and even I get success response in second request, it's too late because app executes code from second fail().
Do you know how to change this code to "back" to the correct flow when I got a success response after the second call?
My knowledge about JS is poor, so even I know have an idea how to fix it, I don't know how to implement a solution ;/
If I understand correctly, you would like to invoke $.ajax({..}) with a fixed configuration and, if that first invocation fails, immediately retry the $.ajax({..}) request with the same configuration, in which case the following changes to ajaxPostWithRootData should achieve what you require:
var ajaxPostWithRootData = function(url, rootData) {
// Define reusable function that sets up and invokes
// ajax request
var doRequest = function() {
return decoratorFunction(
$.ajax({
//standard data required to send request
}))
}
// Return a promise that is controlled by this deferred behavior
return $.Deferred(function(deferred) {
doRequest()
.fail(function (){
doRequest ()
.fail(function () {
// On fail after second attempt, throw error
// to external app flow
deferred.reject();
}).done(function (result){
// If second attempt succeed, continue external
// app flow
deferred.resolve(result);
});
})
.done(function (result){
// If first attempt succeed, continue external
// app flow
deferred.resolve(result);
});
}).promise();
};
There updates should also work nicely with your external code and preserve the overall flow of events that you're after.
Hope that helps!

Return result of XMLHttpRequest

I'm trying to return the result of an XMLHTTPRequest:
Click me for Google CDN jQuery!
<script>
const url = {
httpRequest: function(callback) {
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", callback);
xhr.open("GET", document.querySelector("a").href); // Using querySelector to simulate how my real program works - I don't want to hardcode anything mainly because this should be dynamic.
xhr.send("");
},
compileData: function(data) {
var response = data.target.responseText.substring(4, 17)
// I can't figure out how to take this response and 'return' it.
},
getContent: function() {
url.httpRequest(url.compileData)
}
}
var content = url.getContent() // I want 'content' to be equal to "jQuery v3.3.1"
</script>
But, I can't figure out how to 'return' the response.
Yes, I know that there are other questions out there like this one, namely: How do I return the response from an asynchronous call? But, I'm new to JavaScript and have no idea how to integrate what they're saying there into my case.
A few options for applying the guidance given in "How do I return the response from an asynchronous call?"
If you'd like to continue using callbacks, then it's a matter of adding another. The result you'll want is to allow your entry point to accept its own:
url.getContent(function (content) {
// proceed with `content` here
});
So, getContent needs to expect the argument:
getContent: function(callback) {
// ...
}
But, it also needs to call the callback, which it can do with yet another intermediate function, so it can combine callback with compileData:
getContent: function(callback) {
url.httpRequest(function (data) {
callback(url.compileData(data));
});
}
You can also use Promises throughout the url object.
Starting with httpRequest, you can utilize jQuery.ajax() and its shortcuts returning a customized promise object – a jqXHR:
httpRequest: function () {
return Promise.resolve(
jQuery.get(document.querySelector("a").href)
);
}
In this, Promise.resolve() can be used to convert the customized promise into a standard Promise.
Then, getContent can be modified to interact with the promise, modifying the response with compileData:
getContent: function () {
return url.httpRequest().then(url.compileData);
}
And, the caller can add its own handler using the Promise's methods:
url.getContent().then(function (content) {
// proceed with `content` here
});
Also, a brief aside... In supporting browsers, you can use fetch() in place of jQuery.ajax(), which already returns a standard Promise, but will first give you a Response object rather than the raw data.
httpRequest: function () {
return fetch(document.querySelector("a").href)
.then(response => response.text())
}
There are three different ways according to 3 JS standards i.e es5,es6 and es7 but here you have used xhr therefore it is an old standard.
Hence, you should use xhr.onload method of xhr object over here to return the response. Simply do:
xhr.onload = callback;
insert this code between xhr.open and xhr.send.
You need Promises, Promises and more Promises! And you should return one from your function.
function httpRequest() {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onload = (response) => {
resolve(response); // You should check the response and maybe preprocess it
// so that not everything is passed further
}
xhr.open("GET", document.querySelector("a").href);
xhr.send("");
});
}
and then you may use then and catch
httpRequest().then((data) => {
// Yay! Got response from the server!
}).catch((error) => {
// Yay! More errors!
});

How do I stop a $interval function to execute if the server has not responded yet?

Let me clarify the question:
I have a function in my controller that uses a service to send an HTTP GET request to see if the status is 'done'. This function runs every 5 secs. However, this would also run in every 5 seconds even if there is no response yet, without killing the previous request.
How do I ensure that if there is no response, in 5 secs, that the previous request is killed? Or is there a better solution to this?
Here's my $interval function:
self.$interval(function setInterval() {
EngineService.getEngines().then(
function getEngines(response) {
self.enginesListFromDatabase = response;
for (const item of self.enginesListFromDatabase) {
if (item.status === 'done' && self.doneArray.indexOf(item.id) === -1) {
self.doneArray.push(item.id);
}
}
}
);
}, self.interval);
The service has this http call:
getEngines() {
return this.$http({
method: 'GET',
url: '/api/engines',
}).then(
function getResponse(res){
return res.data;
}
);
}
I tried looking into the docs, but couldn't make sense of how to use $interval properly for this case, or $q.
You can create a $promise and save it to EngineService for canceling requests.
EngineService.canceler = $q.defer(); // $q is a service u need to inject to EngineService. this is just example that u should assign it to the service
getEngines() {
return this.$http({
method: 'GET',
url: '/api/engines',
timeout:EngineService.canceler
}).then(
function getResponse(res){
return res.data;
}
);
}
and cancel the last request:
self.$interval(function setInterval() {
EngineService.canceler.resolve();
EngineService.getEngines().then(
function getEngines(response) {
self.enginesListFromDatabase = response;
for (const item of self.enginesListFromDatabase) {
if (item.status === 'done' && self.doneArray.indexOf(item.id) === -1) {
self.doneArray.push(item.id);
}
}
}
);
}, self.interval);
Regarding your cancelling the request approach - I think it would be much easier to have a variable in your controller (or service) that holds the done/undone status. That way, in each function in the then(..) section, you can ask:
if (!self.done) {
...
}
And then get on in case that's relevant. Otherwise, all requests' responses would fail at that if and nothing would happen, nothing gets overridden.
To cancel future requests -
You can make use of $interval.cancel method and cancel your interval when you're getting your response.
var intervalFunc = $interval(function () {
...
if (!self.done && gotMyWay) {
$interval.cancel(intervalFunc);
}
}, self.interval);
Fiddle demonstrating the cancel mechanism.

Javascript esriRequest (dojo) in a function async issue

I am facing the following synchronization issue. I wouldn't be surprised if it has a simple solution/workaround. The BuildMenu() function is called from another block of code and it calls the CreateMenuData() which makes a request to a service which return some data. The problem is that since it is an async call to the service when the data variable is being used it is undefined. I have provided the js log that also shows my point.
BuildMenu: function () {
console.log("before call");
var data=this.CreateMenuData();
console.log("after call");
//Doing more stuff with data that fail.
}
CreateMenuData: function () {
console.log("func starts");
data = [];
dojo.forEach(config.layerlist, function (collection, colindex) {
var layersRequest = esriRequest({
url: collection.url,
handleAs: "json",
});
layersRequest.then(
function (response) {
dojo.forEach(response.records, function (value, key) {
console.log(key);
data.push(key);
});
}, function (error) {
});
});
console.log("func ends");
return data;
}
Console log writes:
before call
func starts
func ends
after call
0
1
2
3
4
FYI: using anything "dojo." is deprecated. Make sure you are pulling all the modules you need in "require".
Ken has pointed you the right direction, go through the link and get familiarized with the asynchronous requests.
However, I'd like to point out that you are not handling only one async request, but potentionally there might be more of them of which you are trying to fill the "data" with. To make sure you handle the results only when all of the requests are finished, you should use "dojo/promise/all".
CreateMenuData: function (callback) {
console.log("func starts");
requests = [];
data = [];
var scope = this;
require(["dojo/_base/lang", "dojo/base/array", "dojo/promise/all"], function(lang, array, all){
array.forEach(config.layerlist, function (collection, colindex) {
var promise = esriRequest({
url: collection.url,
handleAs: "json",
});
requests.push(promise);
});
// Now use the dojo/promise/all object
all(requests).then(function(responses){
// Check for all the responses and add whatever you need to the data object.
...
// once it's all done, apply the callback. watch the scope!
if (typeof callback == "function")
callback.apply(scope, data);
});
});
}
so now you have that method ready, call it
BuildMenu: function () {
console.log("before call");
var dataCallback = function(data){
// do whatever you need to do with the data or call other function that handles them.
}
this.CreateMenuData(dataCallback);
}

Loading configuration async in JavaScript, how best to wait for response?

This is what I am trying to do:
Request a JSON object from remote server script
WAIT until the JavaScripts gets all the response data
print a value from the response object
I'm trying to use setTimeout in the get config function to recall itself after 1 second if the value is undefined, when it's no longer undefined do something with the value.
Alternatively it seems I could just create some method that loops for some number of seconds, but i'd like to avoid that if there is a better way to accomplish what i'm trying to do? My current code just seems to recurse without any delay which breaks my runtime
Thanks for any ideas, heres the code:
function runApplication() {
var initialiser = new Initialiser();
var config = new Config();
initialiser.fetchConfigurations(config);
config.alertValue('setting1');
}
function Initialiser() {
debug.log("Started");
}
Initialiser.prototype.fetchConfigurations = function(config) {
var req = new XMLHttpRequest();
var url = CONFIGURATION_SERVER_URL;
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == 200) {
var configObject = eval('(' + req.responseText + ')');
config.setConfig(configObject);
} else {
debug.log("Downloading config data...please wait...");
}
}
req.open("GET", url, true);
req.send(null);
}
function Config() {
this.config
}
Config.prototype.setConfig = function(configObject) {
this.config = configObject;
}
Config.prototype.getValue = function(setting) {
if(this.config === undefined) {
setTimeout(this.getValue(setting), 1000);
} else {
return this.config[setting];
}
}
Config.prototype.alertValue = function(setting) {
if(this.config === undefined) {
setTimeout(this.alertValue(setting), 1000);
} else {
alert(this.config[setting]);
}
}
From what I'm looking at, you should just add an extra step to .setConfig, which handles the next piece.
Config.prototype.setConfig = function (data) {
this.config = data;
this.doSomethingAfterTheDataExists();
};
There are lots of ways to do this...
...writing a moderator/observer/pub-sub implementation is dirt-simple, and could be done in relatively few lines for the benefit given...
Writing a Promise system would be even more powerful, but would require more work (because it's like the above implementations, with an added layer of abstraction, to make a really clean and straightforward interface for async).
The other alternative to having .setConfig publish a message, or fire another method, would be to call another method, after .setConfig in the AJAX call.
xhr.onreadystatechange = function () {
// .......
config.setConfig(/* ... */);
config.doSomethingAfterTheDataExists();
};
If you're making multiple AJAX calls, with multiple assignments to multiple properties, and you want to wait until every single call is done, then you really should look into implementing a Promise system.
It will save your sanity and would be well-worth the ~60-100 lines which you could minify into oblivion.
If you're just waiting for that one response to come back, then keep in mind, what happens after the onreadystatechange says that it's finished and the data's there (your success check) is synchronous.
The waiting is the async part.
Once it's delivered, everything (including the stuff inside of that check), goes back to being procedural.
Just use jQuery and getJSON with success/done callback.
You could use a synchronous call instead of asynchronous call for XMLHttpRequest. But this may not be the best option if your your users need to interact with the web page while you wait for a response.
...
req.open("GET", url, false);
request.send(null);

Categories

Resources