- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
This article is updated for Yokohama release.
In previous posts, I’ve explored how to set up a custom workspace for an app and configure forms for your records. Now, let's dive into buttons.
When it comes to buttons, you have two main options: UI Actions and Declarative Actions. While UI Actions are familiar and allow a button to appear in both the classic UI and the workspace, Declarative Actions are necessary for certain use cases, such as adding buttons to lists, related lists, or opening modals. If you're creating server-side buttons for forms, I recommend sticking with UI Actions, as they support both classic UI and Workspaces and are generally more familiar to developers.
Buttons Architecture.
You may wonder how these two types of buttons work together in Workspaces. Here's the breakdown:
-
UI Actions: When marked as "Format for Configurable Workspaces," the following objects are created:
-
UX Form Action record: This acts as a wrapper for all UI Actions, making them available in the Workspace.
-
UX Form Action Layout Item record: This represents the button in the Workspace, including its design and color.
-
-
Declarative Actions: Similarly, when you create a Declarative Action, related UX Form Action and UX Form Action Layout Item records are created automatically.
Additionally, the UX Form Action Layout record must be created manually to define which buttons are visible for a given table. Multiple layouts can be created for the same table, and optionally, the layout can be linked to an UX Action Configuration record to specify which layout to use for a specific workspace.
You can also create a UX Form Action Layout Group record to group multiple buttons together.
Configuring Form Buttons
Let’s start by adding buttons to your forms. For instance, I want a button that changes the state of a record in my app. For this, I’ll use a UI Action. To make it visible in the workspace, just fill in a few additional fields:
-
Select Workspace Form Button.
-
Check Format for Configurable Workspace.
-
Define the button’s action.
It’s that simple!
Related list buttons
While form buttons are configured similarly to how they were in the classic UI, related list buttons work a bit differently. All related list buttons are stored in the sys_declarative_action_assignment table. You can access them via Workspace Experience → Actions & Components → Related List Actions.
Hiding the New Button
If your related list is an M2M (many-to-many) table notice that default "New" button opens a new m2m record form instead of opening a child record form. If this is not the expected behavior, you can hide it.
To do this:
-
Open Related List Actions and search for the "New" button associated with the Global table.
-
Find the record for M2M tables and open it.
-
Create an exclusion rule for the "New" button, specifying the table name and checking Exclude this table.
Actions configuration
Now let’s configure actions for our buttons. Before adding buttons to an M2M table, it’s a good idea to create an Action Config record. This is optional, but it helps to keep all custom buttons centralized.
-
Go to Now Experience Framework → Actions & Events → UX Form Actions Configs and create a new record.
-
Associate your buttons with this configuration.
To tell the workspace to use this configuration:
-
Go to UX Application and replace the actionConfigId UX Page Property with the sys_id of your new action config.
Now, you can proceed with button configuration.
M2M Table Buttons
For M2M tables, I want to have buttons for creating a new record and associating or removing existing records. Since we’re working in a workspace, we’ll use three buttons instead of just "New" and "Edit": New, Add, and Remove.
New Button
The New button will create a new record in the child table and establish the relationship between the parent and child records.
❗Note: In Yokohama release there is an OOTB New button for m2m tables, so you don't need to create it anymore. But, if your table is not registered as m2m it won't be visible on the related list and you'll have to build this button anyway.
-
Create a new button, select your M2M table, and the view used in the workspace.
-
Set the Action label to "New" and select UXF Client Action for the Implemented As field.
-
Choose the Create New Record ManyToMany client action.
-
On the Action Configurations related list, associate your button with the previously created Action Config.
Remove button
The Remove button will delete the relationship between parent and child records, but not the child record itself.
-
Create the button as before, set the Action label to "Remove," and choose Server Script for Implemented As.
-
In the Server Script, use
current.deleteRecord();
to remove the relationship.
Add button
The Add button will open an OOTB Multi-Record Associator modal to allow users to select child records to associate with the parent record.
-
Create a new button and set the Action label to "Add."
-
In the Implemented As field, select UXF Client Action.
-
Create a new client action and populate the payload with the necessary values (e.g., table, parent record sys_id, related list name):
- route - mra;
- fields must contain:
- query - query defined for related list, use {{query}} variable;
- table - table name of m2m table, use {{table}} variable;
- parentRecordSysId - sys_id of parent record, use {{parentRecordSysId}} variable;
- userGivenTable - table name child record;
- label - title of the modal;
- parentFieldName - column on m2m table where you store parent record;
- referencedFieldName - column on m2m table where you store child record;
- extensionPoint - used to add additional filter to the list shown in the modal. If you want to see all records set it to DEFAULT;
- columns - columns from child table that you want to show in the modal;
- hideSelectAll - hides option to select all records;
- relatedListName - name of the related list, use {{relatedListName}} variable;
- params must contain:
- type - m2m (for m2m table).
-
Payload example:
{ "size":"lg", "route": "mra", "fields": { "query": "{{query}}", "table": "{{table}}", "parentRecordSysId": "{{parentRecordSysId}}", "userGivenTable": "x_snc_home_autom_0_automation_task", "label": "Add", "parentFieldName": "x_snc_home_autom_0_automation_idea", "referencedFieldName": "x_snc_home_autom_0_automation_task", "extensionPoint": "DEFAULT", "columns": "number,short_description,state", "hideSelectAll": false, "relatedListName": "{{relatedListName}}" }, "params": { "type": "m2m" } }
-
On the Action Configurations related list, associate your button with the Action Config.
💡Tip: For the modal configuration, you can specify only the variables in the payload and fill in actual values in the declarative action form.
Opening Modals
Earlier, I recommended creating custom events to trigger modals. However, this approach is now outdated, and I no longer suggest using it. The recommended approach is much simpler and involves mapping a Declarative Action to the pre-configured [Record Page] Open Modal event, which is part of the Form controller. This means no additional configuration in UI Builder is necessary.
Steps to Map the Declarative Action to the Pre-configured Event:
-
Create the Event Mapping:
After creating your Declarative Action, you will associate it with the [Record Page] Open Modal event. This is done in the Event Mapping related list on the Action Assignment record.To map the event, follow these steps:
-
In the Action Assignment record, create a new Event Mapping via the related list.
-
Fill in the following fields:
-
Source Element ID: This is the ID of the component where your button is located. For example, if the button is in a related list, the component is called list_related (you can find this ID in UI Builder).
-
Source Declarative Action: This will be auto-populated.
- Controller: Set the Controller field to the Form controller (typically called Form), as this is the controller responsible for handling form actions, including modals.
-
Target Event: Select [Record Page] Open Modal. This is the OOTB event that comes with the Form controller. This approach eliminates the need for creating custom events in UI Builder.
-
Target Payload Mapping: This is the mapping between the Declarative Action's payload and the event’s payload. Use the following structure for the mapping:
{ "container": { "fields": { "container": { "columns": { "binding": { "address": [ "columns" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "extensionPoint": { "binding": { "address": [ "extensionPoint" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "hideSelectAll": { "binding": { "address": [ "hideSelectAll" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "label": { "binding": { "address": [ "label" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "parentFieldName": { "binding": { "address": [ "parentFieldName" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "parentRecordSysId": { "binding": { "address": [ "parentRecordSysId" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "referencedFieldName": { "binding": { "address": [ "referencedFieldName" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "relatedListName": { "binding": { "address": [ "relatedListName" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "table": { "binding": { "address": [ "table" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "userGivenTable": { "binding": { "address": [ "userGivenTable" ] }, "type": "EVENT_PAYLOAD_BINDING" }, "view": { "binding": { "address": [ "view" ] }, "type": "EVENT_PAYLOAD_BINDING" } }, "type": "MAP_CONTAINER" }, "params": { "container": { "type": { "binding": { "address": [ "type" ] }, "type": "EVENT_PAYLOAD_BINDING" } }, "type": "MAP_CONTAINER" }, "route": { "type": "JSON_LITERAL", "value": "mra" }, "size": { "type": "JSON_LITERAL", "value": "lg" } }, "type": "MAP_CONTAINER" }
-
-
-
Using the OOTB MRA Modal:
If you're opening the out-of-the-box MRA modal, the mapping above is sufficient, and you don't need to make any additional changes. The modal will automatically open with the correct configuration. -
For Custom Modals:
If you're using a custom modal, you'll need to ensure that the custom modal is added to the viewport modal page collection. This step allows ServiceNow to recognize and render the custom modal when the [Record Page] Open Modal event is triggered.
Controlling Button Visibility
To control button visibility, you can set conditions based on user roles or the state of the parent record. For instance, to show buttons only when the parent record is in the "Accepted" state:
-
Open the button in Advanced View.
-
Under the Conditions tab, add a Script Condition that checks if the parent record is in the "Accepted" state.
Note: Record conditions are evaluated only when the page loads, so they are not dynamic. For more complex visibility rules, use Dynamic Record Conditions.
Confirmation Message
Finally, you may want to display a confirmation message when a record is removed from the related list. This can be achieved through standard UI features.
In my next post you'll read about how to add some nice UI features to your custom workspace: Workspace for a custom app: headers, highlighted values, sidebar.
Not Recommended Anymore:
For historical context, here’s the old approach, which involved creating a custom event to trigger modals, a method that is now obsolete.
Previously, I recommended:
-
Creating Custom Events: You would define a custom event (like RECORD#OPEN_MODAL) to open modals.
-
Mapping Custom Events: You would then map this custom event to the modal's event handler, passing the required payload.
This older approach is no longer needed, as ServiceNow now offers a more efficient method of opening modals using the pre-configured [Record Page] Open Modal event in the Form controller.
Create new event
First, we need an event that will open the modal. Since it's a custom workspace there are no pre-configured events in it, so I'll create a new one. I prefer to use one event that will open a viewport modal and to configure all sorts of subpages for this modal. Let's call this event RECORD#OPEN_MODAL, this is how the same type of event is called in OOTB workspaces (but you can call it something else).
- Open your record in UI Builder, select top-level component (usually called Body).
- Add new handled event to the component.
- Call it the way you want, but notice the format of the event name. I'm not sure what will happen if you use other format, but I prefer to stick with it.
- Add payload fields. Since I'm going to use the same event for opening different subpages I need to pass 3 parameters in the payload:
- route - name of the subpage;
- fields - required parameters;
- params - optional parameters.
Next step is to open a modal when this event is fired. We are going to open preconfigured modal, you can find it in the modals list (see Modal Container).
- Create new event mapping.
- Select [Record Page] Open Modal - Data resource event handler. This is an OOTB event handler that will open preconfigured viewport modal.
- Fill payload parameters that should be passed, see screenshot below.
Associate button with event
Next, we need to associate our action with the event, so ServiceNow will know which event to fire. To do that on Action Assignment record you've created in step 1 create new Event mapping (via related list).
Follow these steps:
- Create new Event Mapping.
- Fill the fields in the event mapping:
- Source element ID - id of the component where you button is located. In our example it's related list and component is called list_related (you can find this ID in UI Builder);
- Source Declarative Action - auto-populated;
- Parent Macroponent - name of your record page;
- Target Event - event you created before;
- Target Payload Mapping - this is mapping between event's payload and client action payload we've created before, for this example you can use this code:
{ "type": "MAP_CONTAINER", "container": { "route": { "type": "EVENT_PAYLOAD_BINDING", "binding": { "address": [ "route" ] } }, "fields": { "type": "EVENT_PAYLOAD_BINDING", "binding": { "address": [ "fields" ] } }, "size": { "type": "EVENT_PAYLOAD_BINDING", "binding": { "address": [ "size" ] } }, "params": { "type": "EVENT_PAYLOAD_BINDING", "binding": { "address": [ "params" ] } } } }
- 16,508 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.