| name | fiori-flexible-programming |
| description | Use when working with SAP Fiori Elements flexible programming model FPM: building blocks macros:Table, macros:FilterBar macros:Chart macros:Field, extension points, custom section, custom column, controller extension, ControllerExtension.extend, custom action, sap.fe.macros namespace, OData V4 extension.
|
| metadata | {"category":"fiori","version":"1.0.0","keywords":["FPM","flexible programming model","building block","macros:Table","macros:FilterBar","macros:Chart","extension point","custom section","controller extension","sap.fe.macros"],"related":{"fiori-annotations":"base annotations required before extending","fiori-elements-floorplans":"the floorplan being extended","fiori-navigation":"navigate to custom sections"}} |
Fiori Flexible Programming Model — Best Practices
Primary reference: https://ui5.sap.com/#/topic/549c8e9c70ce4715bdacde98e24c0b55
FPM Explorer: https://sapui5.hana.ondemand.com/test-resources/sap/fe/core/fpmExplorer/index.html
Only for OData V4 — the flexible programming model is not available for V2
The flexible programming model (FPM) removes the old choice between "pure Fiori Elements"
and "freestyle UI5". You use the Fiori Elements framework as the base and extend exactly
what you need — nothing more.
Three extension levels, in order of preference:
- Building Blocks — reuse Fiori Elements components in your own views
- Extension Points — add custom sections, columns, or actions via manifest.json
- Controller Extensions — override or augment standard lifecycle/edit-flow behavior
Building Blocks — use Fiori Elements components in custom views
Building blocks are the recommended way to extend. They keep framework compliance
(draft handling, navigation, personalization) while letting you add custom UI.
Table Building Block
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:macros="sap.fe.macros">
<macros:Table
id="CustomOrderTable"
metaPath="/Orders"
readOnly="false"
enableExport="true"
personalization="Sort,Filter,Column" />
</core:FragmentDefinition>
FilterBar Building Block
<macros:FilterBar
id="OrderFilterBar"
metaPath="/Orders/@com.sap.vocabularies.UI.v1.SelectionFields"
search="true" />
Chart Building Block
<macros:Chart
id="OrdersChart"
metaPath="/Orders/@com.sap.vocabularies.UI.v1.Chart" />
Field Building Block
<macros:Field
metaPath="/Orders/totalAmount"
id="amountField" />
Extension Points — add to existing floorplan pages
Custom Section on Object Page
In manifest.json:
"OrdersObjectPage": {
"type": "Component",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"entitySet": "Orders",
"content": {
"body": {
"sections": {
"CustomApprovalSection": {
"template": "orders.ext.CustomApprovalSection",
"position": { "placement": "After", "anchor": "GeneralFacet" },
"title": "Approval History",
"visible":
Fragment: app/orders/ext/CustomApprovalSection.fragment.xml:
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:macros="sap.fe.macros">
<VBox>
<macros:Table
id="ApprovalHistoryTable"
metaPath="/Orders/approvalHistory"
readOnly="true" />
</VBox>
</core:FragmentDefinition>
Custom Column in List Report table
"OrdersList": {
"settings": {
"entitySet": "Orders",
"controlConfiguration": {
"@com.sap.vocabularies.UI.v1.LineItem": {
"columns": {
"CustomStatusColumn": {
"header": "Custom Status",
"template": "orders.ext.CustomStatusColumn",
"position": { "placement": "After", "anchor": "DataField::status" }
}
}
}
}
}
}
Custom Action in toolbar
"actions": {
"CustomExport": {
"press": "orders.ext.ExtensionController.onCustomExport",
"enabled": true,
"text": "Export to Excel"
}
}
Controller Extensions — override standard behavior
sap.ui.define([
'sap/ui/core/mvc/ControllerExtension',
'sap/ui/core/mvc/OverrideExecution'
], (ControllerExtension, OverrideExecution) => {
return ControllerExtension.extend('orders.ext.ExtensionController', {
metadata: {
methods: {
onBeforeSave: { public: true, final: false, overrideExecution: OverrideExecution.After }
}
},
onBeforeSave() {
const context = this.base.getModel().bindContext('/Orders(...)');
return Promise.resolve();
},
onCustomExport() {
const table = this.base.byId('fe::table::Orders::LineItem');
},
onAfterRendering() {
}
});
});
Wire in manifest.json:
"extends": {
"extensions": {
"sap.ui.controllerExtensions": {
"sap.fe.templates.ObjectPage.ObjectPageController": {
"controllerName": "orders.ext.ExtensionController"
}
}
}
}
When to use what
| Need | Approach |
|---|
| Add a table/chart/filter from your own data | Building Block |
| Add a whole new section to Object Page | Extension Point + custom fragment |
| Add a column to an existing table | Extension Point controlConfiguration |
| Add a toolbar button with custom logic | Extension Point action + controller extension |
| Override save/edit/navigation flow | Controller Extension |
| Completely custom page in the navigation flow | Custom Page (last resort) |
Common mistakes to avoid
-
❌ Using freestyle SAPUI5 for the whole app when a floorplan covers 80% of the use case
-
✅ Use floorplan + building blocks/extension points for the remaining 20%
-
❌ Building blocks used outside OData V4 context — they require V4 metadata
-
✅ Flexible programming model is V4 only
-
❌ Overriding the entire ObjectPageController when only one method needs changing
-
✅ Only override specific hooks, leave the rest to the framework
-
❌ Using sap.ui.getCore().byId(...) to find controls — breaks with framework updates
-
✅ Use this.base.byId(...) in controller extensions
-
❌ Putting custom section fragments inside webapp/ at root level — hard to maintain
-
✅ Keep all extensions in app/<name>/ext/ folder with clear naming