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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
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