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 }); }