I have an array of validation checks and one happens to be using ajax to check for an address. I needed the entire array to return true and fire sequentially before submitting a form. I tried using promises but to no avail.
Here's the issue. If I either enter an incorrect address or leave the input(s) blank then it doesn't submit the form (good). But when I actually enter a valid address the form submits despite the fact that my other validations in the validations array have false values. What am I doing wrong?
var validations = [validateInputPresence, validatePassword, validateAddress];
$continueButton.on('click', function() {
toggleSpinner();
subscribe().then(function() {
submitForm(); // This is firing even when some values are false in the array
}, function() {
toggleSpinner();
});
});
function subscribe() {
var promises = validations.map(function(validation) {
return validation();
});
return Promise.all(promises);
}
function validatePassword() {
var password = $password.val();
var format = /^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])[^+&\n]{8,}$/;
return validateInput(format.test(password));
}
function validateAddress() {
return new Promise(function (resolve, reject) {
$.ajax({
type: 'POST',
url: '/address/validate',
data: $form.serialize(),
dataType: 'json',
success: function(response) {
var hasValidAddres = response.Data === 200;
validateInput(hasValidAddres);
hasValidAddres ? resolve() : reject();
},
error: function() {
toggleSpinner();
}
});
});
}
function validateInput(validation) {
if (validation) {
return true;
} else {
return false;
}
}
The main problem is that you don't return the Promise in the validateInput() function. You can't reject a promise returned by Promise.all via returning a false in one of its function.
Read more about Promise.all, the quote below is taken from MDN
If something passed in the iterable array is not a promise, it's converted to one by Promise.resolve.
So actually anything, but Promise will be treated as being resolved.
What you should do is write the validateInput function to return a promise.
function validateInput(validation) {
return new Promise(function(resolve, reject) {
if (validation) {
resolve();
} else {
reject();
}
});
}
What am I doing wrong?
You've never tested whether the values in the array were true or false. You did most of the promise stuff correctly, and subscribe() returns a promise for an array of boolean values. If you want to test whether all of them are true, you will need to do that explicitly:
subscribe.then(function(results) {
if (results.every(Boolean)) // all are true
submitForm();
else // some are false
…;
}, function(err) {
// some validations threw an exception
});
Related
This is the current code I have
function getInput() {
Swal.fire({
input: "text",
}).then((result) => {
if (result) {
return(result.value);
});
}
function main() {
if (condition) {
const test = getInput();
} else {
// do something else
}
}
I know this isn't correct because it returns undefined. I've tried looking for a bunch of solutions but I always get undefined or the promise itself returned, have no idea how I'm supposed to get the value.
What I want to do is somehow return the value of the SweetAlert2 input so that I can use it in other functions.
Edit
I think I got mostly what I had wanted to do. Instead of putting the then handler in getInput(), I put it on test, so then I could put the rest of my code in the handler.
function getInput() {
return Swal.fire({
input: "text",
})
}
function main() {
if (condition) {
const test = getInput();
test.then((result) => {
if (result) {
// do stuff
}
});
} else {
// do something else
}
}
Ideal use case to use a callback as your program is not certain when the value will be returned.
In then handler, callback function will be invoked and input will be passed as an argument.
function getInput(cb) {
Swal.fire({
input: "text",
}).then((result) => {
if (result) {
return cb(result.value);
});
}
}
function main(input) {
console.log(input);
}
getInput(main)
I have a chained Ajax requests in my function.
Function A and B are Ajax requests. A will run first and B will run after A returns its data.
My problem is that Function C is the one that executes Function B. When Function C is executed, a button that executes C will be disabled temporarily until A && B is finished.
How do I return a value on function c below?
I need to return true on the function c AFTER function a & b requests completed returning data.
I'm thinking if I return true on the callback function inside done(), fail() or always(), the value will return to the callback function and not to the c().
function a(methodA) {
return $.ajax({
type: "get",
url: "api/a",
cache:false,
data: { method: methodA },
dataType: "json",
});
}
function b(methodB) {
return $.ajax({
type: "get",
url: "api/b",
cache:false,
data: { method: methodB },
dataType: "json",
});
}
function c(data) {
a(data)
.done(function(data2) {
b(data2)
.done(function(data3) {
})
.fail(function(data3) {
})
.always(function(data3) {
})
})
.fail(function(data3) {
})
}
$(document).ready(function() {
$('#btnC').click(function(event) {
$('#btnC').prop('disabled', true);
var c = c(getData);
if(c == true) {
$('#btnC').prop('disabled', false);
}
});
});
jQuery Ajax method returns a promise object. (To be exact, jqXHR)
Usually, it uses like this.
var result =
$.post("url").done(function() {
// after ajax.
});
// called when ajax process done finished.
result.promise().done(function() {
// blah blah blah
});
However, when it comes to a nested ajax, you need to more work than usual.
Use then() and return inner ajax.
function callAjaxs() {
return $.get("ajaxA").then(function(res) {
return $.get("ajaxB", res).done(function() {
// blah blah blah
});
});
}
// call function and use promise. (of course, you can also use fail, always)
callAjaxs().promise().done(function() {
// $('#btnC').prop('disabled', false);
});
If you don't care about details, I think this way is a good option.
If you want the return value of c to be dependent on asynchronous callbacks you can't just return a value. You have to return a promise because the value you want to return is timed based on the asynchronous requests made by the 2 ajax calls (not available when return is called). Therefor you can promise the return of a value but cannot give the value right away.
function a() {
// mock request since content doesn't matter
return $.get('https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js');
}
function b(fail) {
// google does not allow cross origin requests
if (fail) return $.get('https://google.com');
return a();
}
function c(failB) {
let requests = [
Promise.resolve( a() ),
Promise.resolve( b(failB) ),
];
return Promise
.all(requests)
.then(results => Promise.resolve(true))
.catch(error => Promise.reject('one or multiple requests failed'));
}
c().then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
c(true).then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Returning just the value true might be a bit useless, since if the promise is resolved you know everything succeeded. You could just simply leave out that part and the results will be passed on as result of the c promise. The same goes for the error message. In the above code I don't use the error itself but instead generate my own string. If you are more interested in the failing object you could leave out this catch in c. See the example below.
function a() {
// mock request since content doesn't matter
return $.get('https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js');
}
function b(fail) {
// google does not allow cross origin requests
if (fail) return $.get('https://google.com');
return a();
}
function c(failB) {
let requests = [
Promise.resolve( a() ),
Promise.resolve( b(failB) ),
];
return Promise.all(requests);
}
c().then(results => {
// in this case has 2 the same values since the same url is requested
results.forEach(resp => console.log(resp.substring(0, 15)));
}).catch(jqXhr => {
console.log(jqXhr.statusText);
});
c(true).then(results => {
results.forEach(resp => console.log(resp.substring(0, 15)));
}).catch(jqXhr => {
console.log(jqXhr.statusText);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I am using Angular resourse to get my data from an API, in this way:
var getAccountListPerUser = function () {
return $resource(uri, {}, {
get: {
headers: service.getDefaultHeaderRequest(),
method: 'GET',
transformResponse: function (data) {
var accountList = [];
try {
accountList = JSON.parse(data);
} catch (e) {
accountList = [];
}
return accountList;
},
isArray: true,
cache: true
}
}).get().$promise;
};
In my controller I have to use it and another two service functions defined in the same way.
var promiseResourcesAccountList = usrWebUserService.getAccountListPerUser();
promiseResourcesAccountList.then(function(result){
$scope.usersWithAccountsAndProfiles = result;
var filteredProfiles = [];
for (var account in result) {
...
}
$scope.filteredProfiles = filteredProfiles;
});
And:
var promiseResourcesEditUser = usrWebUserService.getResourcesUser(currentUser);
promiseResourcesEditUser.then(function (result) {
usrWebUserFactory.mapBasicPreferences($scope, result);
});
And then another very similar, this information loads data in three divs, but I want to show them only when all the three functions have completed correctly. I think I have to chain the result of the promises. How can I do that?
You can chain them like:
promiseResourcesAccountList.then(function(result){
///whatever processing
//return a promise
return promiseResourcesEditUser()
}).then(function(){
return anotherPromise();
}).then(function(){
//update scope here
});
alternatively, you could also use $q.all([promise1, promise2, promise3]).then(...);
#terpinmd is correct. Chaining promises is pretty simple. Say you have a service with a "getWidgets" that returns a promise, and you want to use the response from that service to call another service, "getWidgetOwners" that will return another promise :
Assumptions
getWidgets returns an array of widget objects.
getWidgetOwners accepts an array of ownerIds
How To:
service.getWidgets()
.then(function(widgets) {
return widgets.map(function(widget) { // extract ownerIds
return widget.ownerId;
});
})
.then(service.getWidgetOwners) // pass array of ownerId's to
.then(function(owners) { // the next service
console.log(owners);
});
I need to make submitAdapterAuthentication() function to work the first getUserRoles() function, but with the current implementation of the getUserRoles() function is being executed first that submitAdapterAuthentication(). How can I fix this?
checkOnline().then(function(onl) {
userObj.isLoginOnline = onl;
}).then(function() {
submitAdapterAuthentication(user, pass);
}).then(function() {
getUserRoles();
});
function submitAdapterAuthentication(user, pass) {
var invocationData = {
parameters : [ user, pass ],
adapter : "adapterAuth",
procedure : "submitLogin"
};
ch.submitAdapterAuthentication(invocationData, {
onFailure : function(error) {
WL.Logger.log("ERROR ON FAIL: ", error);
},
onSuccess : function() {
WL.Client.updateUserInfo({
onSuccess : function() {
//return promise
WL.Client.updateUserInfo({
onSuccess : function() {
}
});
}
});
}
});
}
// my function to obtain roles
// It should be performed after submitAdapterAuthentication
function getUserRoles(){
var arrayRoles = [];
var attributes = WL.Client.getUserInfo(realm, "attributes");
if(attributes){
if(attributes.roles){
arrayRoles.push(attributes.roles);
}
}
}
When chaining promises, if you return anything but another promise from a then() callback, the resulting promise will be resolved immediately with the value undefined.
In order to make sure your callbacks are executed in the order you specified, just make sure each callback is returning a promise at the end. If you want to return some value from a callback, wrap it in a $q.when(). In this case it looks like you are not using any intermediary return values, so you can just wrap any arbitrary value in a $q.when() to make sure a promise is returned:
checkOnline().then(function(onl) {
userObj.isLoginOnline = onl;
return $q.when(true);
}).then(function() {
submitAdapterAuthentication(user, pass);
return $q.when(true);
}).then(function() {getUserRoles();});
Based on your latest edit, it looks like ch.submitAdapterAuthentication() is likely returning a promise. If this is the case, you should return this promise from the function:
return ch.submitAdapterAuthentication(invocationData, {...
And then return this promise in the then callback:
then(function() {return submitAdapterAuthentication(user, pass);})
If ch.submitAdapterAuthentication() does not return a $q promise, you will have to wrap it yourself:
var deferred = $q.defer();
ch.submitAdapterAuthentication(invocationData, {
onFailure : function(error) {
WL.Logger.log("ERROR ON FAIL: ", error);
deferred.reject(error);
},
onSuccess : function() {
WL.Client.updateUserInfo({
onSuccess : function() {
deferred.resolve();
}
});
}
});
return deferred.promise;
I would like to perform the same action after a promise has either been fulfilled with a success result or failure, ie I want to perform the same action for the success and error handler and then continue to send down the result of the promise to the appropriate erroe/success handlers.
var pleaseWaitPromise = playAudioAsync("please wait");
myLongRunningPromise().then(function tempSuccessHandler(result) {
pleaseWaitPromise.cancel();
return result;
}, function tempErrorHandler(error) {
pleaseWaitPromise.cancel();
return WinJS.Promise.wrapError(error);
}).done(function realSuccessHandler(result) {
console.info(result);
}, function realError(error) {
console.error(error);
});
Is there a more elegant way to stop the pleaseWaitPromise, which could also be a function call instead of a promise (like clearInterval)
jfriend is right you'd typically want finally here - it does exactly what your code above does. Sadly WinJS promises do not feature .finally at the moment so unless you'd like to shim it (patching it on the prototype of WinJS.promise) you're stuck with it.
You can also put it as a function:
function always(prom, fn){
return prom.then(function(v){ fn(v); return v; },
function(e){ fn(e); return WinJS.Promise.wrapError(error); });
}
Which would look like:
always(myLongRunningPromise(),
pleaseWaitPromise.cancel();
})).done(function realSuccessHandler(result) {
console.info(result);
}, function realError(error) {
console.error(error);
});
Sorry, but I don't understand the extra step, wouldn't this just do what you want?
var pleaseWaitPromise = playAudioAsync("please wait");
myLongRunningPromise().then(function tempSuccessHandler(result) {
pleaseWaitPromise.cancel();
console.info(result);
}, function tempErrorHandler(error) {
pleaseWaitPromise.cancel();
console.error(error);
});
edit: second try
I know it is a known anti-pattern, but what if you return a Promise that never fails? Something like:
function neverFails(myLongRunningPromise, pleaseWaitPromise){
return new WinJS.Promise(function (complete){
myLongRunningPromise().then(function () {
pleaseWaitPromise.cancel();
console.info(result);
return complete();
}, function (error) {
pleaseWaitPromise.cancel();
console.error(error);
return complete();
});
});
}
Does that make sense?