Yeah, this is a tricky problem, having to interrupt PreSaveAction
to do some sort of async validation before you really know what you want to return from PreSaveAction
.
How I have handled it in the past is to basically
- have a flag set to
false
to indicate whether I made my check
- when
PreSaveAction
fires, check the flag, if it's set one way, start the validation, but return false
from PreSaveAction
to prevent the item from saving
- do the validation
- set the flag to indicate the validation is done
- re-trigger
PreSaveAction
by simulating another click on Save from code
Some example code so you get the idea:
// declare some variables outside the scope of any function
// so you can access them from anywhere
var madeValidationCheck = false;
var duplicateFound = false;
$(document).ready(function() {
// whatever you have in here
});
function PreSaveAction() {
// once PreSaveAction fires, see if you have done your validation
if (!madeValidationCheck) {
// the validation check hasn't happened yet, so we need to start that off.
// since it is async and might take some time, i like to show the user that something is happening
// so they don't get antsy
$('input[id$="SaveItem"]').before('<img class="waitImage" src="' + _spPageContextInfo.webServerRelativeUrl + '/_layouts/15/1033/images/progress16.gif" />');
// now we start our function that has the async code in it
DoAsyncValidation();
// now, since we are waiting for the async validation
// to finish, we _don't_ want the form to save yet so:
return false;
} else {
// our madeValidationCheck is true, so we must have our validation
// result by now, so clear the visual cue for waiting,
// and return the _actual_ result we want from the validation,
// which could eb either true or false
$('img.waitImage').detach();
// you could optionally show your alert here
if (duplicateFound) {
alert('duplicate found!')
}
return !duplicateFound;
}
}
function DoAsyncValidation() {
// get the values that you want to check
var name = $('input[title="Name"]').val();
var training = $('input[title="Training"]').val();
// you don't need to iterate over anything. you can find out if
// any items in your list have this value combination by using
// a REST query and filtering for items that have both of these values
var itemRequest = "https://your-server/sites/your-site/_api/web/lists/getbytitle('Your List')/items?$filter=Name eq '" + name + "' and Training eq '" + training +"'";
$.ajax({
url: itemRequest,
method: 'GET',
headers: {
accept: 'application/json;odatat=verbose'
}
}).done(function(data) {
// if you get _any_ results, you found a match
// for that value pair, so all you care is if
// you got more than zero results
if (data.d.results.length > 0) {
duplicateFound = true;
// or you could optionally alert your error message here
alert('duplicate found!')
} else {
duplicateFound = false;
}
// ok, now we have our validation results so
// set the flag that you check in PreSaveAction
madeValidationCheck = true;
// and then trigger PreSaveAction again by "clicking" the save button
$('input[id$="SaveItem"]').click();
}).fail(function(jqXhr, status, error) {
// not sure what you want to do if the REST request failed.
// you could force the form not to save by pretending that the validation failed
duplicateFound = true;
madeValidationCheck = true;
alert('sorry there was an error')
$('input[id$="SaveItem"]').click();
// OR
//
// you could have another flag variable for this case
// which you could set here and check back in PreSaveAction
// to know not to show the "duplicate found" message
// but instead show a different error
// but still return false from PreSaveAction again
// to make sure the item is not saved
// many ways to handle this, i can't really tell you what to do here
});
}