agentsclimarketplace

Sap ui5 fiori

Skill efeumutaslan/SAP-SKILLS/skills/sap-ui5-fiori

23 SAP development skills for Claude Code — ABAP, RAP, CAP, Fiori, BTP, HANA, S/4HANA, Integration Suite and more. Agent Skills Specification compatible.

Install
npx -y skills add efeumutaslan/SAP-SKILLS --skill sap-ui5-fiori

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

SAPUI5 and Fiori application development skill. Use when building Fiori apps, writing XML views, creating UI5 controllers, binding OData models, configuring manifest.json, implementing routing, using Fiori Elements (LROP/OVP/ALP), or working with SAP Fiori design guidelines. If the user mentions SAPUI5, Fiori, XML view, UI5 controller, manifest, Fiori Elements, sap.m, sap.ui.table, JSONModel, ODataModel, or Fiori launchpad, use this skill.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

14.6 KB, as published. Nobody here has run it

SAPUI5 and Fiori Application Development

Related Skills

  • sap-fiori-testing — OPA5, wdi5, accessibility testing for the apps built here
  • sap-odata — OData v2/v4 service design and consumption (the backend for most Fiori apps)
  • sap-rap-comprehensive — RAP backends that auto-generate OData for UI5 consumption
  • sap-cap-advanced — CAP backends with CDS-based OData services
  • sap-build-apps — Low-code alternative when full UI5 is overkill

Quick Start

Scaffold a new freestyle UI5 app (Fiori Tools):

npm install -g @sap/generator-fiori
yo @sap/fiori
# Select: SAPUI5 freestyle → Basic → OData V4 service → project details

Scaffold a Fiori Elements app (zero-code UI):

yo @sap/fiori
# Select: SAPUI5 Fiori Elements → List Report Object Page (LROP)

Run locally with live-reload:

npm start                     # Starts ui5 serve with local OData mock or proxy

Build for deployment:

npm run build                 # Produces dist/ folder
npm run deploy                # Deploys to ABAP, CF, or HTML5 repo

Core Concepts

MVC architecture

app/
├── webapp/
│   ├── Component.js          # Component: root, manifest, models, router
│   ├── manifest.json         # App descriptor (routes, models, data sources)
│   ├── view/                 # XML views (declarative UI)
│   │   ├── App.view.xml
│   │   ├── Worklist.view.xml
│   │   └── Object.view.xml
│   ├── controller/           # Controller logic (event handlers, business logic)
│   │   ├── App.controller.js
│   │   ├── Worklist.controller.js
│   │   └── Object.controller.js
│   ├── model/                # Formatters, grouping, custom models
│   │   └── formatter.js
│   ├── i18n/                 # Translations
│   │   └── i18n.properties
│   ├── css/                  # Custom styles
│   └── test/                 # QUnit/OPA5 tests
├── ui5.yaml                  # UI5 tooling config
└── package.json

Models (critical to understand)

ModelUse caseBinding syntax
JSONModelClient-side state, mock data, view state{/path/to/value}
ODataModel (v4)Primary for SAPUI5 + modern SAP backends{propertyName} or {BindingPath}
ODataModel (v2)Legacy OData services, Gateway services{propertyName}
ResourceModeli18n texts from .properties files{i18n>textKey}
DeviceModelDevice info (phone/tablet/desktop, orientation){device>/system/phone}

Binding modes

<!-- Property binding: single value -->
<Text text="{Name}" />

<!-- Element binding: bind a context (entity) to a control -->
<Form binding="{/Products('HT-1001')}">

<!-- Aggregation binding: list of items -->
<List items="{/Products}">
  <StandardListItem title="{Name}" description="{Category}" />
</List>

<!-- Expression binding: computed values -->
<ObjectStatus text="{= ${Price} > 100 ? 'Expensive' : 'Cheap' }" />

<!-- i18n binding -->
<Button text="{i18n>buttonSave}" />

Routing (manifest.json)

"routing": {
  "config": {
    "routerClass": "sap.m.routing.Router",
    "viewType": "XML",
    "viewPath": "my.app.view",
    "controlId": "app",
    "controlAggregation": "pages"
  },
  "routes": [
    { "name": "worklist", "pattern": "", "target": "worklist" },
    { "name": "object", "pattern": "Products/{productId}", "target": "object" }
  ],
  "targets": {
    "worklist": { "viewName": "Worklist" },
    "object":   { "viewName": "Object" }
  }
}

Navigate from a controller:

this.getOwnerComponent().getRouter().navTo("object", { productId: "HT-1001" });

Common Patterns

Pattern 1: Basic controller skeleton

sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "sap/ui/model/json/JSONModel",
  "sap/m/MessageToast"
], function (Controller, JSONModel, MessageToast) {
  "use strict";

  return Controller.extend("my.app.controller.Worklist", {

    onInit: function () {
      const oViewModel = new JSONModel({ busy: false, delay: 0 });
      this.getView().setModel(oViewModel, "viewModel");
    },

    onRefresh: function () {
      const oList = this.byId("productsList");
      oList.getBinding("items").refresh();
    },

    onItemPress: function (oEvent) {
      const sPath = oEvent.getSource().getBindingContext().getPath();
      const sProductId = sPath.match(/'([^']+)'/)[1];
      this.getOwnerComponent().getRouter().navTo("object", { productId: sProductId });
    },

    onSearch: function (oEvent) {
      const sQuery = oEvent.getParameter("query");
      const oBinding = this.byId("productsList").getBinding("items");
      const aFilters = sQuery ? [new sap.ui.model.Filter("Name", "Contains", sQuery)] : [];
      oBinding.filter(aFilters);
    }
  });
});

Pattern 2: Worklist XML view (list + search + pull-to-refresh)

<mvc:View
  controllerName="my.app.controller.Worklist"
  xmlns="sap.m"
  xmlns:mvc="sap.ui.core.mvc">
  <Page title="{i18n>worklistTitle}">
    <subHeader>
      <Toolbar>
        <SearchField width="100%" search=".onSearch" />
      </Toolbar>
    </subHeader>
    <content>
      <List
        id="productsList"
        items="{path: '/Products', parameters: {$count: true}}"
        growing="true"
        growingThreshold="20"
        growingScrollToLoad="true">
        <ObjectListItem
          title="{Name}"
          number="{
            parts: [{path: 'Price'}, {path: 'CurrencyCode'}],
            type: 'sap.ui.model.type.Currency',
            formatOptions: {showMeasure: false}
          }"
          numberUnit="{CurrencyCode}"
          type="Navigation"
          press=".onItemPress">
          <attributes>
            <ObjectAttribute text="{Category}" />
          </attributes>
        </ObjectListItem>
      </List>
    </content>
  </Page>
</mvc:View>

Pattern 3: Fiori Elements — annotation-driven LROP

With Fiori Elements you write no controller code. The UI is generated from CDS annotations or manifest.json + metadata.xml.

// Backend annotations (CAP/CDS)
annotate Products with @(
  UI.LineItem: [
    { Value: ID },
    { Value: Name },
    { Value: Category },
    { Value: Price }
  ],
  UI.HeaderInfo: {
    TypeName: 'Product',
    TypeNamePlural: 'Products',
    Title: { Value: Name }
  },
  UI.SelectionFields: [ Category, Price ]
);

The UI5 app's manifest.json references sap.fe.templates.ListReport and the OData service — that's it. The app auto-renders a filter bar, table, and detail page.

Pattern 4: Fragments (reusable UI chunks)

<!-- view/Dialog.fragment.xml -->
<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">
  <Dialog title="{i18n>dialogTitle}">
    <content>
      <Input value="{/newValue}" />
    </content>
    <beginButton>
      <Button text="OK" press=".onDialogOk" />
    </beginButton>
    <endButton>
      <Button text="Cancel" press=".onDialogCancel" />
    </endButton>
  </Dialog>
</core:FragmentDefinition>
// Controller
onOpenDialog: async function () {
  if (!this._oDialog) {
    this._oDialog = await this.loadFragment({ name: "my.app.view.Dialog" });
  }
  this._oDialog.open();
}

Pattern 5: manifest.json essentials

{
  "_version": "1.58.0",
  "sap.app": {
    "id": "my.app",
    "type": "application",
    "dataSources": {
      "mainService": {
        "uri": "/sap/opu/odata/sap/ZMY_SERVICE/",
        "type": "OData",
        "settings": { "odataVersion": "4.0" }
      }
    }
  },
  "sap.ui5": {
    "dependencies": {
      "minUI5Version": "1.120.0",
      "libs": { "sap.m": {}, "sap.ui.core": {}, "sap.f": {} }
    },
    "models": {
      "i18n": { "type": "sap.ui.model.resource.ResourceModel",
                "settings": { "bundleName": "my.app.i18n.i18n" } },
      "": {
        "dataSource": "mainService",
        "preload": true,
        "settings": {
          "synchronizationMode": "None",
          "operationMode": "Server",
          "autoExpandSelect": true,
          "earlyRequests": true
        }
      }
    },
    "routing": { /* ... see Routing section */ }
  }
}

Pattern 6: Custom formatters

// model/formatter.js
sap.ui.define([], function () {
  "use strict";
  return {
    statusText: function (sStatus) {
      const oBundle = this.getOwnerComponent().getModel("i18n").getResourceBundle();
      switch (sStatus) {
        case "A": return oBundle.getText("statusActive");
        case "I": return oBundle.getText("statusInactive");
        default:  return sStatus;
      }
    },
    currencyValue: function (sValue) {
      return sValue ? parseFloat(sValue).toFixed(2) : "";
    }
  };
});

Use in XML:

<Text text="{path: 'Status', formatter: '.formatter.statusText'}" />

Error Catalog

ErrorCauseFix
Cannot read property 'getText' of undefinedi18n model not loaded when controller reads itRead i18n inside onInit AFTER this.getView().getModel("i18n").getResourceBundle() is available, not at module scope
The given binding path "/X" is invalidPath doesn't exist in model or model name is wrongUse debug tools (Ctrl+Alt+Shift+P) to inspect bindings; check model name prefix (i18n>, device>)
404 on /sap/opu/odata/...Service not activated or wrong pathIn SEGW: activate service in /IWFND/MAINT_SERVICE; in RAP: check service binding Publish
No class sap.m.XyzTypo in control name or library not loadedCheck spelling, add the library to sap.ui5.dependencies.libs in manifest.json
Cannot read properties of null (reading 'getBindingContext')Control not yet rendered when handler firesUse onAfterRendering or attachEventOnce instead of onInit
CSRF token validation failedMissing X-CSRF-Token on write requestsODataModel handles this automatically; if using custom fetch, first send GET with X-CSRF-Token: Fetch, reuse token for POST/PUT/DELETE
sap.ui.model.odata.v4.ODataListBinding: Path does not have a typeautoExpandSelect: true with a path the service doesn't exposeSet autoExpandSelect: false OR make sure every bound property is in the service metadata
UI5 app works locally but 404 on BTPdestinations not configured in xs-app.jsonAdd a route pointing to the destination; verify destination exists in BTP cockpit

Performance Tips

  1. autoExpandSelect: true (OData v4) — UI5 automatically adds $select and $expand based on bindings, reducing payload size by 60–80%.
  2. growing="true" with growingThreshold — never load thousands of rows; let the user scroll to fetch more.
  3. Preload components — set "preload": true on critical models in manifest for parallel loading.
  4. Use sap.ui.table.Table for 1000+ rows — it virtualizes rows, unlike sap.m.Table.
  5. Avoid forceUpdate() — prefer binding refresh over full re-render.
  6. Lazy-load fragments — load dialogs/popovers on first open, not in onInit.
  7. async: true in Component — enable async routing and view loading.
  8. Use sap-ui-compatVersion=edge — removes legacy code paths.
  9. Minify + preload in buildui5 build --clean-dest --all produces Component-preload.js (single-file bundle).
  10. Avoid deep $expand — prefer separate batch requests; deeply nested expands kill response time.

Gotchas

  • this binding in async callbacks — use arrow functions or .bind(this); regular function expressions lose this.
  • OData v2 vs v4 path syntax — v2: /Products('HT-1001'); v4: /Products(ID='HT-1001'). They are NOT interchangeable.
  • Two-way binding requires change events — without them, the model is updated but the server is not.
  • sap.ui.getCore().byId() is deprecated — use this.byId() inside controllers or view's byId().
  • Fragments share the controller of the view that loaded them — event handlers refer to the view controller, not a separate one.
  • Duplicate IDs across fragments — when a fragment is loaded multiple times, prefix IDs with this.createId() or use addDependent().
  • manifest.json changes need a full app reload — hot-reload doesn't pick them up.
  • sap.m.Table vs sap.ui.table.Table — the first is responsive (mobile-first), the second is desktop-only. Pick based on target devices.

Fiori Elements (when to use it)

Use Fiori Elements when:

  • The UI is mostly CRUD over a well-modeled entity
  • You want consistent Fiori look without custom code
  • You can annotate the backend (CAP/CDS) with UI.* annotations
  • The template (LROP/OVP/ALP) matches the use case

Avoid Fiori Elements when:

  • The UI needs heavy customization (custom visualizations, wizards)
  • The data model is flat or non-standard
  • You need pixel-perfect design control

Templates:

  • LROP (List Report Object Page): filter → table → detail — the most common pattern
  • OVP (Overview Page): card-based dashboard
  • ALP (Analytical List Page): chart + table analytics
  • Worklist: focused task lists

Custom extensions:

  • Extension points: @sap/ux-specification defines where you can inject custom fragments/controllers
  • Flexible programming model: sap.fe.core.controllerextensions.* for lifecycle hooks

References

  • references/bindings-cheatsheet.md — Complete binding syntax reference
  • references/fiori-elements-annotations.md — UI.* annotation catalog for LROP/OVP/ALP
  • references/controls-catalog.md — When to use which UI5 control
  • templates/controller-template.js — Copy-paste controller skeleton
  • templates/xml-view-template.xml — Copy-paste XML view skeleton
  • templates/manifest-template.json — Copy-paste manifest.json

MCP Server Integration

For live UI5 development assistance, pair this skill with:

  • @ui5/mcp-server — UI5 control info, API lookup, sample code
  • @sap-ux/fiori-mcp-server — Fiori Tools operations (generate app, build, deploy)
  • @sap/mdk-mcp-server — MDK for mobile-first scenarios

Install via mcp-configs/fullstack-btp.json.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.