**TITLE**: Adding Custom Actions Using Extension Points (SAP Fiori elements)

**INTRODUCTION**: Step-by-step guide to add custom action buttons (list report global actions, table toolbar actions, object page header/table/form/footer actions) by implementing controller extensions and manifest.json entries for SAP Fiori elements (OData V2 and V4). Includes sample controller code, manifest fragments, and handler function templates. Preserve use of extensionAPI; avoid manipulating internal framework controls.

**TAGS**: fiori-elements, sapui5, extensionPoints, custom-actions, manifest.json, odata-v2, odata-v4, controller-extension

**STEP**: 1 — Caution and integration rule

**DESCRIPTION**: Use app extensions only when required. Always use SAP Fiori elements extensionAPI from your extension code. Do not manipulate internal controls, properties, models, or framework internals directly. After extension creation, UI placement and behavior is the app’s responsibility.

**LANGUAGE**: Plain

**CODE**:
```text
Caution:
Use app extensions with caution and only if you cannot produce the required behavior by other means, such as manifest settings or annotations. To correctly integrate your app extension coding with SAP Fiori elements, use only the `extensionAPI` of SAP Fiori elements.

After you've created an app extension, its display (control placement and layout) and system behavior (model and binding usage, busy handling) lies within the application's responsibility. SAP Fiori elements provides support only for the official `extensionAPI` functions. Don't access or manipulate controls, properties, models, or other internal objects created by the SAP Fiori elements framework.
```

**STEP**: 2 — Create controller extension files and handler functions (OData V2 & V4)

**DESCRIPTION**: Create controller extension files in your app. Typical app and file locations:
- App name: my_app
- Controller file names: MyListReportExt.controller.js (extends ListReport), MyObjectPageExt.controller.js (extends MainObjectPage), MySubObjectPageExt.controller.js (extends SubObjectPage)
- Location: my_app/webapp/ext/controller

Implement handler functions that will be invoked by the manifest-defined action press handlers. Use the extensionAPI inside handlers.

**LANGUAGE**: JavaScript

**CODE**:
```javascript
sap.ui.define([], function() {
    return {
        onCustomAction1 : function(oEvent) { … },
        onCustomAction2 : function(oEvent) { … },
        …
    }
})
```

**STEP**: 3 — OData V2: Extend manifest.json for controllerExtensions and actions (list report and object page)

**DESCRIPTION**: Add custom actions under sap.ui5 → extends → extensions → sap.ui.controllerExtensions. Define entity set, Actions block, and parameters (id, text, press handler, requiresSelection, applicablePath, determining, global, command). The example snippets below show where to add actions for list report table toolbar, object page header, object page table toolbar, form actions, footer bar, and list report global actions. When using keyboard shortcuts, add sap.ui.commands mapping. To construct the "<ComponentName>#<Component ID without app ID prefix>" string, use the component name in sap.ui.generic.app pages and fetch the component ID via getController().getOwnerComponent().getId() or from a ComponentContainer and strip the app prefix.

**LANGUAGE**: XML

**CODE**:
```xml
...
"extends": {
    "extensions": {
        "sap.ui.commands": {
            "<ComponentName>#<Component ID without app ID prefix>": {
                "<CustomActionCommandName>": {
                    "shortcut": "Ctrl+G"
                }
            }
        },
        "sap.ui.controllerExtensions": {
            "sap.suite.ui.generic.template.ListReport.view.ListReport": {
                "controllerName": "my_app.ext.controller.ListReportExtension",
                    "sap.ui.generic.app": {
                    "<entity set>": {
                        "EntitySet": "<entity set>",
                            "Actions": {
                                "<Action 1>": {
                                    "id": "<id>",
                                    "text": "<button text>",
                                    "press": "<handler function>",
                                    "requiresSelection": <true| false >,
                                    "applicablePath": <path>,
                                    "command": "<CustomActionCommandName>"
                                 },
                                "<Action 2>": {
                                    ...
                                },
                            ...
                        }
                        ...
                    }
                }
            }
        }
    }
}
```

**LANGUAGE**: XML

**CODE**:
```xml
...
"extends": {
    "extensions": {
        "sap.ui.commands": {
            "<ComponentName>#<Component ID without app ID prefix>": {
                "<CustomCommandName>": {
                    "shortcut": "Ctrl+K"
                }
            }
        },
        "sap.ui.controllerExtensions": {
            ...
            "sap.suite.ui.generic.template.ObjectPage.view.Details#my_app::sap.suite.ui.generic.template.ObjectPage.view.Details:: <entity set>": {
                "controllerName": "my_app.ext.controller.DetailsExtension",
                    "sap.ui.generic.app": {
                    "<entity set>": {
                        "EntitySet": "<entity set>",
                            "Header" : {
                            "Actions": {
                                "<Action 1>": {
                                    "id" : "<id>",
                                    "text" : "<button text>",
                                    "press" : "<handler function>"
                                    "applicablePath": <path>,
                                    "command": "<CustomCommandName>"
                                },
                                "<Action 2>": {
                                    ...
                                }
                            }
                        },
...
```

**LANGUAGE**: XML

**CODE**:
```xml
...
"extends": {
  "extensions": {
    "sap.ui.controllerExtensions": {
      ...
      "sap.suite.ui.generic.template.ObjectPage.view.Details#my_app::sap.suite.ui.generic.template.ObjectPage.view.Details:: <entity set>":  {
        "controllerName": "my_app.ext.controller.DetailsExtension",
          "sap.ui.generic.app": {
            "<entity set>": {
              "EntitySet": "<entity set>",
              "Sections": {
                "<SmartTable Facet ID>": {
                  "id" : "<SmartTable Facet ID>",
                  "Actions": {
                    "<SmartTable Action 1>": {
                      "id" : "<id>",
                      "text" : "<button text>",
                      "press" : "<handler function>",
                      "applicablePath": <path>,
                      "requiresSelection": <true|false>
                    },
                    "<SmartTable Action 2>": { ... }
                  }
...
```

**LANGUAGE**: XML

**CODE**:
```xml
...
"extends": {
  "extensions": {
    "sap.ui.controllerExtensions": {
      ...
      "sap.suite.ui.generic.template.ObjectPage.view.Details": {
        "controllerName": "my_app.ext.controller.DetailsExtension",
          "sap.ui.generic.app": {
            "<entity set>": {
              "EntitySet": "<entity set>",
              "Sections": {
                "<Form Facet ID>": {
                  "id": "<Form Facet ID>",
                  "Actions": {
                    "<Action 1>": {
                      "id" : "<id>",
                      "text" : "<button text>",
                      "press" : "<handler function>"
                      "applicablePath": <path>,
                    },
                    "<Action 2>": { ... }
                  }
...
```

**LANGUAGE**: XML

**CODE**:
```xml
"sap.ui5": {
   "extends": {
      "extensions": {
         "sap.ui.controllerExtensions": {
            "sap.suite.ui.generic.template.ObjectPage.view.Details#my_app::sap.suite.ui.generic.template.ObjectPage.view.Details:: <entity set>":  {
               "controllerName": "my_app.ext.controller.ListReportExtension",
               "sap.ui.generic.app": {
                  "<entity set>": {
                     "EntitySet": "<entity set>",
                     "Actions": {
                        "<Action 1>": {
                           "id": "<id>",
                           "text": "<button text>",
                           "press": "<handler function>",
                           "determining": true
                        }
                     }
                  }
               }
            }
         }
      }
   }
}
```

**LANGUAGE**: XML

**CODE**:
```xml
"sap.ui5": {
   "extends": {
      "extensions": {
         "sap.suite.ui.generic.template.ObjectPage.view.Details#my_app::sap.suite.ui.generic.template.ObjectPage.view.Details:: <entity set>":  {
            "sap.suite.ui.generic.template.ObjectPage.view.Detail": {
               "controllerName": "my_app.ext.controller.DetailsExtension",
               "sap.ui.generic.app": {
                  "<entity set>": {
                     "EntitySet": "<entity set>",
                     "Header": {
                        "Actions": {
                           "<Action 1>": {
                              "id": "<id>",
                              "text": "<button text>",
                              "press": "<handler function>",
                              "applicablePath": <path>,
                              "determining": true
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
}
```

**LANGUAGE**: XML

**CODE**:
```xml
...
"extends": {
   "extensions": {
      "sap.ui.controllerExtensions": {
         "sap.suite.ui.generic.template.ListReport.view.ListReport": {
            "controllerName": "my_app.ext.controller.ListReportExtension",
            "sap.ui.generic.app": {
               "<entity set>": {
                  "EntitySet": "<entity set>",
                  "Actions": {
                     "<Action 1>": {
                        "id" : "<id>",
                        "text" : "<button text>",
                        "press" : "<handler function>",
                     "global": <true|false>
                     },
                     "<Action 2>": {
                        ...
                     },
                     ...
                  }
               ...
```

**STEP**: 4 — Component ID construction (how to form "<ComponentName>#<Component ID without app ID prefix>")

**DESCRIPTION**: To map keyboard command entries in sap.ui.commands to a specific component use:
- <ComponentName> = the component name used in sap.ui.generic.app pages (e.g., sap.suite.ui.generic.template.ListReport)
- <Component ID without app ID prefix> = the component ID string with application prefix stripped. Obtain the full component id via the view or ComponentContainer and strip the app prefix.

**LANGUAGE**: Plain / JavaScript examples

**CODE**:
```text
Examples to get the component id:
- Using an XML view:
  Element.getElementById('STTA_MP::sap.suite.ui.generic.template.ListReport.view.ListReport::STTA_C_MP_Product').getController().getOwnerComponent().getId()
  where Element is required from module sap/ui/core/Element.

- Using ComponentContainer:
  Element.getElementById('__xmlview0-__clone0--host').getComponent()
  where '__xmlview0-__clone0--host' is the ComponentContainer ID.
```

**STEP**: 5 — OData V4: Declare actions under routing → targets → <target> → options → settings → controlConfiguration and define handler module

**DESCRIPTION**: For OData V4, define actions in manifest under routing targets controlConfiguration or content/header/footer actions. The action configuration supports press (handler), visible and enabled can be booleans or handler functions, text, and position (placement + anchor). The handler function name format is "<app ID from manifest>.<Folder Name>.<Script file>.<Method Name>". Create an ext folder and a CustomActions.js with functions that accept (oContext, aSelectedContexts).

**LANGUAGE**: XML

**CODE**:
```xml
"<Action name>": {
     "press": "<handler function>",
     "visible": <true|false|handler function>,
     "enabled": <true|false|handler function>,
     "text": "<button text>",
     "position": {
          "placement": <"Before"|"After">,
          "anchor": "<Anchor action name>"
     }
}
```

**LANGUAGE**: JSON

**CODE**:
```
"myCustomEnablement": {
     "press": "SalesOrder.ext.CustomActions.navigateExternal",
     "enabled": "SalesOrder.ext.CustomActions.enabledForCompletedOnly",
     "text": "Enabled for Completed"
}
```

**LANGUAGE**: JavaScript

**CODE**:
```javascript
enabledForCompletedOnly: function(oBindingContext, aSelectedContexts) {
     if (aSelectedContexts && aSelectedContexts.length === 1) {
          return true;
     }

    return false;
}
```

**LANGUAGE**: XML

**CODE**:
```xml
"controlConfiguration": {
     "<NavigationPropertyFromRootEntityType>/@com.sap.vocabularies.UI.v1.LineItem": {
          "actions": {
               "myCustomAction": {
                    "press": "TestApplication.ext.CustomActions.message"
                    ....
               }
          }
     }
}
```

**LANGUAGE**: Plain (file path instructions)

**CODE**:
```text
Create ext folder and CustomActions.js:
- Folder: webapp/ext
- File: webapp/ext/CustomActions.js
- Export methods that match manifest handler names, e.g., TestApplication.ext.CustomActions.message
```

**LANGUAGE**: JavaScript

**CODE**:
```javascript
sap.ui.define(
     [],
     function () {
          "use strict";
          return {
               message: function (oContext, aSelectedContexts) {
                    // oContext :  binding context of the current entity
                    // aSelectedContexts : array of binding contexts for selected items in case of table action
                    alert("message");
               },
          };
     }
);
```

**STEP**: 6 — Results / manifest.json placement examples (OData V4 table toolbar, object page header, footer, list report global, section and form toolbar actions)

**DESCRIPTION**: Use these manifest structures as concrete examples for routing target settings. Insert your action blocks under the appropriate target options.settings.content (header/footer) or controlConfiguration.

**LANGUAGE**: XML

**CODE**:
```xml
{
    "sap.ui5": {
        "routing": {
            "targets": {
                "<ListReportTargetName>": {
                    "options": {
                        "settings": {
                            "controlConfiguration": {
                                "@com.sap.vocabularies.UI.v1.LineItem": {
                                    "actions": {
                                        "<ActionName>": {
                                            "press": "<ApplicationId.FolderName.ScriptFilename.methodName>",
                                            "text": "<button text>",
                                            "enabled": <true|false|handler function>,
                                            "visible": <true|false|handler function>
                                        }
                                    }
                                }
                            }
                         }
                    }
                }
```

**LANGUAGE**: XML

**CODE**:
```xml
{
     "sap.ui5": {
          "routing": {
               "targets": {
                    "<ObjectPageTargetName>": {
                         "options": {
                               "settings": {
                                    "content": {
                                         "header": {
                                              "actions": {
                                                   "<ActionName>": {
                                                   }
                                              }
                                         }
                                    }
                               }
                         }
                    }
               }
          }
     }
}
```

**LANGUAGE**: XML

**CODE**:
```xml
"sap.ui5": {
     "routing": {
          "targets": {
               "<ObjectPageTargetName>": {
                    "options": {
                         "settings": {
                              "controlConfiguration": {
                                   <NavigationPropertyFromRootEntityType>/@com.sap.vocabularies.UI.v1.LineItem": {
                                        "actions": {
                                             "<ActionName>": {
                                             }
                                        }
                                   }
                              }
                         }
                    }
               }
          }
     }
}
```

**LANGUAGE**: XML

**CODE**:
```xml
"sap.ui5": {
     "routing": {
          "targets": {
               "<ObjectPageTargetName>": {
                    "options": {
                         "settings": {
                              "content": {
                                   "footer": {
                                        "actions": {
                                             "<ActionName>": {
                                                  ...
                                             }
                                        }
                                   }
                              }
                         }
                    }
               }
          }
     }
}
```

**LANGUAGE**: XML

**CODE**:
```xml
"sap.ui5": {
    "routing": {
        "targets": {
            "<ListReportTargetName>": {
                "options": {
                    "settings": {
                        "content": {
                            "header": {
                                "actions": {
                                    "<ActionName>": {
                                        ...
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```

**LANGUAGE**: XML

**CODE**:
```xml
"sap.ui5": {
        "routing": {
            "targets": {
                "SalesOrderManageObjectPage": {
                    "options": {
                        "settings": {
                            "enhanceI18n": "i18n/SalesOrderObjectPage.properties",
                            "controlConfiguration": {
                                "@com.sap.vocabularies.UI.v1.FieldGroup#OrderData": {
                                    "actions": {
                                        "customSectionAction": {
                                            "press": "SalesOrder.ext.CustomActions.alert",
                                            "visible": true,
                                            "enabled": true,
                                            "text": "Alert",
                                            "position": {
                                                "placement": "After",
                                                "anchor": "DataFieldForIntentBasedNavigation::SalesOrder::manageInline"
                                            }
                                        },
                                        "sectionAction2": {
                                            "press": "SalesOrder.ext.CustomActions.accountDetails",
                                            "visible": true,
                                            "enabled": true,
                                            "text": "Display account details",
                                            "position": {
                                                "placement": "Before",
                                                "anchor": "customSectionAction"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
```

**LANGUAGE**: XML

**CODE**:
```xml
"sap.ui5": {
    "routing": {
        "targets": {
            "SalesOrderManageObjectPage": {
                "options": {
                    "settings": {
                        "controlConfiguration": {
                            "@com.sap.vocabularies.UI.v1.FieldGroup#OrderData": {
                                "actions": {
                                    "customSectionAction": {
                                        "press": "SalesOrder.ext.CustomActions.alert",
                                        "visible": true,
                                        "enabled": false,
                                        "text": "Action on Form",
                                        "inline": true
                                    },
                                    "customSectionAction2": {
                                        "press": "SalesOrder.ext.CustomActions.alert",
                                        "visible": true,
                                        "enabled": true,
                                        "text": "Action not on Form"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```

**STEP**: 7 — Notes and tips

**DESCRIPTION**: Keep these operational notes while implementing:
- Action names and id values should be identical.
- requiresSelection defaults to true for table toolbar actions.
- determining is deprecated: avoid using determining buttons.
- If you add a command (keyboard shortcut), the event handler parameter signature can change; adapt code accordingly.
- Do not override default keyboard shortcuts (e.g., Ctrl+E, Ctrl+Enter).

**LANGUAGE**: Plain

**CODE**:
```text
Notes:
- Action name == id (use identical values).
- requiresSelection default: true (table toolbar).
- determining default: false (deprecated).
- When adding keyboard shortcut mapping (command), event handler parameter structure may change — update handler code if required.
- Do not override framework default keyboard shortcuts (e.g., Ctrl+E, Ctrl+Enter).
```

**STEP**: 8 — Example explorer and live demos

**DESCRIPTION**: For interactive examples and live samples, explore the SAP Fiori development portal "Standard Floorplans - Extensions - Extensions for Object Pages - Custom Action".

**LANGUAGE**: Plain

**CODE**:
```text
Live demo:
https://ui5.sap.com/test-resources/sap/fe/core/fpmExplorer/index.html#/topic/floorplanObjectPage/customAction
```


**TITLE**: Adding Custom Fields to the Filter Bar (SAP Fiori Elements — V2 & V4)

**INTRODUCTION**: Practical, code-focused guide to add and manage custom filter fields in the Smart Filter Bar for SAP Fiori elements (OData V2 and V4). Includes manifest.json fragments, XML fragments, controller extension code, export customization, custom operator registration and implementation, required-field handling, and metadata binding examples. Use these snippets directly in your app extension files and manifest.

**TAGS**: fiori-elements-framework, filter-bar, custom-filters, sapui5, manifest, extension, smartfilterbar, manifest.json

STEP: 1 — Add View & Controller Extension (OData V2)
DESCRIPTION: Extend ListReport to add a view fragment into the SmartFilterBar and register a controller extension. Place registration in manifest.json under "extends". Replace <myEntityset>, <myNamespace>, and controller name accordingly.
LANGUAGE: JSON
CODE:
```json
"extends": {
                        "extensions": {
                        "sap.ui.controllerExtensions": {
                          "sap.suite.ui.generic.template.ListReport.view.ListReport": {
                            "controllerName": "my_app.ext.controller.ListReportExtension"
                          }
                        },
                "sap.ui.viewExtensions": {
                                        "sap.suite.ui.generic.template.ListReport.view.ListReport": {
                                    "SmartFilterBarControlConfigurationExtension|<myEntityset>": {
                                              "className": "sap.ui.core.Fragment",
                                              "fragmentName": "<myNamespace>.ext.fragment.CustomFilter",
                                              "type": "XML"
                                          },
```

STEP: 2 — Mandatory Extension Methods (OData V2)
DESCRIPTION: Implement these extension methods in your controller extension. Purpose:
- onBeforeRebindTable: evaluate custom fields and add filters to bindingParameters.
- getCustomAppStateData: serialize state of custom controls into provided object for navigation/variant handling.
- restoreCustomAppStateData: restore values from saved custom app state.
- onInitSmartFilterBarExtension: initialize default values or bind controls to the filter bar.
Also note SAP Fiori elements calls corresponding extension variants: onBeforeRebindTableExtension, getCustomAppStateDataExtension, restoreCustomAppStateDataExtension, onInitSmartFilterBarExtension.
LANGUAGE: JavaScript
CODE:
```javascript
// Implement these methods inside your controller extension
// onBeforeRebindTable: add filters to oBindingParameters
// getCustomAppStateData: write custom filters into oCustomDataObject
// restoreCustomAppStateData: read from oCustomDataObject and set UI state
// onInitSmartFilterBarExtension: setup initial states, bindings or listeners
```

STEP: 3 — Modify Exported Filter Values (OData V2)
DESCRIPTION: Use onBeforeExportTableExtension in the controller extension to transform exported filter representation (e.g., convert internal filter OverallCost <= 1000 to user-friendly "Cost = Low"). Add this code to your controller extension file.
LANGUAGE: JavaScript
CODE:
```javascript
sap.ui.define([
    "sap/ui/core/Element",
>   "sap/ui/export/util/Filter"
> ], function (Element, ExportFilter) {
>   "use strict";
>
>   return {
>           onInit: function () {
>                 //...
>            },
>            /**
>             * This extension method modifies the "Cost" filter in the exported file.
>             *
>             * Removes the existing filter with label "OverallCost" and value "<=1000",
>             * and adds a new filter with label "Cost" and value "Low"
>            *
>            * @param oExportParams
>            * @returns
>            */
>            onBeforeExportTableExtension: function (oExportParams) {
>                if (!oExportParams.includeFilterSettings) {
>                      return;
>                  }
>                // Array of filters in the exported file
>                var aFilterSettings = oExportParams.filterSettings;
>                 // Find the "Cost" combobox in smart filter bar
>            var COST_COMBOBOX_ID = "STTA_MP::sap.suite.ui.generic.template.ListReport.view.ListReport::STTA_C_MP_Product--CustomFilter-OverallCost-combobox";
>                var oCostCombobox = Element.getElementById(COST_COMBOBOX_ID);
>                if (oCostCombobox && oCostCombobox.getValue()) {
>                     var oRawValue, sNewCostValue;
>                     for (var i = 0; i < aFilterSettings.length; i++) {
>                          var oCurrentFilter = aFilterSettings[i];
>                          // Find the filter with property "OverallCost"
>                          if (oCurrentFilter.getProperty() === "OverallCost") {
>                               //Store the raw value
>                               oRawValue = oCurrentFilter.rawValues[0];
>                              //Remove the current filter from the filters array
>                               aFilterSettings.splice(i, 1);
>                               break;
>                             }
>                      }
>                      // Derive Cost category (Low / High) from the raw value
>                      if (oRawValue.operator === "<=" && oRawValue.value === "1000") {
>                          sNewCostValue = "Low";
>                      } else {
>                         sNewCostValue = "High";
>                      }
>
>                      //Create a new export filter with the new label and value
>                      var sProperty = "OverallCost",
>                          sLabel = "Cost",
>                          oNewRawValue = {operator: "==", value: sNewCostValue},
>                          oCostFilter = new ExportFilter(sProperty, oNewRawValue, sLabel);
>
>                     //Add the updated filter to the filter settings array
>                     aFilterSettings.push(oCostFilter);
>                 }
>          }
>      };
> });
>  in the controller extension.
>                      This extension method is supported for exporting tables on analytical list
>                      pages, list reports and object pages.
```

STEP: 4 — Configure Custom Filter Field in manifest.json (OData V4) — Properties Template
DESCRIPTION: Define a custom filter field configuration under controlConfiguration in manifest.json. Replace placeholders: <Key_of_filter_field>, <Path to XML template>, <ProjectFolder>, property path, label and position. Use "key" equal to metadata property if using predefined operators; else any unique key when using custom operators but provide "property".
LANGUAGE: JSON
CODE:
```json
"<Key_of_filter_field>": {
  // Must be the exact technical name of the underlying filter field if the custom filter uses predefined operators.
  // For a custom filter using custom operators, this can be any unique key in the manifest.json file.

  "label": "<Static text or i18n binding>",
  // Label displayed in the UI. Can be a static string (e.g., "MyText") or a i18n binding (e.g., "{i18n>MyCustomLabel}").

  "property": "<Path to filter property>",
  // Only required for custom filters using custom operators.
  // Specifies the full path to the property to be filtered.
  // Example: "Rating"

  "template": "<Path to XML template>",
  // Path to the XML fragment that contains the filter control.
  // Example: "SalesOrder.ext.CustomRatingFilter".

  "required": true | false,
  // Defines whether the filter field is mandatory.
  // Default: false.

  "availability": "Default" | "Hidden" | "Adaptation",
  // Determines the visibility of the filter field:
  // - Default: shown in filter bar.
  // - Adaptation: available only via the Adapt Filters dialog.
  // - Hidden: not visible anywhere.

  "position": {
    "placement": "Before" | "After",
    "anchor": "<Key_of_filter_field>"
    // Defines the position of the custom filter field relative to another field.
    // For annotation-based filters, use the annotation key, e.g. "SalesOrder"
  }
}
```

STEP: 5 — Bind Custom Control to filterValues Model
DESCRIPTION: Bind your control's value property to the relative path {filterValues>} so SAP Fiori elements handles variant management and app state. The filterValues model is provided internally and must be used for value-provider bindings.
LANGUAGE: XML / Annotation
CODE:
```xml
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:l="sap.ui.layout">
    <HBox alignItems="Center" width="100%">

<!--     // ...bind it directly (and use a filter value type) (recommended) // -->
         <RatingIndicator id="MyCustomRatingIndicatorId"
             core:require="{
                 Value: 'sap/fe/macros/filter/type/Value'
             }"
             value="{path: 'filterValues>', type: 'Value'}"
         />

<!--     // Example for adapting the used operator, using GT (greater than) instead of default EQ -->
<!--     <RatingIndicator-->
<!--         id="MyCustomRatingIndicatorId"-->
<!--         core:require="{-->
<!--             Value: 'sap/fe/macros/filter/type/Value'-->
<!--         }"-->
<!--         value="{path: 'filterValues>', type: 'Value', formatOptions: { operator: 'GT' }}"-->
<!--     />-->

    </HBox>
</core:FragmentDefinition>
```

STEP: 6 — Handle Value Change Dynamically (setFilterValues)
DESCRIPTION: Use setFilterValues extension API to update filters when control changes. Template must include a control with change event that calls a handler which calls this.setFilterValues(<key>, <value>) or this.setFilterValues(<key>) to clear.
LANGUAGE: XML
CODE:
```xml
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:l="sap.ui.layout">
    <HBox alignItems="Center" core:require="{handler: <ProjectFolder>/ext/CustomRating'}" width="100%">

        <RatingIndicator id="MyCustomRatingIndicatorId"
            value="{= ${filterValues>} ? ${filterValues>}.substring(1) : null }"
            change="handler.onValueChanged" />

        <core:Icon src="sap-icon://reset"
            press="handler.onReset"
            class="sapUiSmallMarginBegin" />

    </HBox>
</core:FragmentDefinition>
```

STEP: 7 — Handler for Change & Reset (JS)
DESCRIPTION: Example handler module that uses extension API methods to update custom filter values.
LANGUAGE: JavaScript
CODE:
```javascript
sap.ui.define(["sap/ui/model/Filter", "sap/ui/model/FilterOperator"], function(Filter, FilterOperator) {
  "use strict";
  return {
    onValueChanged: function(oEvent) {
        this.setFilterValues("Rating", oEvent.getParameter("value"));
    },
    onReset: function(oEvent) {
        this.setFilterValues("Rating");
    }
  };
});
```

STEP: 8 — Custom Operators: manifest.json Setup (OData V4)
DESCRIPTION: For custom operators, add entries under "sap.fe" -> "macros" -> "filter" -> "customFilterOperators" with the function identifier string so Fiori elements recognizes them.
LANGUAGE: JSON
CODE:
```json
"sap.fe": {
    "macros":
        "filter": {
            "customFilterOperators": [{
                "name": "SalesOrder.ext.CustomRating.onMultiValueRatingLevels"
              }]
        }
   }
}
```

STEP: 9 — Custom Operator Template & Registration (OData V4)
DESCRIPTION: Example manifest snippet showing custom filter configuration where "property" is specified (required for custom operators) and template points to fragment. Replace placeholders accordingly.
LANGUAGE: JSON
CODE:
```json
    ...
"targets": {
                "SalesOrderManageList": {
                    "type": "Component",
                    "id": "SalesOrderManageList",
                    "name": "sap.fe.templates.ListReport",
                    "options": {
                        "settings" : {
                            "contextPath" : "/SalesOrderManage",
                            "controlConfiguration" : {
                                 "@com.sap.vocabularies.UI.v1.SelectionFields" : {
                                    "navigationProperties": ["_Partner", "_DistributionChannel"],
                                    "filterFields": {
                                        "Rating": {
                                            "label": "{i18n>CustomRatingFilter}",
                                            "availability": "Default",
                                            "template": "<ProjectFolder>.ext.CustomRatingFilter",
                                            "property": "Rating",
                                            "required": true,
                                            "position": {
                                                "placement": "After",
                                                "anchor": "SalesOrder"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
...
```

STEP: 10 — Custom Operator Fragment Example (MultiComboBox)
DESCRIPTION: Use macro type 'sap.fe.macros.filter.type.MultiValue' and specify formatOptions.operator referencing your custom operator function. The operator receives an array of values and must return a sap.ui.model.Filter (masks multi-value/multi-property logic).
LANGUAGE: XML
CODE:
```xml
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m">
  <MultiComboBox
    width="100%"
    selectedKeys="{path: 'filterValues>', type: 'sap.fe.macros.filter.type.MultiValue', formatOptions: { operator: 'SalesOrder.ext.onMultiValueRatingLevels' }}"
  >
    <layoutData>
      <FlexItemData growFactor="1" />
    </layoutData>
    <items>
      <core:Item key="low" text="low" />
      <core:Item key="medium" text="medium" />
      <core:Item key="high" text="high" />
    </items>
  </MultiComboBox>
</core:FragmentDefinition>
```

STEP: 11 — Implement Custom Operator Function
DESCRIPTION: Example implementation of a custom operator that converts user selections (low/medium/high) into model Filters and combines them with OR. Ensure you import sap.ui.model.Filter and FilterOperator where used.
LANGUAGE: JavaScript
CODE:
```javascript
onMultiValueRatingLevels: function (values) {
    const filters = [];

    values.forEach((value) => {
        switch (value) {
            case "low":
                filters.push(new Filter({ path: "Rating", operator: FilterOperator.EQ, value1: 1 }));
                break;
            case "medium":
                filters.push(new Filter({ path: "Rating", operator: FilterOperator.EQ, value1: 2 }));
                filters.push(new Filter({ path: "Rating", operator: FilterOperator.EQ, value1: 3 }));
                filters.push(new Filter({ path: "Rating", operator: FilterOperator.EQ, value1: 4 }));
                break;
            case "high":
                filters.push(new Filter({ path: "Rating", operator: FilterOperator.EQ, value1: 5 }));
                break;
            default:
                return null;
        }
    });

    return new Filter({
        filters: filters
    });
}
```

STEP: 12 — Mark Custom Filter Required & Visualize ValueState
DESCRIPTION: Mark a custom filter as required in manifest.json (required = true). To show dynamic validation (e.g., red frame when empty), bind valueState to a formatter in a handler module. Example XML fragment and handler implementation below.
LANGUAGE: XML
CODE:
```xml
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m">
    <Input
        core:require="{
            handler: 'SalesOrder/ext/CustomRating',
            Value: 'sap/fe/macros/filter/type/Value'
        }"
        value="{path: 'filterValues>', type: 'Value', formatOptions: { operator: 'Contains' }}"
        valueState="{path: 'filterValues>', formatter: 'handler.onFilterInputFormatValue'}"
        placeholder="{i18n>customStringFilterPlaceholder}"
    />
</core:FragmentDefinition>
```

LANGUAGE: JavaScript
CODE:
```javascript
sap.ui.define(["sap/ui/core/ValueState"], function (ValueState) {
    "use strict";

    return {
        // this handler needs to be registered in the custom XML's input field
        onFilterInputFormatValue: function (sValue) {
            if (!sValue) {
                return ValueState.Error;
            } else {
                return ValueState.None;
            }
        }
    };
});
```

STEP: 13 — Custom Filter Fields with Metadata Binding
DESCRIPTION: Use metadata (metaModel) bindings to derive labels or other text for custom filters, and configure property, template and position in manifest.json.
LANGUAGE: JSON
CODE:
```json
"MyCustomSoldToPartyThing": {
  "label": "{metaModel>/SalesOrderManage/SoldToParty@com.sap.vocabularies.Common.v1.Label}",
  "property": "Rating",
  "template": "SalesOrder.ext.CustomSoldToParty",
  "required": true,
  "position": {
        "placement": "After",
        "anchor": "MyCustomRatingThing"
    }
}
```

STEP: 14 — Notes, Restrictions & Best Practices (Actionable)
DESCRIPTION:
- Prefer annotations/manifest settings over app extensions; use extensionAPI only.
- Bind value-providing property of custom control to {filterValues>} to enable variant and app state handling.
- For predefined-operator custom filters: key must match metadata property name; do not specify property in manifest.
- For custom-operator filters: key must be unique and property must be specified (points to metadata property used). If a custom operator involves multiple properties, manifest "property" accepts only one property (possible limitation in multi-entity scenarios).
- Register custom operator identifiers under sap.fe -> macros -> filter -> customFilterOperators.
LANGUAGE: PlainText
CODE:
```text
- Use only extensionAPI functions for integration with SAP Fiori elements.
- Implement onBeforeRebindTable / getCustomAppStateData / restoreCustomAppStateData / onInitSmartFilterBarExtension where needed.
- Bind controls to {filterValues>} for variant and navigation persistence.
- Register custom operator functions in manifest.json so Fiori elements recognizes them.
```

fiori-elements-framework filter-bar custom-filters sapui5 manifest extension smartfilterbar manifest.json


**TITLE**: Adding Custom Filters to Overview Page (OVP) — SAP Fiori Elements

**INTRODUCTION**: This guide shows how to add custom filter controls to the global SmartFilterBar on an OVP overview page. It covers: creating an XML fragment for custom filter UI, implementing controller extension functions to produce filters and persist/restore app state, and wiring the fragment/controller in manifest.json.

**TAGS**: fiori-elements-framework, SAPUI5, OVP, smartfilterbar, filters, manifest.json, extension

**STEP**: 1 — Create view extension fragment (customFilter.fragment.xml)

**DESCRIPTION**: Create an XML fragment (e.g., customFilter.fragment.xml) that extends the SmartFilterBar by adding custom filter controls. Define these properties on each smartfilterbar:ControlConfiguration:
- groupId: associates the custom control with a filter group (falls back to default group if groupId not present).
- key: entity property name used for filter criteria.
- visibleInAdvancedArea: boolean to show control in advanced area.
- Input id: element id to retrieve/set value in controller.

Place the fragment in your extension namespace and reference it via manifest (see Step 3).

**LANGUAGE**: XML

**CODE**:
```xml
<core:FragmentDefinition xmlns="sap.m"
                         xmlns:smartfilterbar="sap.ui.comp.smartfilterbar"
                         xmlns:core="sap.ui.core">
    <!-- Product ID Filter -->
    <smartfilterbar:ControlConfiguration groupId="_BASIC" key="ProductID" label="Product ID (Extension)" visibleInAdvancedArea="true">
        <smartfilterbar:customControl>
            <Input id="ProductID" type="Text"/>
        </smartfilterbar:customControl>
    </smartfilterbar:ControlConfiguration>

    <smartfilterbar:ControlConfiguration groupId="GlobalFilters" key="SalesOrderID" label="Sales Order ID (Extension)" visibleInAdvancedArea="false">
        <smartfilterbar:customControl>
            <Input id="SalesOrderID" type="Text"/>
        </smartfilterbar:customControl>
    </smartfilterbar:ControlConfiguration>
</core:FragmentDefinition>
```

**STEP**: 2 — Create controller extension (customFilter.controller.js)

**DESCRIPTION**: Implement controller extension methods on your overview page controller extension. Required functions:
- getCustomFilters(): read values from fragment inputs and return a sap.ui.model.Filter (combined with AND) or undefined if none.
- getCustomAppStateDataExtension(oCustomData): store custom input values into the app state object (oCustomData).
- restoreCustomAppStateDataExtension(oCustomData): restore saved values from oCustomData back into the custom inputs.

Ensure sap.ui.model.Filter (or Filter from "sap/ui/model/Filter") is available/imported in your controller module.

**LANGUAGE**: JavaScript

**CODE**:
```javascript
getCustomFilters: function () {
    var oValue1 = this.oView.byId("ProductID").getValue();
    var oValue2 = this.oView.byId("SalesOrderID").getValue();
    var aFilters = [],
        oFilter1,
        oFilter2;

    if (oValue1) {
        oFilter1 = new Filter({
            path: "ProductID",
            operator: "EQ",
            value1: oValue1
        });
        aFilters.push(oFilter1);
    }

    if (oValue2) {
        oFilter2 = new Filter({
            path: "SalesOrderID",
            operator: "EQ",
            value1: oValue2
        });
        aFilters.push(oFilter2);
    }

    if (aFilters && aFilters.length > 0) {
        return new Filter(aFilters, true);
    }
}
```

**LANGUAGE**: JavaScript

**CODE**:
```javascript
getCustomAppStateDataExtension: function (oCustomData) {
    // The content of the custom field will be stored in the app state,
    // so that it can be restored later, for example after a back navigation.
    // The developer has to ensure that the content of the field is stored
    // in the object that is returned by this method.
    if (oCustomData) {
        var oCustomField1 = this.oView.byId("ProductID");
        var oCustomField2 = this.oView.byId("SalesOrderID");

        if (oCustomField1) {
            oCustomData.ProductID = oCustomField1.getValue();
        }
        if (oCustomField2) {
            oCustomData.SalesOrderID = oCustomField2.getValue();
        }
    }
}
```

**LANGUAGE**: JavaScript

**CODE**:
```javascript
restoreCustomAppStateDataExtension: function (oCustomData) {
            //in order to restore the content of the custom field in the filter bar, for example after a back navigation,
            //an object with the content is handed over to this method and the developer has to ensure that the content of the custom field is set accordingly
            //also, empty properties have to be set
            if (oCustomData) {

                if (oCustomData.ProductID) {
                    var oCustomField1 = this.oView.byId("ProductID");
                    oCustomField1.setValue(oCustomData.ProductID);
                }

                if (oCustomData.SalesOrderID) {
                    var oCustomField2 = this.oView.byId("SalesOrderID");
                    oCustomField2.setValue(oCustomData.SalesOrderID);
                }
            }

        },
```

**STEP**: 3 — Register fragment and controller in manifest.json

**DESCRIPTION**: Add your controller extension and viewExtension fragment under "extends" -> "extensions". Set sap.ovp global filter configuration (ensure the same entity type / set is used in viewExtensions and globalFilterEntitySet / globalFilterEntityType). Note: globalFilterEntityType is deprecated as of SAPUI5 1.54 — prefer globalFilterEntitySet.

Add the following sample configuration to manifest.json (update controllerName, fragmentName, and entity set/type to your app values).

**LANGUAGE**: JSON

**CODE**:
```json
"extends": {
    "extensions": {
        "sap.ui.controllerExtensions": {
            "sap.ovp.app.Main": {
                "controllerName": "my_app.ext.controller.OverViewPageExt"
            }
        },
        "sap.ui.viewExtensions": {
            "sap.ovp.app.Main": {
                "SmartFilterBarControlConfigurationExtension|GlobalFilters": {
                    "className": "sap.ui.core.Fragment",
                    "fragmentName": "sap.ovp.demo.ext.customFilter",
                    "type": "XML"
                }
            }
        }
    }
},
"sap.ovp": {
    "globalFilterModel": "salesOrder",
    "globalFilterEntityType": "GlobalFilters", //Deprecated since SAPUI5 1.54.
    "globalFilterEntitySet": "GlobalFilters",  //Available from SAPUI5 1.54.
    ...
}
```

fiori-elements-framework SAPUI5 OVP smartfilterbar filters manifest.json extension


**TITLE**: Adding Custom Input Fields to the Mass Edit Dialog (OData V2 & V4)

**INTRODUCTION**: Instructions and actionable code examples to add custom input fields to the Mass Edit (Mass Edit / Multi-Edit) dialog in SAP Fiori Elements for both OData V2 and OData V4. Includes manifest changes, fragment XML examples, and controller extension for custom save handling.

**TAGS**: fiori-elements-framework, mass-edit, multi-edit, manifest, sap.ui5, ViewExtension, customFragment, controller-extension, OData V2, OData V4

STEP: 1 — OData V2: Add MultiEditFieldsExtension via ViewExtension in manifest

DESCRIPTION:
- Add a ViewExtension entry named "MultiEditFieldsExtension|<Entity>" under sap.ui5 -> extends -> extensions -> sap.ui.viewExtensions in manifest.json.
- Controls in custom extensions must be surrounded by a GroupElement.
- Provide stable control IDs; handle input values via beforeMultiEditSaveExtension in the application extension/controller.
- ViewExtension is supported for both annotationPath and visibleTable column scenarios.
- Example fragmentName values show typical extension fragment module paths.

LANGUAGE: JSON

CODE:
```json
"sap.ui5": {
    "extends": {
        "extensions": {
            "sap.ui.viewExtensions":{
                "sap.suite.ui.generic.template.ListReport.view.ListReport":{
                    "MultiEditFieldsExtension|STTA_C_SO_SalesOrder_ND": {  // MultiEditFieldsExtension + Entity
                        "className": "sap.ui.core.Fragment",
                        "fragmentName": "STTA_SO_ND.ext.fragments.MultiEditFieldsExtension",
                        "type": "XML"
                    }
                }
            }
        }
    }
}
```

STEP: 2 — OData V2: MultiEdit + quickVariantSelectionX example (using key identifier)

DESCRIPTION:
- When using quickVariantSelectionX, append the key identifier to the MultiEditFieldsExtension fragment key: "MultiEditFieldsExtension|<Entity>|<key>".
- Enable multiEdit via tableSettings.multiEdit.enabled = true for the selected variant.
- Provide stable IDs in fragments and ensure GroupElement wrapping.

LANGUAGE: JSON

CODE:
```json
"sap.ui5": {
    "extends": {
        "extensions": {
            "sap.ui.viewExtensions":{
                "sap.suite.ui.generic.template.ListReport.view.ListReport":{
                    "MultiEditFieldsExtension|C_STTA_SalesOrder_WD_20|1": {  // Key identifier need to be added at the end along with pipe.
                        "className": "sap.ui.core.Fragment",
                        "fragmentName": "ManageSalesOrderWithTableTabs.ext.fragments.MultiEditFieldsExtension",
                        "type": "XML"
                        }
                    }
                }
            }
        }
    }
}
"sap.ui.generic.app": {
    "pages": [{
        "entitySet": " C_STTA_SalesOrder_WD_20",
        "component": {
            "name": "sap.suite.ui.generic.template.ListReport",
            "list": true,
            "settings": {
                "quickVariantSelectionX": {
                    "showCounts": true,
                    "variants": {
                        "0": {
                            "key": "1", // <-- key identifier
                            "annotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant#Expensive",
                            "tableSettings": {
                                "multiEdit":{
                                    "enabled":true
                                },
                            }
                        },
                    }
                }
            }
        }
    }]
}
```

STEP: 3 — OData V2: MultiEditFieldsExtension fragment XML

DESCRIPTION:
- Example fragment to be referenced by fragmentName in the manifest.
- Wrap controls inside a sap.ui.comp.smartform:GroupElement.
- Provide stable IDs for controls.

LANGUAGE: XML

CODE:
```xml
<core:FragmentDefinition xmlns:sfo="sap.ui.comp.smartform" xmlns:core="sap.ui.core" xmlns="sap.m">
    <sfo:GroupElement>
        <Label text="Rating Bar" labelFor="RI_default"/>
        <RatingIndicator id="RI_default" maxValue="5" class="sapUiSmallMarginBottom" value="4"/>
    </sfo:GroupElement>
</core:FragmentDefinition>
```

STEP: 4 — OData V4: Define customFragment in manifest.json

DESCRIPTION:
- For OData V4, define the custom fragment path using options -> settings -> controlConfiguration -> <entity>/@com.sap.vocabularies.UI.v1.LineItem -> tableSettings -> enableMassEdit -> customFragment.
- Controls in custom extensions must be surrounded by a FormContainer.
- Provide stable IDs for inputs so controller extension can access them.

LANGUAGE: JSON

CODE:
```json
"options": {
    "settings": {
        "contextPath": "/RootEntity",
        "variantManagement": "Control",
        "controlConfiguration": {
            "_Child/@com.sap.vocabularies.UI.v1.LineItem": {
                "tableSettings": {
                    "type": "GridTable",
                    "enableMassEdit":{
                        "customFragment": "sap.fe.core.fpmExplorer.ext.fragment.customMassEdit"
                    }
                }
            }
        }
    }
}
```

STEP: 5 — OData V4: custom fragment XML (FormContainer wrapper)

DESCRIPTION:
- Fragment referenced by customFragment must wrap controls inside a form:FormContainer and provide stable IDs for controls so the controller extension can read them.

LANGUAGE: XML

CODE:
```xml
<core:FragmentDefinition xmlns:form="sap.ui.layout.form" xmlns:core="sap.ui.core" xmlns="sap.m">
    <form:FormContainer>
        <form:formElements>
            <form:FormElement label="Execute with the custom Save">
                <form:fields>
                    <CheckBox id="myCheckBox"/>
                </form:fields>
            </form:FormElement>
        </form:formElements>
    </form:FormContainer>
</core:FragmentDefinition>
```

STEP: 6 — OData V4: Implement customMassEditSave in controller extension

DESCRIPTION:
- Implement customMassEditSave inside the list report or object page controller extension to replace/augment the default mass-edit save behavior.
- aParameters contains: aContexts (selected contexts) and oUpdateData (dictionary of propertyPath -> updated value).
- Use Core.byId("<fragmentPrefix>--<controlId>") to access controls inside the custom fragment. In this example, the fragment ID prefix is "customMassEdit" and checkbox id is "myCheckBox".
- If custom action is executed successfully, return true to prevent default mass-edit save; otherwise return false to allow default behavior.

LANGUAGE: JavaScript

CODE:
```javascript
sap.ui.define(
    [
        "sap/ui/core/mvc/ControllerExtension",
        "sap/m/MessageToast",
        "sap/ui/core/Core"
    ],
    function (ControllerExtension, Filter, FilterOperator, IllustratedMessage, IllustratedMessageType, MessageToast, Core) {
        "use strict";

        return ControllerExtension.extend("sap.fe.core.fpmExplorer.OPExtend", {
            override: {
                editFlow: {
                    customMassEditSave: async function (aParameters) {
                        // aParameters.aContexts: array containing the selected contexts for the mass edit
                        // aParameters.oUpdateData: a dictionary containing the propertyPath and its updated value

                        //Retrieve the control within the custom fragment
                        const isSelected = Core.byId("customMassEdit--myCheckBox").getSelected();

                        //Execute the custom save action if selected
                        if (isSelected){
                            await this.base.editFlow
                            .invokeAction("Service.customUpdateAction", {
                                contexts: aParameters.aContexts,
                                parameterValues: aParameters.oUpdateData
                            })
                            .catch(function (e){
                                MessageToast.show("Custom save failed");
                            });
                        }
                        //Do not execute the default mass-edit if custom save was selected
                        return isSelected;
                    }
                }
            }
        });
    }
);
```

fiori-elements-framework
mass-edit
multi-edit
manifest
sap.ui5
ViewExtension
customFragment
controller-extension
OData V2
OData V4


**TITLE**: Adding Custom Messages via securedExecution (SAP Fiori elements)

**INTRODUCTION**: This guide shows how to add and display custom transition messages from template extension code by using the extensionAPI securedExecution method together with the SAPUI5 Messaging API. Use these patterns to create persistent transition messages that the framework displays after a securedExecution busy session completes.

**TAGS**: fiori-elements, securedExecution, Messaging, sap.ui.core.Messaging, extensionAPI, odata-v2, odata-v4

**STEP**: Concept / Behavior Overview

**DESCRIPTION**: Use extensionAPI.securedExecution to start a busy session. At busy-session start, the framework removes previous transition messages. While the busy session runs, add messages to the SAPUI5 Messaging; when the busy session ends, collected transition messages are displayed automatically according to severity.

- Transition messages: create by setting persistent: true on sap.ui.core.message.Message. The Messaging API does not auto-delete transition messages; the Fiori elements framework deletes them at defined points (for example, at the start of a subsequent busy session).
- Non-transition messages: persistent: false messages are controlled by the Messaging API and are removed automatically when their bound entity is reloaded.
- Use mParameters.sActionLabel when calling securedExecution to set action label text; note that for a single transition info message sActionLabel may have no effect because a message toast is shown.

**LANGUAGE**: Text

**CODE**:
```text
Concept summary:
- Use extensionAPI.securedExecution(fnFunction, mParameters) to group operations into a busy session.
- Add messages during the busy session via sap.ui.core.Messaging.
- Use persistent: true on messages to make them transition messages (framework-controlled lifetime).
```

**STEP**: OData V2 — Async operation example (create a transition message after read)

**DESCRIPTION**: Wrap an asynchronous operation in a function that returns a Promise. Inside success callback, construct a sap.ui.core.message.Message with persistent: true and add it via Messaging.addMessages. Then call this.extensionAPI.securedExecution(fnFunction, mParameters). Import "Messaging" from "sap/ui/core/Messaging" where required.

- mParameters.sActionLabel: set to the UI label or custom text to indicate the action that triggered the busy session.

**LANGUAGE**: JavaScript

**CODE**:
```javascript
onCustomButtonPressed: function(oEvent){
  var oSource = oEvent.getSource();
  var oModel = oSource.getModel();
  var sBindingPath = ...; // binding path to retrieve some information
  var fnFunction = function(){
    return new Promise(function(fnResolve, fnReject){
      oModel.read(sBindingPath, {
        success: function(oResponse){
          var oMessage = new sap.ui.core.message.Message({
            message: "We have received the following response: " + oResponse,
            persistent: true, // create message as transition message
            type: sap.ui.core.MessageType.Success
          });
          //"Messaging" required from module "sap/ui/core/Messaging";
          Messaging.addMessages(oMessage);
          fnResolve();
        },
        error: fnReject
    });
  };
  var mParameters = {
        sActionLabel:  oSource.getText() // or "Your custom text"
   };
  this.extensionAPI.securedExecution(fnFunction, mParameters);
}
```

**STEP**: OData V2 — Immediate message (no asynchronous operation)

**DESCRIPTION**: If no async operation is required, still wrap the message addition in a Promise-returning function and call securedExecution. This ensures messaging behavior (removed at busy-session start and displayed after end) is consistent.

**LANGUAGE**: JavaScript

**CODE**:
```javascript
onCustomButtonPressed: function(oEvent){
    var oSource = oEvent.getSource();
    var fnAddMessage = function(){
    return new Promise(function(fnResolve){
          var oMessage = new sap.ui.core.message.Message({
            message: "Custom button was pressed",
            persistent: true, // create message as transition message
            type: sap.ui.core.MessageType.Success
          });
          //"Messaging" required from module "sap/ui/core/Messaging";
          Messaging.addMessages(oMessage);
          fnResolve();
   });
};
var mParameters = {
      sActionLabel:  oSource.getText() // or "Your custom text"
 };
this.extensionAPI.securedExecution(fnAddMessage, mParameters);
}
```

**STEP**: OData V4 — securedExecution example (adding a transition message)

**DESCRIPTION**: Use the editFlow.securedExecution wrapper in OData V4 flows. Create a new Message from "sap/ui/core/message/Message" and add it using Messaging.addMessages. For OData V4 messages: both code and target are mandatory; provide an empty string for target when there is no binding target. Import "Messaging" from "sap/ui/core/Messaging" and "Message" from "sap/ui/core/message/Message".

**LANGUAGE**: JavaScript

**CODE**:
```javascript
securedExecution: function(oContext, aSelectedContexts) {
   this.editFlow.securedExecution(
      function() {
         // "Messaging" required from module "sap/ui/core/Messaging"
         // "Message" required from module "sap/ui/core/message/Message"
         Messaging.addMessages(new Message({
            message: "Server says hello",
            target: "",
            persistent: true,
            type: sap.ui.core.MessageType.Success,
            code: "123"
         }));
      }
   );
},
```

**STEP**: Notes, Tips, and References

**DESCRIPTION**:
- persistent: true = transition message; lifecycle controlled by framework deletions (e.g., at busy session start).
- persistent: false = lifecycle controlled by Messaging; removed when bound entity is reloaded.
- sActionLabel: used to label the action for the busy session; for a single transition info message it may have no visible effect because a toast is shown.
- OData V4 messages: both code and target are mandatory. If no target exists, set target: "" (empty string).
- When adding messages, ensure the Messaging module (sap/ui/core/Messaging) is required/imported and, for individual messages, sap/ui/core/message/Message is required.

Related documents:
- Using the SecuredExecution Method: using-the-securedexecution-method-6a39150.md
- Using Messages: using-messages-239b192.md

**LANGUAGE**: Text

**CODE**:
```text
Related links:
- using-the-securedexecution-method-6a39150.md
- using-messages-239b192.md
```


**TITLE**: Adding Dynamic Side Content to Object Page Sections (SAP Fiori elements)

**INTRODUCTION**: This guide shows how to add dynamic side content to Object Page sections and subsections in SAP Fiori elements (OData V2 and V4). It explains manifest entries, fragment/view usage, equalSplit behavior, i18n keys for toggle button texts, and the Extension API call to programmatically show/hide side content. Use app extensions only when annotations or manifest settings cannot provide the required behavior and always use the SAP Fiori elements extensionAPI.

**TAGS**: fiori-elements-framework, side-content, object-page, manifest, xmlfragment, extensionpoints, odata-v2, odata-v4, extensionAPI

STEP: 1 — OData V2: Add side content via viewExtensions in manifest.json

DESCRIPTION: Use the view extension points BeforeMainContent or AfterMainContent to inject a fragment or view into a subsection's side content. In manifest.json use a key formed as "<Placement>|<EntitySet>|<Annotation_Information_of_Subsection>". Provide className, fragmentName (or viewName), type, and optional equalSplit (true = 50:50). If equalSplit is false or omitted, the layout is responsive and depends on device size.

- Placement options: BeforeMainContent, AfterMainContent
- Key format: <Placement>|<EntitySet>|<Annotation_Info_of_Subsection>
- Properties: className, fragmentName (or viewName), type, equalSplit (optional, boolean)

LANGUAGE: JSON

CODE:
```json
"extends": {
   "extensions": {
         "sap.ui.viewExtensions": {
                 "sap.suite.ui.generic.template.ObjectPage.view.Details": {
                           "AfterMainContent|STTA_C_MP_Product|GeneralInformationForm":{
               "className": "sap.ui.core.Fragment",
               "fragmentName": "STTA_MP.ext.fragments.SideContentExtension",
               "type": "XML",
		"equalSplit": true
               },
"BeforeMainContent|STTA_C_MP_Product|to_ProductSalesData::com.sap.vocabularies.UI.v1.Chart":{
              "className": "sap.ui.core.Fragment",
              "fragmentName": "STTA_MP.ext.fragments.SideContentExtension",
              "type": "XML"
               }
                        }
           }
     }
}
```

STEP: 2 — OData V2: Provide custom toggle button text via i18n

DESCRIPTION: After adding side content, the subsection toolbar shows a toggle button with default text "Show Details" / "Hide Details". To override texts per entity set and subsection, add keys to the i18n.properties for the object page's entity set. Replace separators (--, ::, etc.) with | when forming the key.

- Show button key format:
  - ShowSideContent|<EntitySet>|<Annotation Info of the Subsection>
  - Examples:
    - ShowSideContent|STTA_C_MP_Product|to_ProductSalesData|com.sap.vocabularies.UI.v1.Chart
    - ShowSideContent|STTA_C_MP_Product|GeneralInformationForm
- Hide button key format:
  - HideSideContent|<EntitySet>|<Annotation Info of the Subsection>
  - Examples:
    - HideSideContent|STTA_C_MP_Product|to_ProductSalesData|com.sap.vocabularies.UI.v1.Chart
    - HideSideContent|STTA_C_MP_Product|GeneralInformationForm

LANGUAGE: text

CODE:
```text
i18n.properties entries sample:
ShowSideContent|STTA_C_MP_Product|GeneralInformationForm=Show Details (custom)
HideSideContent|STTA_C_MP_Product|GeneralInformationForm=Hide Details (custom)
```

STEP: 3 — OData V4: Define sideContent in manifest (sections and subSections)

DESCRIPTION: For OData V4, define side content under the routing targets -> options -> settings -> content -> body -> sections for section-level or subsection-level sideContent. Use "template" to point to an xmlfragment module path. Optionally set equalSplit: true for 50:50 layout.

LANGUAGE: JSON

CODE:
```json
{
   "sap.ui5": {
      "routing": {
         "targets": {
            "SalesOrderManageObjectPage": {
               "options": {
                  "settings": {
                     "content": {
                        "body": {
                           "sections": {
                              "<section-key>": {
                                 "sideContent": {
                                    "template": "SalesOrder.ext.CustomSideContent"
                                 }
                              },
                              "<section-key>": {
                                 "subSections": {
                                    "<subSection-key>": {
                                       "sideContent": {
                                          "template": "SalesOrder.ext.CustomSideContent",
                                          "equalSplit": true
                                       }
                                    }
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
}
```

STEP: 4 — Side Content settings (properties and summary)

DESCRIPTION: Side content is implemented as an XML fragment loaded via Fragment.load semantics. Properties:

- template (string): module path to fragment (same syntax as Fragment.load)
- equalSplit (boolean, default false): when true, side content and main content are shown in a 1:1 split regardless of responsive breakpoints. Default/fallback is responsive split (1:3, 1:2 depending on device).

For layout API details see sap.ui.layout.DynamicSideContent.

LANGUAGE: text

CODE:
```text
Properties:
- template: "<module_path_to_a_fragment>"   // fragment module path, e.g. "SalesOrder.ext.CustomSideContent"
- equalSplit: true | false                  // true => 50:50 split; default false => responsive split
```

STEP: 5 — Toggle side content programmatically (showSideContent API)

DESCRIPTION: Use the Extension API method showSideContent("<SideContent-Key>", [boolean]) from within extension code to toggle side content. If the boolean parameter is omitted the API toggles the current state. Provide a controlConfiguration action in manifest to call an extension function that invokes this.showSideContent("<SideContent-Key>").

LANGUAGE: JSON

CODE:
```json
{
   "sap.ui5": {
      "routing": {
         "targets": {
            "SalesOrderManageObjectPage": {
               "options": {
                  "settings": {
                     "content": {
                        "controlConfiguration": {
                           "_Item/@com.sap.vocabularies.UI.v1.LineItem": {
                              "actions": {
                                 "CustomActionToggleSideContent": {
                                  "press": "Namespace.ext.CustomSectionSideContent.toggleSideContent",
                                  "text": "Show/Hide Side Content",
                                  "enabled": true,
                                  "visible": true
                               }
                            }
                         }
                      }
                   }
                }
             }
          }
       }
    }
}
```

LANGUAGE: JavaScript

CODE:
```javascript
// webapp/ext/customSectionSideContent.js
sap.ui.define([], function() {
   "use strict";

   return {
      toggleSideContent: function(oBindingContext) {
         this.showSideContent("<SideContent-key>");
      }
   };
});
```

Usage examples:
- Toggle: this.showSideContent("<SideContent-key>")
- Show explicitly: this.showSideContent("customerInfo", true)
- Hide explicitly: this.showSideContent("customerInfo", false)

STEP: 6 — Determining Section-Key / SubSection-Key / SideContent-Key

DESCRIPTION: SideContent keys are derived from facet nesting and the facet keys at their respective levels. General rules:
- Section-Key: key of the section/facet at the level where you declare sideContent.
- SubSection-Key: key of the subsection (if any); usually matches Section-Key for single-level facets.
- SideContent-Key: the key you use when invoking showSideContent(...) — typically the same as the SubSection-Key or Section-Key depending on nesting.
- Replace annotation separators (e.g., ::, --) with | when forming keys for manifest or i18n.
- Tip: Scenario 4.1 is special — SubSection-Key and SideContent-Key differ (use the specific facet keys shown in your app’s facet/annotation structure).

Examples (from earlier snippets):
- "SalesOrderItems" as Section-Key and SideContent-Key for a reference facet
- For a subsection defined by annotation to_ProductSalesData::com.sap.vocabularies.UI.v1.Chart use:
  - before/after placement key: BeforeMainContent|STTA_C_MP_Product|to_ProductSalesData::com.sap.vocabularies.UI.v1.Chart
  - replace separators with | where needed in i18n keys or other unique keys

LANGUAGE: text

CODE:
```text
Key formation rules summary:
- For V2 extension point key: "<Placement>|<EntitySet>|<AnnotationInfo>"
- For i18n and show/hide keys: replace separators with "|" and use:
  ShowSideContent|<EntitySet>|<AnnotationInfo>
  HideSideContent|<EntitySet>|<AnnotationInfo>
- SideContent-Key for this.showSideContent(...) = Section-Key or SubSection-Key depending on facet nesting
```

STEP: 7 — UX recommendations and constraints

DESCRIPTION: Best practices and restrictions for dynamic side content:
- Use dynamic side content only for small previews (not as an extension of the main panel).
- Do not use tables inside side content.
- Avoid content that causes horizontal scrolling.
- For best visualization, enable equalSplit (50% screen) for side content when appropriate.

LANGUAGE: text

CODE:
```text
UX rules:
- Small preview content only
- No tables in side content
- Avoid horizontal scroll
- Consider equalSplit: true for better visualization (50%:50%)
```

fiori-elements-framework side-content object-page manifest xmlfragment extensionpoints odata-v2 odata-v4 extensionAPI


**TITLE**: Add a filterable Price field to the Smart Filter Bar (Fiori elements, OData V2)

**INTRODUCTION**: This guide shows the exact files, code, and manifest changes to add a custom Price filter (ComboBox with two options) to a List Report smart filter bar in an SAP Fiori elements (OData V2) app. It uses an app extension: a fragment for ControlConfiguration and a controller for filter logic that injects filters into the table binding before rebind.

**TAGS**: fiori-elements-framework, sapui5, smart-filter-bar, odata-v2, extension, i18n, manifest

**STEP**: 1 — Create fragment and controller files (folder layout and files)
**DESCRIPTION**: Create the extension folder structure and files under your webapp. Place the UI fragment for the ControlConfiguration (ComboBox with items) and the controller implementing onBeforeRebindTableExtension. Use these exact file names/locations (adjust namespace/project name if different).
- Create folder: webapp/ext/fragment
- Create file: webapp/ext/fragment/Custom.Filter.fragment.xml
- Create folder: webapp/ext/controller
- Create file: webapp/ext/controller/Custom.Filter.controller.js

LANGUAGE: XML
CODE:
```xml
<core:FragmentDefinition
		xmlns="sap.m"
		xmlns:smartfilterbar="sap.ui.comp.smartfilterbar"
		xmlns:core="sap.ui.core">
	<!-- Price Filter-->
	<smartfilterbar:ControlConfiguration key="CustomPriceFilter" index="3"
	label="{i18n|sap.suite.ui.generic.template.ListReport|SEPMRA_C_PD_Product>xfld.Price}"
							visibleInAdvancedArea="true" groupId="_BASIC">
		<smartfilterbar:customControl>
			<ComboBox id="CustomPriceFilter-combobox">
				<core:Item id="CustomPriceFilterItem0" key="0"
						text="{i18n|sap.suite.ui.generic.template.ListReport|SEPMRA_C_PD_Product>xtit.Price_0-100}"/>
				<core:Item id="CustomPriceFilterItem3" key="1"
						text="{i18n|sap.suite.ui.generic.template.ListReport|SEPMRA_C_PD_Product>xtit.Price_GE100}"/>
			</ComboBox>
		</smartfilterbar:customControl>
	</smartfilterbar:ControlConfiguration>
</core:FragmentDefinition>
```

LANGUAGE: JavaScript
CODE:
```javascript
sap.ui.define([], function () {
    return {
        onBeforeRebindTableExtension: function(oEvent) {
            var oBindingParams = oEvent.getParameter("bindingParams");
            oBindingParams.parameters = oBindingParams.parameters || {};

            var oSmartTable = oEvent.getSource();
            var oSmartFilterBar = this.byId(oSmartTable.getSmartFilterId());
            var vCategory;
            if (oSmartFilterBar instanceof sap.ui.comp.smartfilterbar.SmartFilterBar) {
                //Custom price filter
                var oCustomControl = oSmartFilterBar.getControlByKey("CustomPriceFilter");
                if (oCustomControl instanceof sap.m.ComboBox) {
                    vCategory = oCustomControl.getSelectedKey();
                    switch (vCategory) {
                        case "0":
                            oBindingParams.filters.push(new sap.ui.model.Filter("Price", "LE", "100"));
                            break;
                        case "1":
                            oBindingParams.filters.push(new sap.ui.model.Filter("Price", "GT", "100"));
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }
});
```

**STEP**: 1 — Implementation notes (behavior & change events)
**DESCRIPTION**: Ensure the Smart Filter Bar recognizes changes in the custom control. If your custom control does not automatically fire a change event that the Smart Filter Bar listens to, explicitly call .fireChange() on that control after selection changes so the filter bar recognizes the state change and the table overlay appears.

LANGUAGE: JavaScript
CODE:
```javascript
// Example: fireChange on the ComboBox after programmatic selection
var oCombo = this.byId("CustomPriceFilter-combobox");
oCombo.setSelectedKey("1");
oCombo.fireChange(); // ensures SmartFilterBar recognizes the change
```

**STEP**: 2 — Add i18n property entries
**DESCRIPTION**: Add the translatable label and option texts to your i18n properties file (webapp/i18n/i18n.properties). These keys are referenced by the fragment above.
LANGUAGE: Properties
CODE:
```
#XFLD: Custom filter breakout label
xfld.Price=Price
#XTIT: Price range 0-100
xtit.Price_0-100=Price between 0-100
#XTIT: Price range Over 100
xtit.Price_GE100=Price: Over 100
```

**STEP**: 3 — Register the fragment and controller as extensions in manifest.json
**DESCRIPTION**: Integrate the fragment and controller by adding controller and view extensions under the ListReport extend section of webapp/manifest.json. Ensure the fragmentName and controllerName values match your project namespace and file paths. The manifest snippet below shows how to:
- register the controller extension for the ListReport controller
- register the view extension that adds the ControlConfiguration fragment into the Smart Filter Bar

LANGUAGE: JSON
CODE:
```json
"extends": {
        "extensions": {
                "sap.ui.controllerExtensions": {
                        "sap.suite.ui.generic.template.ListReport.view.ListReport": {
                                "controllerName": "my_app.ext.controller.ListReportExtension"
                        }
                },
                "sap.ui.viewExtensions": {
                        "sap.suite.ui.generic.template.ListReport.view.ListReport": {
                                "SmartFilterBarControlConfigurationExtension|SEPMRA_C_PD_Product": {
                                        "className": "sap.ui.core.Fragment",
                                        "fragmentName": "ManageProducts.ext.fragment.CustomFilter",
                                        "type": "XML"
                                }
                        }
                }
        }
},
```

**STEP**: 3 — Manifest notes (matching file names)
**DESCRIPTION**: The fragmentName in the manifest must match your fragment module path and name (namespace + fragment name without the .fragment.xml extension). If your fragment file is named Custom.Filter.fragment.xml, set fragmentName accordingly when using your namespace (for example: "YourAppNamespace.ext.fragment.Custom.Filter"). If you rename the file to CustomFilter.fragment.xml, adjust the fragmentName in manifest.json to "YourAppNamespace.ext.fragment.CustomFilter".

LANGUAGE: JSON
CODE:
```json
// Example: if file is webapp/ext/fragment/Custom.Filter.fragment.xml
"fragmentName": "YourAppNamespace.ext.fragment.Custom.Filter"

// Example: if file is webapp/ext/fragment/CustomFilter.fragment.xml
"fragmentName": "YourAppNamespace.ext.fragment.CustomFilter"
```

**RESULTS**: After deploying these changes, the List Report smart filter bar will display a new Price filter with two selectable options. The controller's onBeforeRebindTableExtension injects the corresponding Price filter into the table binding so results update accordingly. Remember to follow the extensionAPI guidelines: use only the supported extensionAPI and avoid manipulating framework-internal objects directly.


**TITLE**: Add Multiple Fields (IDs, Descriptions, Actions) to One Column in Responsive Tables via UI.FieldGroup

**INTRODUCTION**: Use UI.FieldGroup to group multiple annotations (IDs, descriptions, action buttons, navigation, annotations) into a single responsive table column. Define the FieldGroup with a Data collection, then reference it from UI.LineItem as a DataFieldForAnnotation (AS_FIELDGROUP) to render multiple fields in one column.

**TAGS**: fiori-elements-framework, UI.FieldGroup, UI.LineItem, annotations, OData, XML, ABAP CDS, CAP CDS, responsive-tables, export, personalization

**STEP**: 1 — Define UI.FieldGroup (XML annotation)

**DESCRIPTION**: Create a UI.FieldGroup annotation with qualifier (e.g., FieldGroup1). Inside Data, add UI.DataField, UI.DataFieldForAction, or other supported annotation records to show multiple items in one column.

**LANGUAGE**: XML

**CODE**:
```xml
<Annotation Term="UI.FieldGroup" Qualifier="FieldGroup1">
    <Record>
        <PropertyValue Property="Data">
            <Collection>
                <Record Type="UI.DataField">
                    <PropertyValue Property="Value" Path="ContractType"/>
                </Record>
                <Record Type="UI.DataFieldForAction">
                    <PropertyValue Property="Label" String="Dummy Button"/>
                    <PropertyValue Property="Action" String="com.sap.gateway.srvd.sadl_gw_appmusicdr_definition.v0001.SOME_ACTION_2"/>
                </Record>
            </Collection>
        </PropertyValue>
    </Record>
</Annotation>
```

**STEP**: 2 — Define UI.FieldGroup (ABAP CDS annotation)

**DESCRIPTION**: Equivalent FieldGroup in ABAP CDS. Use @UI.fieldGroup qualifiers on properties to semantically group them into the same column.

**LANGUAGE**: ABAP CDS

**CODE**:
```

@UI.fieldGroup: [
  {
    value: 'CONTRACTTYPE',
    type: #STANDARD,
    position: 1 ,
    qualifier: 'FieldGroup1'
  }
]
CONTRACTTYPE;

@UI.fieldGroup: [
  {
    label: 'Dummy Button',
    dataAction: 'PUSHDOWN:com.sap.gateway.srvd.sadl_gw_appmusicdr_definition.v0001.SOME_ACTION_2',
    type: #FOR_ACTION,
    position: 2 ,
    qualifier: 'FieldGroup1'
  }
]
contractname;
```

**STEP**: 3 — Define UI.FieldGroup (CAP CDS annotation)

**DESCRIPTION**: CAP CDS syntax for FieldGroup; include Data array with UI.DataField and UI.DataFieldForAction items.

**LANGUAGE**: CAP CDS

**CODE**:
```

UI.FieldGroup #FieldGroup1 : {
    Data : [
        {
            $Type : 'UI.DataField',
            Value : ContractType
        },
        {
            $Type : 'UI.DataFieldForAction',
            Label : 'Dummy Button',
            Action : 'com.sap.gateway.srvd.sadl_gw_appmusicdr_definition.v0001.SOME_ACTION_2'
        }
    ]
}
```

**STEP**: 4 — Reference FieldGroup from UI.LineItem (XML annotation)

**DESCRIPTION**: In UI.LineItem, reference the FieldGroup qualifier using DataFieldForAnnotation and Target = @UI.FieldGroup#Qualifier. This renders the grouped items in one table column and uses the provided label.

**LANGUAGE**: XML

**CODE**:
```xml
<Annotation Term="UI.LineItem">
    <Collection>
        …………
        …………
        <Record Type="UI.DataFieldForAnnotation">
            <PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#FieldGroup1" />
            <PropertyValue Property="Label" String="Type Information"/>
        </Record>
        …………
        …………
    </Collection>
</Annotation>
```

**STEP**: 5 — Reference FieldGroup from UI.LineItem (ABAP CDS annotation)

**DESCRIPTION**: ABAP CDS lineItem uses type #AS_FIELDGROUP and valueQualifier referencing the FieldGroup qualifier to include the grouped fields in the LineItem.

**LANGUAGE**: ABAP CDS

**CODE**:
```

@UI.lineItem: [
  {
    label: 'Type Information',
    type: #AS_FIELDGROUP,
    valueQualifier: 'FieldGroup1'
  }
]
producttype;
```

**STEP**: 6 — Reference FieldGroup from UI.LineItem (CAP CDS annotation)

**DESCRIPTION**: CAP CDS LineItem should include a DataFieldForAnnotation with Target pointing to the FieldGroup qualifier.

**LANGUAGE**: CAP CDS

**CODE**:
```

UI.LineItem : [
    {
        $Type : 'UI.DataFieldForAnnotation',
        Target : '@UI.FieldGroup#FieldGroup1',
        Label : 'Type Information'
    }
]
```

**STEP**: 7 — Supported annotations inside UI.FieldGroup

**DESCRIPTION**: Use these annotation types inside UI.FieldGroup.Data collection to compose the semantically grouped column.

**LANGUAGE**: List

**CODE**:
```text
Allowed in UI.FieldGroup:
- UI.DataField
- UI.DataFieldForAction
- UI.DataFieldForIntentBasedNavigation
- UI.DataFieldForAnnotation (can include Communication.Contact, UI.Visualization/Rating, UI.Visualization/Progress)
```

**STEP**: 8 — Additional annotations allowed in OData V2

**DESCRIPTION**: In SAP Fiori elements for OData V2 you can also include DataFieldWithNavigationPath and UI.Chart inside UI.FieldGroup.

**LANGUAGE**: List

**CODE**:
```text
Additional for OData V2:
- UI.DataFieldWithNavigationPath
- UI.DataFieldForAnnotation (UI.Chart)
```

**STEP**: 9 — Export & Personalization restrictions to consider

**DESCRIPTION**: Implementers must be aware of export and personalization limitations when grouping fields into one column.

**LANGUAGE**: List

**CODE**:
```text
Restrictions:
- OData V2 Excel export: Only the first field (first visible field) in the semantically connected column is exported.
- OData V4 Excel export: None of the fields in the semantically connected column are exported.
- Table personalization: Users can only filter, sort, and group on the individual properties included in the UI.FieldGroup (properties must belong to the same entity type as the UI.LineItem). Example: A column "Combined" made of Height and Weight cannot be personalized; users can only personalize on Height and Weight individually.
```

**STEP**: 10 — TextArrangement behavior when ID and text shown in same column

**DESCRIPTION**: If a property and its sap:text property are included in the same FieldGroup column, TextArrangement annotation is ignored and defaults to idOnly. For all other cases, textArrangement can be used to control ID/description display.

**LANGUAGE**: Note

**CODE**:
```text
Note:
- When a property and its sap:text property are shown in the same column (same FieldGroup), TextArrangement is not applicable; default is idOnly.
- Otherwise, use textArrangement to control whether to show ID, description, or both.
```

fiori-elements-framework
UI.FieldGroup
UI.LineItem
annotations
OData
XML
ABAP CDS
CAP CDS
responsive-tables
export
personalization


**TITLE**: Adding Segmented Buttons to a Table Toolbar (SelectionVariant + manifest.quickVariantSelection)

**INTRODUCTION**: This guide shows how to add segmented buttons (or a selection box) to a Fiori Elements table toolbar by defining SelectionVariant annotations and configuring quickVariantSelection in the manifest. Each segmented button (or select entry) is bound to a SelectionVariant that filters the table when selected. Includes XML, ABAP CDS, and CAP CDS examples plus manifest settings for OData V2 and V4.

**TAGS**: fiori-elements, selectionVariant, quickVariantSelection, manifest.json, OData V2, OData V4, CAP, ABAP, XML, JSON, table-toolbar

**STEP**: 1 — Define SelectionVariant annotations (XML)
**DESCRIPTION**: Create SelectionVariant annotations in your metadata/service annotations to define filter variants. Use a simple variant (one property) or a complex variant (multiple properties). Note: If SelectionVariant contains multiple SelectOptionType entries, filters targeting the same property are combined with OR; filters for different properties are combined with AND.
**LANGUAGE**: XML
**CODE**:
```xml
<Annotation Term="UI.SelectionVariant" Qualifier="SimpleFilter">
      <Record>
         <PropertyValue Property="Text" String="Tax amount less than 10 USD" />
         <PropertyValue Property="SelectOptions">
            <Collection>
               <Record Type="UI.SelectOptionType">
                  <PropertyValue Property="PropertyName" PropertyPath="tax_amount" />
                  <PropertyValue Property="Ranges">
                     <Collection>
                        <Record Type="UI.SelectionRangeType">
                           <PropertyValue Property="Sign" EnumMember="UI.SelectionRangeSignType/I" />
                           <PropertyValue Property="Option" EnumMember="UI.SelectionRangeOptionType/LT" />
                           <PropertyValue Property="Low" String="10" />
                        </Record>
                     </Collection>
                  </PropertyValue>
               </Record>
            </Collection>
         </PropertyValue>
      </Record>
   </Annotation>
   <Annotation Term="UI.SelectionVariant" Qualifier="ComplexFilter">
      <Record>
         <PropertyValue Property="Text" String="Net Amount between 10 and 40 And Gross Amount Less than 100 USD" />
         <PropertyValue Property="SelectOptions">
            <Collection>
               <Record Type="UI.SelectOptionType">
                  <PropertyValue Property="PropertyName" PropertyPath="net_amount" />
                  <PropertyValue Property="Ranges">
                     <Collection>
                        <Record>
                           <PropertyValue Property="Sign" EnumMember="com.sap.vocabularies.UI.v1.SelectionRangeSignType/I" />
                           <PropertyValue Property="Option" EnumMember="UI.SelectionRangeOptionType/BT" />
                           <PropertyValue Property="Low" String="10" />
                           <PropertyValue Property="High" String="40" />
                        </Record>
                     </Collection>
                  </PropertyValue>
               </Record>
               <Record Type="UI.SelectOptionType">
                  <PropertyValue Property="PropertyName" PropertyPath="gross_amount" />
                  <PropertyValue Property="Ranges">
                     <Collection>
                        <Record Type="UI.SelectionRangeType">
                           <PropertyValue Property="Option" EnumMember="UI.SelectionRangeOptionType/LT" />
                           <PropertyValue Property="Sign" EnumMember="UI.SelectionRangeSignType/I" />
                           <PropertyValue Property="Low" String="100" />
                        </Record>
                     </Collection>
                  </PropertyValue>
               </Record>
            </Collection>
         </PropertyValue>
      </Record>
   </Annotation>
```

**STEP**: 2 — Define SelectionVariant annotations (ABAP CDS)
**DESCRIPTION**: Alternative: define selection variants in ABAP CDS using @UI.selectionVariant. Each entry includes text, qualifier, and filter expression. Use qualifier values to reference these variants from the manifest.
**LANGUAGE**: ABAP CDS
**CODE**:
```
@UI.selectionVariant: [
  {
    text: 'Tax amount less than 10 USD',
    qualifier: 'SimpleFilter',
    filter: 'tax_amount LT 10'
  }
]

@UI.selectionVariant: [
  {
    text: 'Net Amount between 10 and 40 And Gross Amount Less than 100 USD',
    qualifier: 'ComplexFilter',
    filter: 'net_amount GE 10 and net_amount LE 40 and gross_amount LT 100 '
  }
]
```

**STEP**: 3 — Define SelectionVariant annotations (CAP CDS)
**DESCRIPTION**: Alternative: define UI.SelectionVariant entries in CAP CDS. Use qualifiers (#SimpleFilter, #ComplexFilter) and SelectOptions arrays to reference in the manifest.
**LANGUAGE**: CDS
**CODE**:
```
UI.SelectionVariant #SimpleFilter : {
    Text : 'Tax amount less than 10 USD',
    SelectOptions : [
        {
            $Type : 'UI.SelectOptionType',
            PropertyName : tax_amount,
            Ranges : [
                {
                    $Type : 'UI.SelectionRangeType',
                    Sign : #I,
                    Option : #LT,
                    Low : '10'
                }
            ]
        }
    ]
},
UI.SelectionVariant #ComplexFilter : {
    Text : 'Net Amount between 10 and 40 And Gross Amount Less than 100 USD',
    SelectOptions : [
        {
            $Type : 'UI.SelectOptionType',
            PropertyName : net_amount,
            Ranges : [
                {
                    Sign : #I,
                    Option : #BT,
                    Low : '10',
                    High : '40'
                }
            ]
        },
        {
            $Type : 'UI.SelectOptionType',
            PropertyName : gross_amount,
            Ranges : [
                {
                    $Type : 'UI.SelectionRangeType',
                    Option : #LT,
                    Sign : #I,
                    Low : '100'
                }
            ]
        }
    ]
}
```

**STEP**: 4 — Configure quickVariantSelection in manifest.json for OData V2 (Object Page)
**DESCRIPTION**: Add quickVariantSelection under the relevant section inside component.settings.sections.<SectionID>.quickVariantSelection. Set showCounts to true to display counts next to segmented buttons (this triggers count calls). Define variants as entries with key and annotationPath referencing the SelectionVariant qualifier (com.sap.vocabularies.UI.v1.SelectionVariant#Qualifier). If you define <= 3 variants a segmented button is rendered; 4+ renders a selection box.
**LANGUAGE**: JSON
**CODE**:
```json
"component": {
  "name": "sap.suite.ui.generic.template.ObjectPage",
  "settings": {
    "sections": {
      "SalesOrderItemsID": {
        "navigationProperty": "to_Item",
        "entitySet": "C_STTA_SalesOrderItem_WD_20",
        "createMode": "inline",
        "quickVariantSelection": {
          "showCounts": true,
          "variants": {
            "0": {
              "key": "_tab2",
              "annotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant#SimpleFilter"
            },
            "1": {
              "key": "_tab3",
              "annotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant#ComplexFilter"
            }
          }
        }
      }
    },
    "showRelatedApps": true
  }
}
```

**STEP**: 5 — Configure quickVariantSelection in manifest.json for OData V4 (ControlConfiguration / Table)
**DESCRIPTION**: For OData V4 / sap.fe.templates.ObjectPage, configure controlConfiguration for the relevant table (e.g., _Item/@com.sap.vocabularies.UI.v1.LineItem). Use tableSettings.quickVariantSelection.paths to list annotationPath entries (one per variant). Set showCounts (if true, counts appear next to buttons and not table title) and hideTableTitle when you want to display only the segmented buttons/selection box (use hideTableTitle only if SelectionVariant Text is self-explanatory). Segmented button rendering rule: <= 3 entries -> segmented buttons, 4+ -> selection box.
**LANGUAGE**: JSON
**CODE**:
```json
"SalesOrderManageObjectPage": {
	"type": "Component",
	"id": "SalesOrderManageObjectPage",
	"name": "sap.fe.templates.ObjectPage",
	"options": {
		"settings": {
			"contextPath": "/SalesOrderManage",
			"navigation": {
	           ...
			},
			"controlConfiguration": {
				"_Item/@com.sap.vocabularies.UI.v1.LineItem": {
					"tableSettings": {
						"type": "GridTable",
						"quickVariantSelection": {
							"paths": [
								{
									"annotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant#SimpleFilter"
								},
								{
									"annotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant#ComplexFilter"
								}
							],
							"showCounts": true,
							"hideTableTitle": true
						},
	                  ...
					}
				}
			}
		}
	}
}
```

**STEP**: 6 — Behavior and performance notes
**DESCRIPTION**: Important behavior details to apply during implementation:
- Rendering rule: number of variants <= 3 -> segmented buttons; 4 or more -> selection box.
- Counts:
  - OData V2: set showCounts:false to avoid count calls and not show numbers in button text. If showCounts:true, initial load triggers count calls (two calls are made to determine record count).
  - OData V4: showCounts defaults to false; set to true to show counts next to buttons (counts then not shown next to table title).
- Combining filters: within a SelectionVariant, multiple SelectOptionType entries targeting the same property are OR'ed; different properties are AND'ed.
- hideTableTitle: set to true when you want only the segmented buttons/selection box visible and the SelectionVariant Text is self-explanatory (recommended only if titles are sufficient).

Images showing UI states (optional assets):
- images/Segmented_button_1_selected_65fc70c.png
- images/Segmented_button_2_selected_60bfbdb.png

fiori-elements
selectionVariant
quickVariantSelection
manifest.json
OData V2
OData V4
CAP
ABAP
XML
JSON
table-toolbar