The OnSave event in Model-Driven Forms was one of the events where we couldn’t execute asynchronous code, and that stayed that way for a very long time. The only way we could make some requests before saving a record, was to stop the saving process, make the requests and start the saving again. But that had some other issues, because that would reject any formContext.data.save() attempts, written behind some standard ribbon commands.

A few years ago, we’ve got the “Async OnSave“. This is an option, we can set for each Model-Driven App

Then we can return promises from our OnSave handler

function myHandler(context) {
   return setTimeout(Promise.resolve, 1000);
}

The timeout problem (open dialogs issue)

The OnSave handler is waiting for the Promises returned from the save handler, but there is a limit of 10 seconds. This makes sense, for the most cases we make some requests, but sometimes we need to wait for a user interaction. For instance we might want to let the user confirm, if it’s ok to save or not. The confirm is an asynchronous process, so until now there was no easy way to do that.

Actually there was a documented feature, which would allow us to disable this timeout: disableAsyncTimeout . But it didn’t worked for a long time. For instance here is question from Mehdi El Amri in the Community Forum, which was still unsolved until now.

I don’t know if I’ve missed an announcement, and I don’t know when it started working. Today I’ve heard from a colleague that it works now, so I’ve tried it right away.

Show a confirm dialog in OnSave event

Now we’re able to implement the OnSave handler, where we let first the user decide what to do.

First we call the disableAsyncTimeout. This applies on event arguments, so we need to place it inside the OnSave handler.

If the user says “no”, we can call the “preventDefault” to stop the saving.

We also need to disable the autosave on this form, otherwise the user gets confirm dialogs every 30 seconds.

function SaveAsyncWithDialog(executionContext){
    console.log("entering save");      
    const eventArgs = executionContext.getEventArgs();
    if (eventArgs.getSaveMode() == 70){ //prevent autosave
        eventArgs.preventDefault();
        return;
    }
    eventArgs.disableAsyncTimeout(); //set save timeout
    return Xrm.Navigation.openConfirmDialog({ 
           text:"Would you like to save?", 
           title:"Save confirmation" 
        }, { 
            height: 200, 
            width: 450 })
    .then((success) =>{
        if (success.confirmed){
            console.log("Dialog closed using OK button.");
        }
        else{
            console.log("Dialog closed using Cancel button or X.");
            eventArgs.preventDefault(); //stop saving
        }
    });
}

Not much different, but in case you prefer the async-await pattern

async function  SaveAsyncWithDialog(executionContext){
    console.log("entering save");
    const eventArgs = executionContext.getEventArgs();
    if (eventArgs.getSaveMode() == 70){
        eventArgs.preventDefault();
        return;
    }
    eventArgs.disableAsyncTimeout();
    const success = await Xrm.Navigation.openConfirmDialog({ 
        text:"Would you like to save?", 
        title:"Save confirmation" 
        }, { 
            height: 200, 
            width: 450 });    
    if (success.confirmed){
        console.log("Dialog closed using OK button.");
    }
    else{
        console.log("Dialog closed using Cancel button or X.");
        eventArgs.preventDefault();
    }    
}

If we try now to save the changes, we see the confirm dialog. In the console we get an error from the platform saying that after 10 seconds the Promise timed out, but this is something internal; the save process is paused until the user confirms or rejects. If I click “Cancel” the data won’t be saved. If I click “OK” the data is saved 🙂

If I don’t save, the autosave kicks in every 30 seconds, but I don’t see the dialog because I’ve prevented that. Only the error messages are visible in the console.

If I choose OK, even if the timeout elapsed, I am still able to save

That’s it! Now we can take control of the Promise timeout inside the save events.