Work Instruction | How to Migrate existing Record Pages to Standard Record Pages Revision Log... (Last updated: 19-Jan-2023) VersionPublishedSummary of Changes1.001-19-2023Initial version Overview This article details the steps required to migrate existing Record Pages into the new Standard Record Pages (SRP). The SRP automatically binds components in the SRP to data resources. These components are used in UI Builder. SRP became available in the Tokyo release. You can only create SRPs in Tokyo and beyond. There are breaking changes in SRP so you have to use these migration instructions to move your existing Record Pages into SRPs. Risks & Cautions CAUTION: Fully test your migrated SRPs before removing your existing Record Pages. Dependencies SRPs became available in the Tokyo release. Instructions The Standard Record Page (SRP) component replaces the Record Page component in the Tokyo release. The SRP contains new functionality (controllers and presets) that dramatically eases the task of binding UI components on an SRP to data resources. Existing Record Pages will continue to work but starting with the Tokyo release, you can no longer create new Record Pages. This article shows how to migrate existing Record Pages to SRPs. The Empty Record Page (ERP) was also released in Tokyo. It contains the same new functionality as the SRP but doesn't contain the components in the SRP. All SRP functionality described in this article also applies to ERP unless otherwise noted. You can find the SRP in UI Builder. Migration strategy You can't simply copy referenced or copied versions of Record Pages into SRPs because there are breaking changes in SRP. The following figure provides an overview of the migration process. The following sections detail how to do the migration. 1. Collect the configuration changes For each Record Page in your application, make a list of the configuration changes and added extensions, e.g., page properties, action bar entries, Record Page tab extensions, contextual sidebar extension entries, and modal extension entries. You'll use this list to update the preset values in the SRP configuration. Experience page properties on Record Pages are available on your SRP with the following bindings: @context.app.<property>.Here's an example of a list of Record Page configurations. My Application objects to migrate:Record Page object::SRP Object:Experience properties @context.app.view Preset for <tbd> @context.app.headerConfigId Preset for <tbd>Action Bar UI Action name <your UI Action> Script enabled <collect script name>Condition requiredEvent and payload <TBD> DA name <your DA> Script enabled <collect script name>Condition requiredEvent and payload <TBD>Record Page Tabs Playbook tab Client condition: <insert condition here> SRP extension point <fill in SRP extension point for Record Page Tabs name + controller preset> Overview tab Server condition: <insert condition here> SRP extension point <fill in SRP extension point for Record Page Tabs name + controller preset> Logic to hide Details tab <add howto>Override preset for <fill in SRP extension point for Record Page Tabs name + controller preset>Contextual SidebarRecommendation pageSRP extension point <fill in SRP extension point for Contextual Sidebar name + controller preset> Logic to hide the Attachment tabOverride preset for <fill in SRP extension point for Contextual Sidebar name + controller preset>Modals<TBD><TBD> 2. List all customization changes List the changes to the functionality or layout added by your team to a custom or copied Record Page. For example, your team might have changed the standard layout of the Record Page, or customized the components, scripts, or client state variables. You'll reapply these customizations in the SRP after you create it. 3. Configure the Declarative Actions The SRP and Record Page use Declarative Actions (DAs) to add custom actions to a page. The extensibility model of the Action Bar remains the same. So, the main effort is to configure the DAs for the SRP. DAs use Add-on Event mappings to forward their user action events to the right handler on a UI-Builder-created page. During migration, you must recreate or move these Add-on event mappings to the new SRP. The sys_ux_addon_event_mapping table contains the records belonging to your application, e.g., ParentMacroponent=<RecordPageSysId>, where <RecordPageSysId> = 558adc930f0210107d20409dc4767e84 for a Record Page, or a different value for a custom Record Page. 3a. Migrating the Declarative Actions Test the Declarative Actions (DAs) on your new SRP template-based page to make sure they do not appear.If your DAs do not work, copy the Add-on Event Mapping record from your Record Page into your SRP. Alternatively, update the record with the new ParentMacroponent field. This option reduces your chance of A/B testing your pages.Update the new Add-on Event Mapping record to replace the ParentMacroponent field with the sysId of your new SRP.If deemed safe, remove the original Add-on Event Mapping record.For DAs that rely on an MRA route (usually called, Add), the type property must be an optional parameter. Look for "address": ["type"] in the Payload section and move the type object into the params part of the payload, for example:"params": { "type": "MAP_CONTAINER", "container": { "type": { "type": "EVENT_PAYLOAD_BINDING", "binding": { "address": ["type"] } } }} 4. Configure the dynamic routing The Record Page and SRP share the same dynamic routing functionality. Update the source component on your sys_ux_dynamic_route_mapping record. The following table shows the mapping. BehaviorSource component for Record PageSource component for SRPRelated List Newgformlist_relatedRelated List column item clickgformlist_relatedForm header item clickgformsecondary_itemsUI Action clickgform ui_action_bar (for Declarative Action) record (for classic UI Action) Activity Stream link clickactivity_streamrecordForm reference icon clickreferenceInfoClickformAgent Assist item clickagent_assist_1agent_assist 5. Rebuild viewport subpages for Record Page tabs and the Contextual Sidebar If your team added custom viewport subpages to the Record Page tabs or Contextual Sidebar, you must decide whether to: Move the viewport subpage inline on the new SRP, orKeep the pages as viewport subpages Tip: Move your viewport subpage tabs inline unless you have to deliver tab content through multiple plugins. Inline tabs provide usability, performance, and configuration improvements. Moving a viewport subpage inline on an SRP You might choose to move viewport subpage tabs, such as the Details tab, Related Lists, and Related Info to be inline because: There's a significant improvement to the overall user experience.Inline tabs allow presets to be used to set their input properties. Inline tabs are more performant because they avoid extra server-side calls.Moving content from different pages into a single SRP-based page reduces artifacts like screens, routes, and screen collections. Use the following procedure to move viewport tabs inline on an SRP. In UI Builder, open a new SRP and select the Main Tab object. You can see in the configuration tab the following tabs: Details and RelatedList, which have been inlined. To add your new content inline, for example, to add a Playbook tab, select Add.To add a new tab to replace your viewport child tab, select Start from an empty container, and then select Next.Enter the tab title and icon, and hit Create.Once created, you see the following:Move the Playbook tab to the first position; that is how the order is calculated. On the page, edit the content of the inline tab to include Playbook content.Use the same procedure to add inline tab content to the Contextual Sidebar Tabs component, but start the process (step 1 above) with the Tab sidebar object. Keep the viewport subpages by rebuilding them You might want to keep pages as viewport subpages because you want: The flexibility to deliver content from different plugins or applications.To separate work streams to avoid merge conflicts. This practice is discouraged. Create or find the following extension point record in the sys_us_extension_point table. FieldValueNotesName <app-name> record tabs, <app-name> contextual sidebar This is the extension point name. Relate it to its function by preceding 'record tabs" or 'contextual sidebar' with the application name, for example, CWF record tabs, SOW contextual sidebar.ComponentTabsThe viewport component. In this case, Tabs is the name of the viewport you want to bind to from SRP.ControllerRecordThe controller where you want to get your parameters. Point the viewport subpage's route property extension point at the extension point discovered in the first step.Note: You can leave the pre-Tokyo parent binding in place for the properties, ParentMacroponent and ParentMacroponentCompositionElementId. Doing so enables simple viewport children with no input properties, conditions, or thrown events to work in both San Diego and Tokyo-based viewports.If your viewport subpage takes input parameters, screen conditions, or throws events to be handled by the parent page with the viewport container, change the following parameter mapping as shown in the following table. ViewportsExtension PointsInline Tab Screen Configuration Parent page (PP) Context Props (@context.props.*) Extension Point (EP) Controller bindings Use as is, in PP Route ParentMacroponentCompositionElementId (MCE) propertyValues PP State Props (@state.*)EP Controller bindingsUse as is, in MCE propertyValues PP Data Broker (@data.*) EP Controller bindingsUse as is, in MCE propertyValues PP MCE selectable props (@elements.*) Not supported (validate) Use as is, in MCE propertyValues Screen Conditions Screen Configuration [eg: table=incident] Screen Configuration (limited to fields and parameters declared on the route) (validate) "Hide tab" feature in UIB PP Context Props (parent.*) [parent.table=incident] Not Available (parent.* references will resolve to an empty string)"Hide tab" feature in UIBScripted Conditions Screen Configuration Screen Configuration (limited to fields and parameters declared on the route) (validate)Add a data broker (to the host) to pull the necessary server-side data Host Context Props (bug)Not Available (references will resolve to undefined); Use controller output props insteadUse @context.propsScreen Event MappingsHandled events on the hostOperations on the controllers / App shell eventsCopy as appropriate under MCE event mappings Data broker operations on the hostOperations on the controllers / App shell eventsCopy as appropriate under MCE event mappings 6. Rebuild modals If you have modal content in your Record Page, add a modal to your new SRP in one of the following ways: Use UI BuilderMigrate your viewport modals by hand to add a reference to a viewport modal onto your SRP page and edit the modal itself Add a modal to an SRP using UI Builder To migrate an existing modal from the San Diego family Record Page to SRP using UI Builder: In UI Builder, open your SRP and select Add > Modal.A selection of modal types appears.Select one of the modal types.Select an option type: Inline in your page definition if choosing Alert, Confirm, Confirm and destroy, Custom or iFrame.Viewport using an extension point if choosing a Viewport Modal. Many to pre-Tokyo modals are viewport modals. Configure the modal by following the UI Builder prompts. Migrate viewport modals To migrate an existing modal from the San Diego family Record Page to SRP: Open the SRP in UI Builder.Select +Add > Viewport Modal.The Add a page collection modal appears.Select a Record Page modal collection to use in your SRP.You have the option of choosing a different collection from the original modal. The viewport appears.Select either: Edit Content to edit the existing modal in this collection.+ Add to add another modal to this collection.Note: You can use a different collection for your application's modals. Open your modal viewport and build out the content. Add the modal's input page properties, like table, sysId, or whatever your modal needs.Rebuild the content inside your old modal into the new modal: In UI16, copy the composition/layout.In UI Builder, rebuild the components individually on your new modal page. In UI Builder, bind the events that your modal would handle in the Events tab. In UI Builder, in the Configuration tab, bind the input parameters of your modal page (say, table and sysId above) to the output of the controller attached to the extension point. Here is how you would bind the controller outputs (shown referenced with @data.<outputField>) in the configuration: table: @data.table sysId: @data.sysId where the content coming from the UI Controller is attached to your extension point. 7. Apply the configuration changes to a new page Apply the Record Page customizations to your new SRP: Experience Page PropertiesAction bar entries on your pageRecord Page Tabs extension entries on your pageContextual Sidebar extension entries on your page Review your page functionally. Things to test include: All buttons work as expected.Navigating from one page to another, and one tab to another works as expected.The right view exists for things, such as case records, knowledge records, or any special dynamic route mapping.Special Handling Notes or other modals you built open and close as expected.All the inline or viewport tabs you added work as expected. 8. Apply the customizations to your new SRP Each of the Record Page components that are part of the SRP template has a preset value. The following image shows preset values. The data flow is as follows: An SRP page loads.The required parameters table and sysId from the URL or route mapping become available on the page.The page loads the components defined in the composition section. Some of these components may have presets that pull in a controller. For example, the header_image component pulls in the Record controller in the image above.The page variables table and sysId are bound to the Record controller so it can load the record internally and expose the many fields describing the record in the object, @data.gform.One of those fields is the value for header_image, available in the path, @data.gform.form.header.headerImageItem.headerImageThe preset is configured to pass this path containing the header_image into the proper field (image_source) of the component, as shown: It is important to verify that the components on your page have the correct preset configured and pulls in the Record controller for your SRPs. If you overrode presets, such as changing the path from @data.gform.form.header.headerImageItem.headerImage to something else, make sure the preset overrides are correct. Breaking changes between Record Pages and SRPs Breaking changes that require Record pages to be rebuilt with SRP include: The new Tabs component replaces the use of both Record Page Tabs and Contextual Sidebar. The Tabs component has a horizontal layout for supporting related lists in the current tab configuration, and a vertical layout to support the contextual sidebar.All out-of-box tabs in both horizontal and vertical layouts now use the inline feature of the Tabs component instead of separate viewport subpages to take advantage of presets. This includes the Overview, related lists/info tabs inside Record Page Tabs, Attachment, Agent Assist, and Templates for Contextual Sidebar. This means that SRP will not have an extension point defined for Record Page Tabs and Contextual Sidebar OOB the way Record Page does.The extension point model changed for all viewport subpages; moving from a parent/child model to an M2M model with named extension points bound to controllers for parameter availability. This means that references to objects in the parent page or @parent have to be translated to the values coming from the extension point's controller. Troubleshooting tips Scenario 1: A declarative action is visible on the Record page but when clicked, the modal doesn't appear. Solution: See if the modal's route is using the Record page modal extension point and if the parent macroponent is empty. In add-on event mapping, the modal route should use the UI controller record page and source element Action bar. (If it is a related list, use the List – Related.) Make sure the parent is empty on the screen. Scenario 2: Modal or custom pages are loaded but do not fire the events. Solution: SRP uses controllers. So, make sure that events inherited from controllers like UI controller record page, and form controllers are used. Old G_Form or Record inherited events do not work with SRP. Scenario 3: Screen conditions used in any custom page or custom tab do not work. Solution: Make sure in sys_ux_screen_condition parameter mapping payload is added and if a script is used then parameters are accessed in this manner: params.data.<param_name>. Scenario 4: Dynamic route mapping is not getting applied. Solution: Use the proper source component. Source components are changed for SRP. Listed them in technical details. Scenario 5: The MRA does not appear. Solution: To support SRP, the payload structure changed. In the MRA, specify the client action and the add-on event mapping as mentioned in the technical details. It needs to be used and in add-on event mapping uses the UI controller record, and the Source element is an Action bar or list related, based on where it is used. Scenario 6: After migrating to SRP, the custom tab in the related or contextual sidebar does not appear. Solution: It might be due to a configuration issue. If the custom tab is in the related list, it should use the Asset workspace record tabs extension point. If it is on the contextual sidebar, the Asset workspace sidebar extension point needs to be used. Screen conditions, routes, and add-on event mapping should be modified as mentioned in the technical details. <!-- .SOKMKBArticle div.margin { padding: 10px 40px 40px 30px; color: #283d40; font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; font-size: 10pt; } .SOKMKBArticle div.fed{ background-color: #f5f8fa; border: 1px solid; border-color: #bfbfbf; padding: 10px; } .SOKMKBArticle .FedRestricted{ background-color: #c00000; color: #ffffff; padding: 10px; margin-top: 10px; text-align: center; font-size: 14pt; font-weight: bold; } .SOKMKBArticle .CustRestricted{ background-color: #ff0000; color: #ffffff; padding: 10px; margin-top: 10px; text-align: center; font-size: 14pt; font-weight: bold; } .SOKMKBArticle .SNRestricted{ background-color: #ea700d; color: #ffffff; padding: 10px; margin-top: 10px; text-align: center; font-size: 14pt; font-weight: bold; } .SOKMKBArticle .SNConfidential{ background-color: #ffc000; color: #ffffff; padding: 10px; margin-top: 10px; text-align: center; font-size: 14pt; font-weight: bold; } .SOKMKBArticle .Public{ background-color: #00b050; color: #ffffff; padding: 10px; margin-top: 10px; text-align: center; font-size: 14pt; font-weight: bold; } .SOKMKBArticle table.tocTable { border: 1px solid; border-color: #f2f2f2; background-color: #f2f2f2; padding-top: .6em; padding-bottom: .6em; padding-left: .9em; padding-right: .6em; } .SOKMKBArticle table.noteTable { align: left; border: none; border-color: #81b5a1; background-color: #f2f2f2; width: 100%; border-spacing: 2; font-size: 11px; } .SOKMKBArticle table.internalTable { border-top: 1px solid; border-left: 1px solid; border-color: #81b5a1; width: 100%; border-spacing: 1px; } .SOKMKBArticle .sp td { border-bottom: 1px solid; border-right: 1px solid; border-color: #81b5a1; background-color: #ffffff; height: 20px; padding-top: .5em; padding-bottom: .5em; padding-left: .5em; padding-right: .5em; } .SOKMKBArticle .sphr td { border-right: 1px solid; border-bottom: 1px solid; border-color: #81b5a1; background-color: rgb(245, 245, 245); padding-top: .5em; padding-bottom: .5em; padding-left: .5em; padding-right: .5em; height: 20px; } .SOKMKBArticle .sh td { border-bottom: 1px solid; border-right: 1px solid; border-color: #81b5a1; background-color: #81b5a1; color: #ffffff; height: 20px; padding-top: .5em; padding-bottom: .5em; padding-left: .5em; padding-right: .5em; } .SOKMKBArticle th { padding-top: .5em; padding-bottom: .5em; padding-left: .5em; padding-right: .5em; border-bottom: 1px solid; border-right: 1px solid; border-color: #81b5a1; background-color: #283d40; font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; font-size: 10pt; color: #ffffff; height: 20px; } .SOKMKBArticle td { border-color: #81b5a1; margin: 5px 5px 5px 5px; font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; font-size: 10pt; color: #283d40; } .SOKMKBArticle p { color: #283d40; font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; } .SOKMKBArticle li { color: #283d40; font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; font-size: 10pt; line-height: 1.5; } .SOKMKBArticle pre { font-family: Courier New; } .SOKMKBArticle div { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; } .SOKMKBArticle hr { border-top-width: 1px; border-top-style: solid; border-top-color: #81b5a1; } .SOKMKBArticle a { color: #81b5a1; } .SOKMKBArticle a.two:link { padding: 15px 45px 15px 45px; margin-top: 20px; color: #ffffff; text-align: center; background-color: #1F8476; border: 1px solid; border-color: #1F8476; } .SOKMKBArticle a.two:visited { padding: 15px 45px 15px 45px; margin-top: 20px; color: #ffffff; text-align: center; background-color: #1F8476; border: 1px solid; border-color: #1F8476; } .SOKMKBArticle a.two:hover { color: #ffffff; background-color: #259b8a; } .SOKMKBArticle .button { padding: 15px 45px 15px 45px; margin-top: 20px; color: #ffffff; text-align: center; background-color: #1F8476; border: 1px solid; border-color: #1F8476; } .SOKMKBArticle .title { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #81b5a1; font-size: 30pt; } .SOKMKBArticle .hd1 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-size: 20pt; border-bottom: 1px solid; border-bottom-color: #81b5a1; text-decoration: none; } .SOKMKBArticle h1 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-size: 20pt; font-weight: normal; border-bottom: 1px solid; border-bottom-color: #81b5a1; text-decoration: none; } .SOKMKBArticle .hd2 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #68a1af; font-weight: bold; font-size: 16pt; text-decoration: none; } .SOKMKBArticle h2 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #68a1af; font-weight: bold; font-size: 16pt; font-weight: normal; text-decoration: none; } .SOKMKBArticle .hd3 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: normal; font-size: 14pt; text-decoration: none; } .SOKMKBArticle h3 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: normal; font-size: 14pt; text-decoration: none; } .SOKMKBArticle .hd4 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: normal; font-size: 12pt; text-decoration: none; } .SOKMKBArticle h4 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: normal; font-size: 12pt; text-decoration: none; } .SOKMKBArticle .hd5 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: bold; font-size: 10pt; text-decoration: bold; } .SOKMKBArticle h5 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: bold; font-size: 10pt; text-decoration: bold; } .SOKMKBArticle .hd6 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: normal; font-size: 10pt; text-decoration: underline; } .SOKMKBArticle h6 { font-family: Century Gothic, Verdana, Helvetica, Arial, sans-serif; color: #283d40; font-weight: normal; font-size: 10pt; text-decoration: underline; } .SOKMKBArticle details { font-size: 10pt; } .SOKMKBArticle details[open] summary ~ * { animation: sweep .5s; margin-top: 0; padding-top: 10px; } @keyframes sweep { 0% {opacity: 0; margin-top: -10px} 100% {opacity: 1; margin-top: 0px} } .SOKMKBArticle summary { cursor: pointer; outline: none; margin-bottom: 3px; } .SOKMKBArticle .summary { background-color: #81b5a1; font-size: 10px; color: white; cursor: pointer; padding: 5px; width: 100%; border: none; text-align: left; outline: none; vertical-align: top; } -->