Harsha Lanka
ServiceNow Employee
ServiceNow Employee

In this article, we’ll walk you through how we have migrated some of the UI actions from Classic Experience to the Next Experience in Project Workspace.

 

Before we start, make sure you're familiar with:

                  Migrating UI actions from classic form to next experience

                  Declarative Actions in ServiceNow: The COMPLETE Guide

 

We're going to cover four main types of UI actions you might have:

  1. UI actions that work completely on the server-side
  2. UI actions that work on both the client and server-side
  3. Simple UI actions that work only on the client-side
  4. More complex client-side UI actions, like those that show pop-up windows (modals)

For UI actions that are purely server-side:

                  These are pretty straightforward to move over. They only contain server-side code – the code that runs on the ServiceNow server. For instance, a "Re-plan" button that changes the status of a plan when clicked.

 

Below are the steps to migrate this type of UI actions:

  • Open up the UI action.
  • Look for the “workspace” section.
  • Make sure "Format for configurable Workspace" is turned on.
  • Decide if you want this action to show as a button or a menu option in the new version, and select the appropriate setting. (If you want to be appeared as button in the next experience record page, enable “Workspace Form Button”. Else if you want this UI action to appeared as menu option under “more actions” action bar in the next experience record page, enable “Workspace Form Menu”. )

HarshaLanka_0-1712900852699.png

  • Set any necessary role restrictions in the "Requires role" section.
  • Save your changes.

HarshaLanka_1-1712900893380.png

 

To set the order in which this UI action appears, make an entry for it in the "UX Form Action Layout Item" for the resource plan table under “Record Page Action Layout” in “Project WS Record Page Action Config” UX Form Configuration.

 

For UI actions that involve both client and server-side scripting:

                  These actions might do things like checking that all necessary fields are filled out on your device before updating something on the server. Let’s consider an example:

“Confirm” UI action on the resource plan table. The UI action script checks if fields are changed and then updates the resource plan state if they are.

 

Below are the steps to migrate this type of UI actions:

  • Open up the UI action.
  • Look for the “workspace” section.
  • Make sure "Format for configurable Workspace" is turned on.
  • Decide if you want this action to show as a button or a menu option in the new version, and select the appropriate setting. (If you want to be appeared as button in the next experience record page, enable “Workspace Form Button”. Else if you want this UI action to appeared as menu option under “more actions” action bar in the next experience record page, enable “Workspace Form Menu”. )
  • For these particular actions to work in the new workspace, we'll need to adapt our existing scripts to be compatible with the workspace's client-side scripting requirements. This means we can't just reuse the old scripts as they are. We'll have to modify them to fit within the constraints of the workspace client script's APIs.
  • Lets look at the old/existing script of UI action
  • function confirm_checkDirtyForm() {
    	if (g_form.modified) {
    		var modifiedFields = g_form.modifiedFields;
    		var forceSave = modifiedFields['resource_plan.distribution'] || modifiedFields['resource_plan.end_date'] || modifiedFields['resource_plan.fte'] || modifiedFields['resource_plan.group_resource'] || modifiedFields['resource_plan.members_list'] || modifiedFields['resource_plan.members_preference'] || modifiedFields['resource_plan.planned_hours'] || modifiedFields['resource_plan.request_type'] || modifiedFields['resource_plan.resource_type'] || modifiedFields['resource_plan.start_date'] || modifiedFields['resource_plan.skills'] || modifiedFields['resource_plan.role'] || modifiedFields['resource_plan.user_resource'];
    		g_form.addErrorMessage(getMessage('There are some unsaved changes. Save the resource plan before confirming'));
    		return false;
    	}
    	if(g_form.getValue("planned_hours") == "0"){
    		g_form.addErrorMessage(getMessage('Planned hours is zero, the resource plan cannot be confirmed'));
    		return false;
    	}
    	gsftSubmit(null, g_form.getFormElement(), 'frm_confirm');
    }
    
    if(typeof window == 'undefined')
       serverConfirm();
    
    function serverConfirm() {
    	var updatedRecord = new GlideRecord('resource_plan');
    	updatedRecord.get(current.sys_id);
    	updatedRecord.state = ResourcePlanState.CONFIRMED;
    	updatedRecord.update();
    	action.setRedirectURL(updatedRecord);
    }
  • We need to migrate this script in below way and add this in "workspace client script" so that it would be compatible with workspaces. Please refer the comments towards the end of below script on how we have migrated this script.
  • function onClick(g_form) {
    
        function hasFieldModified(fieldName) {
            return g_form.$private.getField(fieldName).value != g_form.$private.getField(fieldName).originalValue;
        }
        if (g_form.isUserModified()) {
            var fieldsToBeValidated = ["distribution", "end_date", "fte", "group_resource", "members_list", "members_preference", "planned_hours", "request_type", "resource_type", "skills", "role", "user_resource"];
            var hasChangeInAnyField = false;
            for (var i = 0; i <= fieldsToBeValidated.length - 1; i++) {
                if (hasFieldModified(fieldsToBeValidated[i])) {
                    hasChangeInAnyField = true;
                    break;
                }
            }
            if (hasChangeInAnyField) {
                g_form.addErrorMessage(getMessage('There are some unsaved changes. Save the resource plan before confirming'));
                return false;
            }
        }
        if (g_form.getValue("planned_hours") == "0") {
            g_form.addErrorMessage(getMessage('Planned hours is 0, plan cannot be allocated'));
            return false;
        }
        var actionName = g_form.getActionName();
        g_form.submit(actionName);
        g_form.reload();
    }
    
    /*
    * g_form.modified api is no more valid in workspace client script. Hence, we are using g_form.isUserModified()
    * To find which fields are modified, g_form.modifiedFields property is no more valid in workspace client script. Hence, we have build a method "hasFieldModified" to figure out if field is modified or not.
    * Once the validation is success, from the workspace client script, we are calling g_form.submit(actionName) again, so that it executes the server side code that is already written in existing or old script.
    * Effectively, we have just included validation part in the workspace client script and on success, we are still relying on the execution of old UI action script.
    */
  • Set any necessary role restrictions in the "Requires role" section.
  • Save your changes.

To set the order in which this UI action appears, make an entry for it in the "UX Form Action Layout Item" for the resource plan table under “Record Page Action Layout” in “Project WS Record Page Action Config” UX Form Configuration.

 

For UI actions that are purely client-side:

               Moving these actions is generally straightforward because they only contain client-side code – the code that runs on the browser. But, we'll need to adapt our existing scripts to be compatible with the workspace's client-side scripting requirements. However, If you encounter any problems in finding the appropriate APIs, experience difficulties during the script migration, or are not satisfied with the performance post-migration, it may be necessary to create a fresh Declarative Action. This new action would then be associated with the record page. For detailed instructions on how to do this, please refer to the comprehensive resources Declarative Actions in ServiceNow: The COMPLETE Guide and Configuring new custom actions in Planning/Details page in Project Workspace.

 

For UI actions that are purely client-side and involve pop-ups or modals:

                   

When updating these specific UI actions for the new workspace, we'll need to revise our current scripts to work with the client-side scripting abilities of the workspace. Directly transferring the old scripts won't work because certain APIs, like GlideModal or GlideDialogWindow, can't be used with workspace client scripts.


Take, for example, the “Cancel” UI action on a resource plan table. This action triggers a pop-up where, upon confirmation, a REST API call is made to mark the resource plan as canceled.

 

Below are the steps to migrate this type of UI actions:

  • Open up the UI action.
  • Look for the “workspace” section.
  • Make sure "Format for configurable Workspace" is turned on.
  • Choose whether this action should appear as a button or a menu item in the new interface, then enable the corresponding option in the workspace settings.
  • For these particular actions to work in the new workspace, we'll need to adapt our existing scripts to be compatible with the workspace's client-side scripting requirements. This means we can't just reuse the old scripts as they are. We'll have to modify them to fit within the constraints of the workspace client script's APIs.
  • Lets look at the old/existing script of UI action
  • var resourcePlanSysId;
    var flag = "list";
    var showConfirmModal = function(resourcePlanSysId) {
    	
    	var formSourceName;
    	formSourceName = new GlideURL(window.location.href).getParam('sysparm_form_source');
    	var dialogClass = window.GlideModal ? GlideModal : GlideDialogWindow;
    	var cancelResourcePlanModal = new dialogClass("cancel_resource_plan", false, 400);
    	cancelResourcePlanModal.setTitle(getMessage("Confirm?"));
    	cancelResourcePlanModal.setPreference("resourcePlanSysId", resourcePlanSysId);
    	cancelResourcePlanModal.setPreference("sysparm_flag", flag);
    	cancelResourcePlanModal.setPreference('sysparm_form_source', formSourceName);
    	cancelResourcePlanModal.setPreference('focusTrap', true);
    	
    	cancelResourcePlanModal.render();
    };
    
    function onCancelResourcePlan() {
    
    	if(typeof g_sysId != 'undefined' && g_sysId)
    		resourcePlanSysId = g_sysId;
    	else if(typeof g_list != 'undefined')
    		resourcePlanSysId = g_list.getChecked();
    	else{
    		resourcePlanSysId = g_form.getUniqueValue();
    		flag = "form";
    	}
    	
    	if(resourcePlanSysId)
    		showConfirmModal(resourcePlanSysId);
    	else
    		GlideUI.get().addOutputMessage({
    			msg : getMessage('Please select a Resource Plan')
    		});
    }
    
  • We can migrate the script in the below way, as mentioned in Migrating UI actions from classic form to next experience
  • function onClick(g_form) {
    	var resourcePlanSysId = g_form.getUniqueValue() || g_sysId;
    	var flag = "form";
    
    	function openPopup() {
    		var url = "/cancel_resource_plan.do?sysparm_rpid="+resourcePlanSysId+"&sysparm_flag="+flag+"&sysparm_form_source=workspace";
    		g_modal.showFrame({
    			title: getMessage("Confirm?"),
    			url: url,
    			size: 'md',
    			height: "5rem",
    			//autoCloseOn: 'URL_CHANGED',
    			callback: function (ret, data) {
    				if(data && data.actionType == "ok") {
    					if(data.status == "success") {
    						g_form.save();
    					}
    				}
    			}
    		});
    	}
    	if(resourcePlanSysId) {
    		openPopup();
    	} else {
    		console.error("Invalid resource plan Id");
    	}
    }
  • Set any necessary role restrictions in the "Requires role" section.
  • Save your changes.
  • Let's delve into the script changes, This example is using a UI Page embedded into next experience modal and uses iframeMsgHelper.confirm or iframeMsgHelper.cancel API (Internally API uses window.parent.postMessage) in the UI Page to pass data from the iFrame back to the workspace. Because g_form is not accessible in the UI Page when it is in the iFrame. 
  • In the above script, g_modal.showFrame is the API that opens up a next experience modal in workspace with iframe and loads the "url" given while calling API.
  • URL that we framed here in the above script generally points to "cancel_resource_plan" UI page. sysparm_rpid, sysparm_flag and sysparm_form_source are the params that UI page is expecting.Screenshot 2024-04-15 at 10.08.56 AM copy.png
  • Let's have a look on how it would look in workspace.

Screenshot 2024-04-15 at 11.45.12 AM.png

  • So, while migrating these type of actions - It is expected that we have to do relevant compatible changes in UI pages as well. In your instance, Please refer to the cancel_resource_plan UI page for the compatible changes that we have done OOB.
  • In cancel_resource_plan UI page, SPMGmodalIframeMsgHelper UI script is included to build a communication between UI page in the iframe and workspace client script.

Screenshot 2024-04-15 at 10.08.56 AM copy 2.png

  • "sysparm_form_source" is the URL param that we have included in UI page to identify if this modal popup gets opened in workspace or other. Hence, the workspace client script passes "sysparm_form_source=workspace" in the URL.

Screenshot 2024-04-15 at 10.08.56 AM copy 2.png

  • As the client script of UI page gets executed in workspace environment, Any references of GlideDialogWindow or GlideModal in the client script will not be accessible. Hence, we have to conditionally handle the flow of execution.

Screenshot 2024-04-15 at 10.29.34 AM.png

  • After the relevant compatible changes in the client script of UI page based on our use case, we also need to provide handling of "Yes" and "No" buttons compatible to workspace. In this example, On click of Cancel action, system opens this UI page as a modal popup with "Yes" and "No" buttons.

Screenshot 2024-04-15 at 11.45.12 AM.png

  • On click of "Yes", Internally, cancel_resource_plan UI page calls rest API and on success, it just reloads the window using window.location.reload API. But this will not work in the current context. Hence, we have to come up with compatible code to make sure it works in workspace environment. 
    • So, In the client script of UI page, Inside the method where ever we are handling  "Yes" button on-click, we are using iframeMsgHelper.confirm API (API from SPMGmodalIframeMsgHelper UI script) to post a message to the workspace client script and from there we are controlling the execution flow from workspace client script.Screenshot 2024-04-15 at 12.03.14 PM.png
    • On execution of iframeMsgHelper.confirm API, it triggers the callback method defined in g_modal.frame APIScreenshot 2024-04-15 at 12.07.17 PM.png
    • In the above example, we are calling g_form.save API  in the callback just to refresh the next experience record page. 
  • On click of "No", Internally, cancel_resource_plan UI page just closes the modal by calling glideDialogWindow.destroy(). But this will not work in the current context. Hence, we have to come up with compatible to make sure it works in workspace environment.
    • So, In the client script of UI page, where ever we are handling "No" button on-click, you need to call iframeMsgHelper.cancel API to close a modal. It will automatically closes the modal.Screenshot 2024-04-15 at 12.17.23 PM.png
    • On execution of iframeMsgHelper.cancel API, it still triggers the callback method defined in g_modal.frame API even after system closes the modal. Any special handling can be done similar to the way we handled "Yes" button.

If you encounter any problems in finding the appropriate APIs, experience difficulties during the script migration, or are not satisfied with the performance post-migration, it may be necessary to create a fresh Declarative Action. This new action would then be associated with the record page. For detailed instructions on how to do this, please refer to the comprehensive resources Declarative Actions in ServiceNow: The COMPLETE Guide and Configuring new custom actions in Planning/Details page in Project Workspace.

 

 

 

Version history
Last update:
‎04-15-2024 12:07 AM
Updated by:
Contributors