This post is similar to previous one related to showing/hiding of the button based on the result of async operation but in this scenario script allows/disallows form to be saved. In my case I check if there is any account with the same “Account Number” available already and if there is script blocks the save. Yes, I know that it’s safer to allow platform to that task for me (for example using Alternate Key feature or so) but it’s just an example that demonstrates the approach could be used in similar use cases.
So here is the code with detailed comments what is happening:
var AccountForm = (function () { var SaveMode = { Save: 1, SaveAndClose: 2, SaveAndNew: 59, Autosave: 70 }; //this is variable that shows if validation was successfully passed or not var isValidationNeeded = true; function OnSave(executionContext) { //so if there are several save handlers and one of previous already called preventDefault //there is no need to do any validations anymore if (executionContext.getEventArgs().isDefaultPrevented()) { return; } //getting save mode from event var saveMode = executionContext.getEventArgs().getSaveMode(); //if savemode is not one of listed - just quit the execution and let the record to be saved if (saveMode !== SaveMode.Save && saveMode !== SaveMode.SaveAndClose && saveMode !== SaveMode.SaveAndNew && saveMode !== SaveMode.Autosave) { return; } //so if validation was successfully passed - flag is reset //and code just leaves the form alone and allows changes to be saved if (!isValidationNeeded) { isValidationNeeded = true; return; } //getting of the form context from execution context object var formContext = executionContext.getFormContext(); //getting of "Account Number" value from field var accountNumber = formContext.getAttribute("accountnumber").getValue(); //if field is blank there is no need to do any checks - code just leaves form if (accountNumber == null) { return; } //preventing of the save operation before async operation is started executionContext.getEventArgs().preventDefault(); //initial composing of query - account number is equal to value from form var query = "$select=accountid&$filter=accountnumber eq '" + accountNumber + "'"; //if form is "Update" if (formContext.ui.getFormType() === 2) { //then current record should be ignored query += " and accountid ne '" + formContext.data.entity.getId() + "'"; } Xrm.WebApi.retrieveMultipleRecords("account", query).then( function success(results) { //so if there are other records with the same account number if (results.entities.length !== 0) { //this message is shown to user only when user caused save, autosave is just blocked if (saveMode !== SaveMode.Autosave) { Xrm.Navigation.openAlertDialog({ text: "Account with this Account Number already exists" }); } } else { //othervice validation flag is set to "Passed" isValidationNeeded = false; //and save event is called again if (saveMode === SaveMode.Save || saveMode === SaveMode.Autosave) { formContext.data.entity.save(); } else if (saveMode === SaveMode.SaveAndClose) { formContext.data.entity.save("saveandclose"); } else { formContext.data.entity.save("saveandnew"); } } }, function (error) { //if something went wrong - error message is shown to user Xrm.Navigation.openAlertDialog({ text: error.message }); } ); } return { OnSave: OnSave }; })();
Don’t forget to set “Pass execution context as first parameter” checkbox during the registration of handler:
If you attach onsave handlers on-the-fly inside onload handler using addOnSave method executionContext is passed inside automatically as a first parameter.
PS I appreciate Chris Groh for code review and suggestions related to:
- different save modes
- sequential execution of several save handlers
Great article Andrew! And the previous one also.
In order to do same thing, I used Promise. With your code, it seems easier.
Thanks for sharing
Hi Andrew,
Thanks for your article.
We’ve used the same approach for some time.
But if you need ot save something on the form calling
ctx.getFormContext().data.save().then(() => {
then -> will always get a canceled callback.
of course, there’s a workaroud but it should be kept in mind.
Sergii,
You are welcome. There always will be a scenario that is not covered by the code provided. That’s why we are paid – to close those gaps.
Thanks,
Andrew
Neha,
I’m not sure why it doesn’t work for you. I did my development in online environment and it worked fine for me.
Andrew
Hi, should this script be attached to the OnSave event of the form? or is it better to attach it to the OnLoad event? Thanks
Hello,
Yes, it should be attached to OnSave handler. I have the screenshot in the post that shows on how to register the handler – https://i2.wp.com/butenko.pro/wp-content/uploads/2018/11/SaveHandler.png
Thanks,
Andrew
Thank you Andrew. I was close to the same solution but after reading your code I figured out a few critical aspects.
Excellent article!
Thanks, Octavian. I’m glad it helped you.
Microsoft wrote that functions formContext.data.entity.save() will be deprecated . I applied new one as suggested
formContext.data.save();
formContext.data.save({ saveMode: 2}).
And it is working until latest Microsft update Dec 17 2021 . After this Microsoft Christmas gift(update) when the user press button Save and Close, then the data on my form could be saved, but the form won’t close, Any help or comments for this case will be useful
Hello Mikhail,
I’m not sure why it doesn’t work when it should according to the documentation.
I would recommend getting in touch with Microsoft and ask what’s wrong and as a workaround in a callback of the promise that is returned calling formContext.data.save – call https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/formcontext-ui/close in order to close the form after it was saved. I understand that it’s a workaround but it should fix the flow for your users.
Andrew
Hi Andrew,
We contacted with Microsoft support regarding this issue , and they confirmed that some “enhancements” were done , and MS product team will be notified about this. I hope, the documentation will be updated as well. So far we applied to fix as
formContext.data.save({ saveMode: SaveMode.SaveAndClose })
.then(successCallback, errorCallback);
and
function successCallback() {
formContext.ui.close();
};
It is working fine.
Mikhail,
Great! Merry Christmas and Happy New Year!
Andrew