Async onSave preventDefault with external call
One of our current projects is the migration of an onPrem Dynamics 365 to the Cloud. Part of that is refactoring existing JavaScript implementation. The current implementation had an onSave function which prevented the save process based on a call to the WebAPI. It took me some time to migrate the function, therefore I thought it might be interesting for others to know how to write an async onSave preventDefault with external call.
Background
All calls to the WebAPI (through Xrm.WebApi) are done async in the Power Platform. This means if you need to get additional data to determine whether you would like to cancel the current save event you would need an async onSave.
I tried the usual approach of calling “preventDefault“, which did not give the expected result as the save was executed either way. According to the Microsoft documentation you should in that case use preventDefaultOnError instead of preventDefault.
Cancels the save operation if the event handler has a script error, returns a rejected promise for an async event handler or the operation times out.
MS Learn
This means the preventDefaultOnError function expects a promise which should either result in success or be rejected. Doing an “await” on the call to the Xrm.WebApi will not cancel the save event.
Configure Model-Driven App
Before we go to deep into the implementation and the JavaScript code we have to configure our Model-Driven App to use aync onSave events.
To activate the async onSave handler feature you have to edit the Model-Driven App, go to settings, Features and activate the “Async save handlers” feature.
Implementation
Let’s take a look at the implementation. Here is a simple onSave function which prevents saving when there is no account with the name “Benedikt”(both in TypeScript and JavaScript).
async function onSave(eventContext: Xrm.Events.EventContext): Promise<void> { (eventContext as Xrm.Events.SaveEventContext).getEventArgs().preventDefaultOnError(); return new Promise(async (resolve, reject) => { try { await Xrm.WebApi.retrieveMultipleRecords("account", "?$select=name&$filter=name eq 'Benedikt'"); resolve(); } catch (error) { console.log(error); reject(error); } }); }
async function onSave(eventContext) { eventContext.getEventArgs().preventDefaultOnError(); return new Promise(async (resolve, reject) => { try { await Xrm.WebApi.retrieveMultipleRecords("account", "?$select=name&$filter=name eq 'Benedikt'"); resolve(); } catch (error) { console.log(error); reject(error); } }); }
First of all, we do execute the preventDefaultOnError function. As I mentioned this function expects a promise to be rejected (or timed out) to prevent the save.
That is the reason why we return a promise where in we make our request to the WebApi (or any other service you’d like to). The promise get’s resolved when there is more than one result. In any other case (no result or error) it gets rejected and therefore the save of the form is cancelled.
Conclusion
I hope you learned how to create an async onSave preventDefault with external call. When you understand what the function preventDefaultOnErrror expects, it is not very hard to satisfy it.
Please let me know if you have any questions or feedback.
You can also subscribe and get new blog posts emailed to you directly.
I tried this a while ago.
I had problems with eventContext.getEventArgs().preventDefaultOnError();
annoying users with Script Error alert notifying about save beeing canceled from script.
Is this fixed now?
I haven’t experienced this so far. Do you have screenshots and the script where it happened?