Close modal in ajaxStop - javascript

I have the following code:
var menuButtonClick = {
onReady: function () {
$(document).on('click', '.menu-button', function () {
menuButtonClick.clickedButton($(this).html());
});
},
clickedButton: function (val) {
switch (val) {
case 'CheckModelBank':
modelBankHandler.get();
break;
}
}
}
var modelBankHandler = (function () {
var get = function () {
var selectedCellData = handsonTable.selectedCellData.get();
var webGrid = handsonTable.WebGrid.get();
$.ajax({
type: 'POST',
url: "http://localhost:56292/api/Data/CheckModelBank",
data: { "": selectedCellData },
success: function (response) {
if (response != null) {
serverResult = JSON.parse(response);
printModelBank(serverResult, webGrid);
}
},
error: function (jqXHR, textStatus, errorThrown) {
if (textStatus == "error") {
modalHandler.printErrorModal();
}
}
});
}
var printModelBank = function (serverResult, webGrid) {
///
}
return {
get: get
}
})();
var fileHandler = {
onReady: function () {
var documentType = "";
$('.upload-file').click(function () {
$('[data-remodal-id=documentModal]').remodal().open();
});
$('.document-option').click(function () {
//Need to get the type of document the user is going to work with so we can parse the document correctly to the webgrid
documentType = $(this).html();
$('#fileUpload').click();
});
$('#fileUpload').change(function () {
fileHandler.upload(documentType);
});
$('.save-to-excell').click(fileHandler.saveDocument);
},
upload: function (documentType) {
var formData = new FormData();
var totalFiles = document.getElementById("fileUpload").files.length;
for (var i = 0; i < totalFiles; i++) {
var file = document.getElementById("fileUpload").files[i];
formData.append("fileUpload", file);
}
$.ajax({
type: 'post',
url: 'http://localhost:59973/Home/Upload',
data: formData,
dataType: 'json',
contentType: false,
processData: false,
success: function (response) {
jsonData = JSON.parse(response.data);
if (jsonData != null) {
if (documentType == "Infolog") {
fileHandler.printDocument(jsonData); //This is used for pickinglist and infolog
} else {
var webGrid = handsonTable.WebGrid.get();
webGrid.loadData(jsonData);
}
}
},
error: function (error) {
if (textStatus == "error") {
modalHandler.printErrorModal();
}
}
});
},
}
$(document).ready(function () {
handsonTable.init();
menuButtonClick.onReady();
fileHandler.onReady();
buttonClicks.onReady();
}).ajaxStart(function () {
$('[data-remodal-id=modalAjax]').remodal().open();
}).ajaxStop(function () {
$('[data-remodal-id=modalAjax]').remodal().close();
});
When I upload a file (fileHandler), the modal shows during ajaxStart and closes on ajaxStop. However, If I click on a button in my menu (menuButtonclick) which trigger my modelBankHandler function, the modal shows during ajaxstart, but does not close on ajaxStop.
Why? All the data are retrieved as expected in my modelBankHandler, so why does not the modal closes?

If you have pressed F12 in the browser and looked at the console you would probably have found an error there. This video might help you to figure out basic problems yourelf.
I think printModelBank might throw an error, if the success or error functions throw an error then jQuery crashes and does not execute the ajaxStop handler:
$(document)
.ajaxStart(function () {
console.log("open model");
}).ajaxStop(function () {
console.log("close model");
});
$.ajax({
type: 'GET',
url: "/none",
data: {},
success: function (response) {
console.log("success");
throw new Error("now stop won't execute");
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("error");
throw new Error("now stop won't execute");
}
});
You could solve this by having success and error as promise handlers, errors in promise handlers should not crash jQuery (but it does):
$(document)
.ajaxStart(function () {
console.log("open model");
}).ajaxStop(function () {
console.log("close model");
});
$.ajax({
type: 'GET',
url: "/none",
data: {}
})
.then(
response => {
console.log("success");
throw new Error("now stop won't execute");
},
(jqXHR, textStatus, errorThrown) => {
console.log("error");
throw new Error("now stop won't execute");
}
);
You could try native promises (jQuery still doesn't get promises right) and have it not crash on error in handler:
$(document)
.ajaxStart(function () {
console.log("open model");
}).ajaxStop(function () {
console.log("close model");
});
Promise.resolve()
.then(_ =>
$.ajax({
type: 'GET',
url: "/none",
data: {}
})
)
.then(
response => {
console.log("success");
throw new Error("now stop WILL execute");
},
(jqXHR, textStatus, errorThrown) => {
console.log("error");
throw new Error("now stop WILL execute");
}
);
IE does not support native promises so you will need a polyfill or try babel with ES2016

Related

Javascript await is only valid in async functions

I have this function to delete items once a popup returns true:
function deleteItems(selectedItems){
if (selectedItems.length > 0) {
$("#confirm-popup-modal").modal("show");
$("#confirm-popup-modal").one('hidden.bs.modal', function (event) {
if ($("#confirm-modal").val() == "true") {
var form_data = selectedItems;
$.ajax({
url: "#Url.Action("Delete", #ViewContext.RouteData.Values["controller"].ToString())",
method: "POST",
data: JSON.stringify(form_data),
contentType: "application/json",
success: function (result) {
if (result.Result == true) {
var deleteId = result.Output;
await CompletedJobsAccess(deleteId);
table.draw();
}
},
error: function (error) {
console.log(error);
}
});
}
});
}
}
Inside the Ajax success is another function called CompletedJobsAccess that will keep looping every 3 seconds to check if a job deletion has been completed:
function CompletedJobsAccess(DeleteId){
return new Promise((resolve,reject)=>{
var loopInterval = setInterval(function() {
$.ajax({
url: "#Url.Action("Verify", "CompletedJobsAccess", new {area="Base" })",
method: "POST",
data: JSON.stringify(DeleteId),
contentType: "application/json",
success: function(verifyResult) {
if (verifyResult.IS_COMPLETED == true && verifyResult.IS_PROCESSING == false) {
if (verifyResult.IS_SUCCESSFUL == true) {
console.log(verifyResult.OUTPUT);
$.each($.parseJSON(verifyResult.OUTPUT), function(index, value) {
if (value.Result == true) {
toastr.success(value.Message);
}else{
toastr.error(value.Message);
}
});
clearInterval(loopInterval);
} else {
toastr.error(verifyResult.ERROR_MESSAGE);
}
}
},
error: function(innerError) {
console.log(innerError);
}
});
}, 3000);
});
}
However, when I load the page, and call deleteItems(selected);, this is the error I get:
Uncaught SyntaxError: await is only valid in async functions and the
top level bodies of modules
I tried searching around but I can't find if it can work within an ajax success function.
EDIT:
Added async to the ajax success function but the table draw function doesn't run.
function deleteItems(selectedItems){
if (selectedItems.length > 0) {
$("#confirm-popup-modal").modal("show");
$("#confirm-popup-modal").one('hidden.bs.modal', function (event) {
if ($("#confirm-modal").val() == "true") {
var form_data = selectedItems;
$.ajax({
url: "#Url.Action("Delete", #ViewContext.RouteData.Values["controller"].ToString())",
method: "POST",
data: JSON.stringify(form_data),
contentType: "application/json",
success: async function (result) {
if (result.Result == true) {
var deleteId = result.Output;
console.log("table before");
await CompletedJobsAccess(deleteId);
console.log("table draw");
table.draw();
}
table.draw();
},
error: function (error) {
console.log(error);
}
});
}
});
}
}
EDIT 2: Updated CompletedJobsAccess to resolve promises:
function CompletedJobsAccess(DeleteId){
return new Promise((resolve,reject)=>{
var loopInterval = setInterval(function() {
$.ajax({
url: "#Url.Action("Verify", "CompletedJobsAccess", new {area="Base" })",
method: "POST",
data: JSON.stringify(DeleteId),
contentType: "application/json",
success: function(verifyResult) {
if (verifyResult.IS_COMPLETED == true && verifyResult.IS_PROCESSING == false) {
if (verifyResult.IS_SUCCESSFUL == true) {
console.log(verifyResult.OUTPUT);
$.each($.parseJSON(verifyResult.OUTPUT), function(index, value) {
if (value.Result == true) {
toastr.success(value.Message);
}else{
toastr.error(value.Message);
}
});
clearInterval(loopInterval);
return Promise.resolve();
} else {
toastr.error(verifyResult.ERROR_MESSAGE);
return Promise.resolve();
}
}
},
error: function(innerError) {
console.log(innerError);
}
});
}, 3000);
});
}
Just make the success function async
$.ajax({
url: "https://jsonplaceholder.typicode.com/users/3",
method: "GET",
success: async function(data) {
console.log("first - now wait a second ...");
await new Promise((res) => setTimeout(res, 1000));
console.log("second, data:",data);
},
error: function(innerError) {
console.log(innerError);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Working JSFiddle (can't work on this site because of CORS)
In CompletedJobsAccess(DeleteId) you return a promise. But the way you set it up it will never execute the resolve function. So your await will wait forever ...
You could place the line
resolve();
right after
clearInterval(loopInterval);
in your CompletedJobsAccess function to make it work.
Do not return yet another Promise.resolve() like you did in your edited code.
A resolve function for a promise is never returned but executed.
Try Adding async before all the function keyword like async function deleteItems(selectedItems){ and also $("#confirm-popup-modal").one('hidden.bs.modal', async function (event) { and it should do the job.
You're using await in functions that don't use the async keyword. await isn't available in regular functions. To solve this, you can change all the functions using await to async function to make it into an asynchronous function.
And if you don't want want to go through every function to make it asynchronous, you can just put the entire code inside an asynchronous IIFE

Calling sync ready made async ajax javascript function

I want to call this function on button click after login and wait for result, to get token value. This function cannot be changed, it is async and supplied from other currently unavailable team.
I already tried something like this, but with no success. I get web service results, but I can't write appropriate sync call to wait to return token.
function getToken() {
param1 = "123456";
ajax_oauth(param1, function (success, response) {
success: return response.token;
});
}
function ajax_oauth(param1, callback) {
APP.debug("oauth login with param1 " + param1);
try {
APP.blockUI();
var DeviceID = APP.readRegistry(APP_CONFIG.REGISTRY.DeviceID);
//---------------------------------------------------------------
$.ajax(
auth_token_url,
{
method: "GET",
accept: 'application/json',
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify({
'param1': param1,
'deviceId': DeviceID
}),
xhrFields: {
withCredentials: false
},
statusCode: {
201: function (response) {
APP_STATE.hasOauth = true;
APP.debug('got response 200 from oauth');
auth.login(response.token); //TODO read expiration from token
try {
var decoded = jwt_decode(response.token);
APP_STATE.uid = decoded.uid;
} catch (err) {
APP.error("unable to decode token " + JSON.stringify(err));
}
},
401: function () {
},
500: function () {
},
503: function () {
}
},
success: function (response) {
APP.unblockUI();
APP_STATE.restAvailable = true;
},
error: function (jqXHR, textStatus, errorThrown) {
APP.unblockUI();
APP_STATE.restAvailable = false;
APP.restError(auth_token_url, jqXHR, errorThrown, textStatus);
APP.callback(callback, false);
}
}
);
} catch (err) {
APP.error("unable to do oauth login, " + err);
}
};
After user clicks on login button, I want to call function ajax_oauth and to return token if params ok. If not, to return login error. Login can't be async, as far as I can see.
For whatever reason you can't tap into the original ajax response, you could intercept the request using $.ajaxPrefilter.
From your code it looks like auth_token_url has a global reference. You could use this to intercept the call by matching the outgoing request on the resource URL.
$.ajaxPrefilter('json', function(options, originalOptions, jqXHR) {
if (options.url === auth_token_url) {
jqXHR.done(function(response) {
try {
var decoded = jwt_decode(response.token);
console.log(decoded);
} catch (err) {
APP.error("unable to decode token " + JSON.stringify(err));
}
});
}
});
Note that this needs to be declared well before the request is made preferably after jQuery is loaded.

DRY Then and Always in jQuery Promise

I have the following example JS code:
function loadData(url, data) {
return $.get(url, data ? data : {});
}
function example1() {
showSpinner();
$.when(loadData('/example1', { hello: 'world' }))
.then(function (resp) {
// do something with the data
})
.always(function(){
hideSpinner();
})
.fail(function (jqXHR, textStatus, errorThrown) {
handleError(jqXHR);
});
}
function example2() {
showSpinner();
$.when(loadData('/example2', { hello: 'world' }))
.then(function (resp) {
// do something with the data
})
.then(function () {
// do something else
})
.always(function(){
hideSpinner();
})
.fail(function (jqXHR, textStatus, errorThrown) {
handleError(jqXHR);
});
}
Both example1() and example2() use the same promise logic and will do something different with the returned data and then always hide the spinner and handle the failures the same (if any). The issue is that I may want to do different things after the data is loaded in each example and have multiple then's follow.
However I am having to repeat the always and fail code. How can I make them more DRY so they are only written once but used in both scenarios (and other examples if need be). So I can't just move the when into a method and pass a callback as there maybe more than one and in different then`s. So this wouldn't work:
function customPromise(load, callback) {
showSpinner();
$.when(load)
.then(function (resp) {
callback(resp);
})
.always(function(){
hideSpinner();
})
.fail(function (jqXHR, textStatus, errorThrown) {
handleError(jqXHR);
});
}
function example3() {
customPromise(loadData('/example2', { hello: 'world' }));
}
If you have successive and unknown number of callbacks, I suggest you move the whole processing to the created function:
function handleRequest(url, data, errorCallback) {
// create promise
let promise = $.get(url, data ? data : {});
// if callback functions provided as args, chain them in `then()` calls
if (arguments.length > 3) {
let callbacks = Array.slice.call(arguments, 3);
let fn = (results) => (results);
let callback;
for (let i = 0; i < callbacks.length; i++) {
callback = callbacks[i] instanceof Function ? callbacks[i] : fn;
promise.then(callback);
}
}
// handle static behavior
promise.always(() => {
hideSpinner();
})
.fail(errorCallback);
// return the created promise
return promise;
}
You can now use this function as follows:
handleRequest(
'/example',
{},
(jqXHR, textStatus, errorThrown) => {
handleError(jqXHR);
},
(resp) => {
// do something with the data
},
() => {
// do something else
}
);
I could think of something like this. Not sure if loadData should be held responsible for showing the spinner.
$(function() {
example1();
example2();
});
function loadData(url, data) {
showSpinner();
return $.get(url, data ? data : {})
.always(function() {
hideSpinner();
})
.fail(function(jqXHR, textStatus, errorThrown) {
handleError(jqXHR);
});
}
function example1() {
$.when(loadData('https://jsonplaceholder.typicode.com/users', {
hello: 'world'
}))
.then(function(resp) {
console.log(resp.length + ' lengthed data received');
});
}
function example2() {
$.when(loadData('https://jsonplaceholder.typicode.com/posts', {
hello: 'world'
}))
.then(function(resp) {
return resp;
})
.then(function(data) {
console.log(data.length + ' lengthed data received');
});
}
function showSpinner() {
console.log('Showing spinner...');
}
function hideSpinner() {
console.log('Hiding spinner...');
}
function handleError(xhr) {
console.error(xhr);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Perhaps you can factor the .when() out into a function returning a promise itself, then call that and chain onto the returned promise with .then(), so something like foobar().then().then(). There's no law stating that always or fail must come last in the chain.
This is a rough sketch for a solution, I have not tested this.
function loadData(url, data) {
return $.get(url, data ? data : {});
}
function wrapWhen(endpoint, data) {
// return a promise
return $.when(loadData(endpoint, data))
.always(function(){
hideSpinner();
})
.fail(function (jqXHR, textStatus, errorThrown) {
handleError(jqXHR);
});
}
function example1() {
showSpinner();
wrapWhen('/example1', {hello: 'world'})
.then(function (resp) {
// do something with the data
});
}
function example2() {
showSpinner();
wrapWhen('/example2', {hello: 'world'})
.then(function (resp) {
// do something with the data
})
.then(function () {
// do something else
});
}

Translating a rest API call from angular to jQuery

Apologies if worded awkwardly, but I have to make an rest API call using jQuery. I've already made the call using angularJS before, but for this case I can't use that. I tried translating it to jQuery but I'm not getting the same results. Is there anything I'm doing wrong or am I missing information? I'm fairly new to jQuery so I feel as if I'm missing something crucial or misunderstood something.
Working code with angularJS:
var req = {
method: 'POST',
url: 'https://fakeurl.com/rest/v1/portal/user/' + $scope.email.value,
headers:{
'Content-Type': 'application/json',
'Header_1': 'Yes',
'x-access-token': 'glsFromWebsite' //$scope.authInfo.token
}
};
restCall($http, req).then(function (res) {
// check for error even though 200 response
if (res.error) {
console.error("Error reported...");
} else {
` //enter success code here
}
});
var restCall = function(http, req) {
var _url = getBaseUrl() + req.url;
req.url = _url;
return new Promise(function(fulfill, reject) {
try {
http(req).then(function (res) {
// check for error even though 200 response
if (res.data.error) {
if (res.data.error === '601') {
console.error('Token is invalid or has expired');
} else {
console.error("Error from end point: " + res.data.error);
}
}
fulfill(res.data);
}, function(err) {
console.error('Error calling rest endpoint',err);
reject();
});
} catch (ex) {
console.error('Exception calling rest endpoint',ex);
reject(ex);
}
});
};
My failing jQuery code:
var processCreate = function (email) {
$.ajax({
url: 'https://fakeurl.com/rest/v1/portal/user/' + email.value,
type: 'POST',
headers: {
'Content-Type': 'application/json',
'Header_1': 'Yes',
'x-access-token': 'glsFromWebsite' //$scope.authInfo.token
},
success: function (res, a, b) {
if (res === 'NOT FOUND') {
//code that runs when this case is true
} else {
//code that runs when this case is false
}
},
error: function () {
console.error("Error...");
}
});
}
Try making an ajax call like this
var processCreate = function (email) {
var authHeaders = {};
authHeaders.Authorization = 'Bearer ' + 'glsFromWebsite';
$.ajax({
url: 'https://fakeurl.com/rest/v1/portal/user/' + email.value,
type: "POST",
cache: false,
dataType : "json",
contentType: "application/json; charset=utf-8",
headers: authHeaders,
success: function (data) {
//console.log(data);
if (data === 'NOT FOUND') {
//code that runs when this case is true
} else {
//code that runs when this case is false
}
},
error: function (xhr) {
console.log(xhr);
}
});
}

BackboneJS, having trouble with success and error when saving a model

For some reason, I cannot enter my success and error blocks when I am saving my model. Wether my response is successful "201" or error "404", my code will not hit the debugger lines. Does anyone know what could be going wrong?
SignInView.prototype.login = function(event) {
event.preventDefault();
return this.model.save(this.credentials(), {
type: 'POST',
url: 'http://localhost:3001/api/v1/users/sign_in'
}, {
success: (function(_this) {
return function(userSession, response) {
debugger;
return window.location.href = "/";
};
})(this),
error: (function(_this) {
return function(userSession, response) {
debugger;
var message;
message = $.parseJSON(response.responseText).error;
return alert(message);
};
})(this)
});
};
The save function only takes two parameters -- you are passing your success and error functions as a third param. Try the following:
SignInView.prototype.login = function(event) {
event.preventDefault();
return this.model.save(this.credentials(), {
type: 'POST',
url: 'http://localhost:3001/api/v1/users/sign_in',
success: (function(_this) {
return function(userSession, response) {
debugger;
return window.location.href = "/";
};
})(this),
error: (function(_this) {
return function(userSession, response) {
debugger;
var message;
message = $.parseJSON(response.responseText).error;
return alert(message);
};
})(this)
});
};

Categories

Resources