Bhubaneswar, Odisha, India
+91-8328865778
support@softchief.com

Restrict/Cancel Save operation conditionally in JavaScript as per Async operations result in Dynamics 365

Restrict/Cancel Save operation conditionally in JavaScript as per Async operations result in Dynamics 365

Sometimes you get requirements where you are doing some validation check in JavaScript by calling XRM.WebAPI or some Async operation and if validation failed, you want to restrict Save operation. If the call code is synchronous then there is no issues. But if the code is asynchronous, you need to take special care otherwise it will not work.

Here I have taken a business scenario “If a student already registered for a course on a date, then users cannot create another course registration for the same student for the same course on the same course start date. Also in the alert system should display a link to old record so that user can click the link to go to existing record for editing without create new record.”

Follow below steps:

Step 1 : Create the JS Web Resource

Create a Solution and add a JS web resource. This JS web resource will be called on Main Form Save event and Quick Create Form save event. The code checks if any record exist using Async XRM.WebAPI.RetrieveMultiple method with same course, student and course start date. If exist shows a Web Resource prompt and cancels Save operation.

use the below code in JS Web Resource. This web resource pass data parameter to HTML web resource.

var CourseForm = (function () {

    var SaveMode = {
			Save: 1,
			SaveAndClose: 2,
			SaveAndNew: 59,
			Autosave: 70
    };

    //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();
        
		var student = formContext.getAttribute("soft_student").getValue()[0].id;
        var course = formContext.getAttribute("soft_course").getValue()[0].id;
        var coursestartdate = formContext.getAttribute("soft_coursestartdate").getValue();

        if (student == "" || course == "" || coursestartdate == null) {
            return;
        }

        //preventing of the save operation before async operation is started
        executionContext.getEventArgs().preventDefault();

        var formatteddate = coursestartdate.getFullYear().toString() + "-" + (coursestartdate.getMonth() + 1).toString() + "-" + coursestartdate.getDate().toString();
        var fetchXML = "?fetchXml=<fetch mapping='logical'><entity name='soft_studentregisteredcourses'><attribute name='soft_name' /><order attribute='soft_name' descending='false' /><filter type='and'><condition attribute='soft_coursestartdate' operator='on' value='" + formatteddate + "' /><condition attribute='soft_course' operator='eq' uitype='soft_course' value='" + course + "' /><condition attribute='soft_student' operator='eq' uitype='soft_student' value='" + student + "' /><condition attribute='statecode' operator='eq' value='0' /></filter></entity></fetch>";

        Xrm.WebApi.retrieveMultipleRecords("soft_studentregisteredcourses", fetchXML).then(
            function success(results) {
                //so if there are other records with the same student and course with course start date
                if (results.entities.length !== 0) {
                    //this message is shown to user only when user caused save, autosave is just blocked
                    if (saveMode !== SaveMode.Autosave) {
                        var recordURL = GetEntityRecordUrl(executionContext, false, true, results.entities[0].soft_studentregisteredcoursesid);

                        var entityName = executionContext.getFormContext().data.entity.getEntityName();
                        var entityId = results.entities[0].soft_studentregisteredcourseid;

                        //var data = {
                        //EntityName: entityName,
                            //EntityId: entityId
						//};

						//custom parameter that you need in the modal dialog 
						var dialogParameters = {
							pageType: "webresource",//required 
							webresourceName: "soft_rewardvalidatehtml.html",//Html Webresource that will be shown
							data:recordURL //optional
                       
						};

						var navigationOptions = {
							target: 2,//use 1 if you want to open page inline or 2 to open it as dialog 
							width: 400,
							height: 300,
							position: 1,//1 to locate dialog in center and 2 to locate it on the side,
							title: "Validation Error"
                        };

                        Xrm.Navigation.navigateTo(dialogParameters, navigationOptions).then(
                        function (returnValue) {
                            //returnValue is blank when "Cancel" button is clicked 
                            if (!returnValue) {
                                return;
                            }

                            console.log(returnValue);

                            //Add your processing logic here    
                        },
                        function (e) {
                            //put your error handler here 
                        });

					}
				} 
				else {
					//otherwise 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 });
			}
		);
    }

	function GetEntityRecordUrl(executionContext, forceClassic = false, newWindow = true, recordId) {
		var strUIType = 'forceUCI=1';
		if (forceClassic == true || forceClassic == 1)
			strUIType = 'forceClassic=1';

		if (newWindow == 0)
			newWindow = false;

		var entityName = executionContext.getFormContext().data.entity.getEntityName();
		var entityId = recordId;

		var entityRecordUrl = executionContext.getContext().getClientUrl() + '/main.aspx?' + strUIType;
		entityRecordUrl += '&newWindow=' + newWindow;
		entityRecordUrl += '&pagetype=entityrecord&etn=' + entityName + '&id=' + entityId;

		return entityRecordUrl;
	}

	return {
		OnSave: OnSave
	};
	
})();

Save and Publish the JS Web Resource.

Step 2 : Create HTML Web resource and read parameter

The JS Web resource pass record id parameter to HTML web resource and in the HTML Web resource we read the record id and format the URL with record ID. Create an HTML web resource with correct name used in JS web resource. Use the code below. In the HTML web resource there is a OK button which on clicking closes the pop-up.

<html><head>
    <meta charset="utf-8">
    <title>Error</title>
    <script type="text/javascript" src="../ClientGlobalContext.js.aspx"></script>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <style type="text/css">
        .footer {
            position: fixed;
            bottom: 0;
            right: 0;
            padding-bottom: 10px;
            padding-right: 10px;
        }

        .footerButton {
            width: 150px;
        }
    </style>
    <script type="text/javascript">
        function onPageLoad() {
            debugger;
            var urlParams = getUrlParameters();
            document.getElementById("errorMessage").innerHTML="You cannot add same course for the student as already the student registered for the course for same date.</br> Check the Existing Record : <a target='_blank' href="+urlParams+">Record Link</a>";
           
            
        }
        function getUrlParameters() {
            var queryString = location.search.substring(1);
            var paramsvalue = encodeURI(queryString).split("=")[1];
            return decodeURIComponent(decodeURIComponent(paramsvalue));
        }
    </script>
<meta></head>
<body onload="onPageLoad();" onfocusout="parent.setEmailRange();" style="overflow-wrap: break-word;">

    <div id="errorMessage" style="font-family: Arial;padding:20px" class="alert alert-danger">
        
    </div>
    <div  style="width:95%;text-align:right"> 
	<button class="btn btn-primary" onclick="javascript:window.close('','_parent','');">OK</button>
	</div>
</body></html>

Step 3 – Call JS method from Main Form Save Event and Quick Create Save Event

Now you can open Main form of Student Registered course table and add JS library and call the function on save.

Do the same thing for the Quick create form.

Publish all components or solution.

TEST NOW.

If you open Student Registered Courses table and try to create a record with same student, course and course start date you will see below pop-up.

The Record Link will open the existing record. Like that if you use quick create option also it will shows message and cancel save operation.

Hope this helps.

Follow my blog for more trending topics on Dynamics 365, Azure, C#, Power Portals and Power Platform. For training, Courses and consulting, call to us at +91 832 886 5778 I am working more for community to share skills in Dynamics 365 and Power Platform. Please support me by subscribing my YouTube Channel. My YouTube Channel link is this : https://www.youtube.com/user/sppmaestro

You can enroll now !We are giving 30% discount on our Internship Program

Don’t miss the chance to participate in the upcoming Internship Program which will be done using Microsoft Dot Net Web Development Full Stack Technology. The new batch will be starting from May 20, 2024.  We will have most experienced trainers for you to successfully complete the internship with live project experience.

Why to choose Our Internship Program?

Industry-Relevant Projects
Tailored Assignments: We offer projects that align with your academic background and career aspirations.
Real-World Challenges: Tackle industry-specific problems and contribute to meaningful projects that make a difference.

Professional Mentorship
Guidance from Experts: Benefit from one-on-one mentorship from seasoned professionals in your field.
Career Development Workshops: Participate in workshops that focus on resume building, interview skills, and career planning.

Networking Opportunities
Connect with Industry Leaders: Build relationships with professionals and expand your professional network.
Peer Interaction: Collaborate with fellow interns and exchange ideas, fostering a supportive and collaborative environment.

Skill Enhancement
Hands-On Experience: Gain practical skills and learn new technologies through project-based learning.
Soft Skills Development: Enhance communication, teamwork, and problem-solving skills essential for career success.

Free Demo Class Available