SuperAPI AI Directory

fiori-flexible-programming

Coding & development

View repository

Updated 10-16 02:37

About this skill

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:

  1. Building Blocks — reuse Fiori Elements components in your own views
  2. Extension Points — add custom sections, columns, or actions via manifest.json
  3. 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

<!-- custom section fragment: app/orders/ext/CustomSection.fragment.xml -->
<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": true
            }
          }
        }
      }
    }
  }
}

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

// app/orders/ext/ExtensionController.js
sap.ui.define([
  'sap/ui/core/mvc/ControllerExtension',
  'sap/ui/core/mvc/OverrideExecution'
], (ControllerExtension, OverrideExecution) => {
  return ControllerExtension.extend('orders.ext.ExtensionController', {
    metadata: {
      methods: {
        // override standard edit-flow hook
        onBeforeSave: { public: true, final: false, overrideExecution: OverrideExecution.After }
      }
    },

    // Runs before the user saves — validate or enrich data
    onBeforeSave() {
      const context = this.base.getModel().bindContext('/Orders(...)');
      // custom validation logic
      return Promise.resolve();
    },

    // Custom action handler (wired in manifest actions)
    onCustomExport() {
      const table = this.base.byId('fe::table::Orders::LineItem');
      // export logic
    },

    // Lifecycle hook — runs when Object Page is ready
    onAfterRendering() {
      // DOM-safe customizations
    }
  });
});

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