# Sap Ui5 Fiori

> >

- **Type:** Skill
- **Install:** `agentstack add skill-efeumutaslan-sap-skills-sap-ui5-fiori`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [efeumutaslan](https://agentstack.voostack.com/s/efeumutaslan)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [efeumutaslan](https://github.com/efeumutaslan)
- **Source:** https://github.com/efeumutaslan/SAP-SKILLS/tree/main/skills/sap-ui5-fiori

## Install

```sh
agentstack add skill-efeumutaslan-sap-skills-sap-ui5-fiori
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 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):**
```bash
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):**
```bash
yo @sap/fiori
# Select: SAPUI5 Fiori Elements → List Report Object Page (LROP)
```

**Run locally with live-reload:**
```bash
npm start                     # Starts ui5 serve with local OData mock or proxy
```

**Build for deployment:**
```bash
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)

| Model | Use case | Binding syntax |
|-------|----------|----------------|
| `JSONModel` | Client-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}` |
| `ResourceModel` | i18n texts from .properties files | `{i18n>textKey}` |
| `DeviceModel` | Device info (phone/tablet/desktop, orientation) | `{device>/system/phone}` |

### Binding modes
```xml

  

 100 ? 'Expensive' : 'Cheap' }" />

buttonSave}" />
```

### Routing (manifest.json)
```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:
```javascript
this.getOwnerComponent().getRouter().navTo("object", { productId: "HT-1001" });
```

## Common Patterns

### Pattern 1: Basic controller skeleton
```javascript
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)
```xml

  worklistTitle}">
    
      
        
      
    
    
      
        
          
            
          
        
      
    
  

```

### 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`.

```cds
// 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)
```xml

  dialogTitle}">
    
      
    
    
      
    
    
      
    
  

```

```javascript
// 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
```json
{
  "_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
```javascript
// 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:
```xml

```

## Error Catalog

| Error | Cause | Fix |
|-------|-------|-----|
| `Cannot read property 'getText' of undefined` | i18n model not loaded when controller reads it | Read i18n inside `onInit` AFTER `this.getView().getModel("i18n").getResourceBundle()` is available, not at module scope |
| `The given binding path "/X" is invalid` | Path doesn't exist in model or model name is wrong | Use 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 path | In SEGW: activate service in `/IWFND/MAINT_SERVICE`; in RAP: check service binding `Publish` |
| `No class sap.m.Xyz` | Typo in control name or library not loaded | Check 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 fires | Use `onAfterRendering` or `attachEventOnce` instead of `onInit` |
| `CSRF token validation failed` | Missing X-CSRF-Token on write requests | ODataModel 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 type` | `autoExpandSelect: true` with a path the service doesn't expose | Set `autoExpandSelect: false` OR make sure every bound property is in the service metadata |
| `UI5 app works locally but 404 on BTP` | `destinations` not configured in xs-app.json | Add 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 build** — `ui5 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`.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [efeumutaslan](https://github.com/efeumutaslan)
- **Source:** [efeumutaslan/SAP-SKILLS](https://github.com/efeumutaslan/SAP-SKILLS)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-efeumutaslan-sap-skills-sap-ui5-fiori
- Seller: https://agentstack.voostack.com/s/efeumutaslan
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
