" MicromOne: Handling Form Save Events with Pre-Save Validation in Dynamics 365 Using JavaScript

Pagine

Handling Form Save Events with Pre-Save Validation in Dynamics 365 Using JavaScript


When customizing forms in Microsoft Dynamics 365, it’s common to need validation logic before saving a record — for example, checking for duplicates or ensuring data consistency.

This article shows how to manage the OnSave event and perform a custom validation call before allowing the record to be saved. The validation can invoke a custom action or Web API request to check for duplicates or other business rules.

Overview

We’ll use two key components:

OnSave handler — intercepts the save event and prevents it temporarily.

CheckEntity function — executes a validation call (e.g., to a custom action or an API endpoint).

If the validation finds a duplicate or issue, the user will be prompted with a confirmation dialog before proceeding.

The OnSave Function

Here’s a sample implementation of the OnSave event handler:

OnSave: function (ExecutionContext) {
    formContext = ExecutionContext.getFormContext();

    if (!SAVE) {
        ExecutionContext.getEventArgs().preventDefault();
        CustomNamespace.Entity.CheckEntity(formContext, function () {
            var confirmStrings = {
                subtitle: "Duplicate record found",
                text: "A similar record already exists. Do you want to continue saving?",
                title: "Duplicate Detected",
                confirmButtonLabel: "CONFIRM",
                cancelButtonLabel: "CANCEL"
            };
            var confirmOptions = { height: 350, width: 500 };

            Xrm.Navigation.openConfirmDialog(confirmStrings, confirmOptions).then(
                function (success) {
                    if (success.confirmed) {
                        SAVE = true;
                        formContext.data.save();
                    } else {
                        return; // cancel save
                    }
                }
            );
        }).then((existsDuplicate) => {
            if (existsDuplicate === false) {
                SAVE = true;
                formContext.data.save();
            }
        });
    }

    SAVE = false;
},

Key Notes:

  • The code uses preventDefault() to stop the default save action until validation completes.

  • It calls a validation function (CheckEntity) to verify data before saving.

  • If a potential duplicate is found, the user is shown a confirmation dialog.

  • If no duplicate exists, the record is saved automatically.

This ensures clean control over the save process and prevents unwanted record duplication.

The Validation Function

The CheckEntity function performs a validation call — for instance, invoking a custom action in Dynamics 365 that returns whether a duplicate exists.

CheckEntity: function (formContext, callback) {
    return new Promise((resolve, reject) => {
        var field1 = formContext.getAttribute("field1")?.getValue() || "";
        var field = formContext.getAttribute("field")?.getValue() || "";
        var field3 = formContext.getAttribute("field3")?.getValue() || "";
        var combinedField = (field1 + " " + field).trim();

        var lookupField = formContext.getAttribute("lookup_field")?.getValue();
        var entityId = formContext.data.entity.getId()?.replace(/[{}]/g, "") || "00000000-0000-0000-0000-000000000000";

        var entityObject = {
            "@odata.type": "Microsoft.Dynamics.CRM.entityname",
            "entityid": entityId,
            "combinedfield": combinedField,
            "field3": field3
        };

        if (lookupField && lookupField.length > 0) {
            entityObject["lookup_field@odata.bind"] = `/relatedentity(${lookupField[0].id.replace(/[{}]/g, "")})`;
        }

        var request = {
            ValidateEntityPreOperation_EntityObject: entityObject,
            getMetadata: function () {
                return {
                    boundParameter: null,
                    parameterTypes: {
                        ValidateEntityPreOperation_EntityObject: { typeName: "mscrm.entityname", structuralProperty: 5 }
                    },
                    operationType: 0,
                    operationName: "custom_ValidateEntityPreOperation"
                };
            }
        };

        Xrm.WebApi.execute(request).then(async response => {
            const contentType = response.headers.get("content-type");
            if (contentType && contentType.indexOf("application/json") !== -1) {
                var jsonResponse = await response.json();

                if (jsonResponse && callback) {
                    callback();
                    resolve(true); // duplicate found
                }
            } else {
                resolve(false); // no duplicate
            }
        }).catch(error => {
            console.error("Error in CheckEntity:", error);
            reject(null);
        });
    });
},