fix-fiori-elements-extensions
Handle Fiori Elements V2 controller extensions during UI5 modernization. Use this skill when: - Extension controllers use `sap.ui.controller()` AND the manifest has a matching `controllerName` entry (Case B → report only, do not modernize) - Extension controllers use `Controller.extend()` with manifest `controllerName` registration (Case B → report only, do not modernize) - Code contains `registerControllerExtensions` calls (Case A → ControllerExtension.extend + override) - Manifest.json has `sap.ui5/extends/extensions/sap.ui.controllerExtensions` with `controllerName` entries Case B (plain object) is by far the most common. For Case B, do NOT modify controller files — report them and move on. Case A requires actual code modernization. For plain controller definitions in custom (non-Fiori-Elements) apps where `sap.ui.controller()` is just defining a standalone controller, use `fix-js-globals` (Case 9) instead.
How do I install this agent skill?
npx skills add https://github.com/ui5/plugins-coding-agents --skill fix-fiori-elements-extensionsIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides instructions for modernizing Fiori Elements V2 controller extensions in SAP UI5 applications. It defines refactoring patterns for different registration cases (A and B) and provides steps for code transformation and manifest updates. No security issues were detected.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Fix Fiori Elements Controller Extensions
This skill handles Fiori Elements V2 controller extensions during UI5 modernization. There are two cases with different actions:
- Case B (most common): Extensions registered via
controllerNamein manifest → Report only. Do NOT modify the controller files. Leave them as-is and inform the user which files need manual attention. - Case A (rare): Extensions using
registerControllerExtensions→ Perform the full modernization toControllerExtension.extend()+ manifest registration.
Linter Rules Handled
| Rule ID | Message Pattern | This Skill's Action |
|---|---|---|
no-deprecated-api | Use of deprecated registerControllerExtensions | Case A: Modernize to manifest + ControllerExtension |
no-deprecated-api | Use of deprecated sap.ui.controller | Case B (if manifest has controllerName): report only, do not fix; Case A (if registerControllerExtensions): ControllerExtension class |
no-deprecated-api | Use of deprecated Controller (from sap/ui/core/mvc/Controller) | Case B: report only, do not fix |
Quick Decision: Which Case Am I?
Is there a `controllerName` entry in manifest.json under
sap.ui5/extends/extensions/sap.ui.controllerExtensions with a SIMPLE key
(no "#" in the key)?
├── YES → Case B: DO NOT MODERNIZE. Report the file(s) and move on.
│ (regardless of whether source uses sap.ui.controller() or Controller.extend())
└── NO → Is there a registerControllerExtensions call in JS?
├── YES → Case A: modernize to ControllerExtension.extend() + manifest with #stableId key
└── NO → This skill does not apply
When to Use
Case B (most common) — Detected when the manifest already has a controllerName entry under a simple key, and the controller file uses either sap.ui.controller() or Controller.extend():
// manifest.json — controllerName registration already present (simple key, no "#")
"sap.ui.controllerExtensions": {
"sap.suite.ui.generic.template.ListReport.view.ListReport": {
"controllerName": "my.app.ext.controller.ListReportExt"
}
}
The source controller might look like either of these:
// Variant 1: sap.ui.controller() — NO sap.ui.define wrapper
sap.ui.controller("my.app.ext.controller.ListReportExt", {
onInit: function() { ... },
onCustomAction: function() { ... }
});
// Variant 2: Controller.extend() — inside sap.ui.define
sap.ui.define(["sap/ui/core/mvc/Controller", ...], function(Controller, ...) {
return Controller.extend("my.app.ext.controller.ListReportExt", { ... });
});
Linter output triggering Case B:
MyExtension.controller.js:1:1 error Use of deprecated function 'sap.ui.controller' no-deprecated-api
MyExtension.controller.js:3:5 error Use of deprecated function 'Controller' (from 'sap/ui/core/mvc/Controller') no-deprecated-api
Action for Case B: Do NOT modify these files. Report them to the user and continue with the rest of the modernization.
Case A (rare, older apps only) — Apply when you see linter output like:
Component.js:25:5 error Use of deprecated function 'registerControllerExtensions' no-deprecated-api
Or when an app has patterns like:
// In Component.js or elsewhere — NO manifest entry exists yet
this.getExtensionComponent().registerControllerExtensions("sap.suite.ui.generic.template.ListReport.view.ListReport", {
onInit: function() { ... },
onAction: function() { ... }
});
Fix Strategy
The action depends on how the extension is registered in the manifest. Determine which case applies before proceeding.
Case Detection
| Manifest Registration | Source Code (any of) | Action |
|---|---|---|
controllerName in manifest (simple key, no #stableId) | sap.ui.controller(), Controller.extend(), or plain object | Case B: Report only — do NOT modify files |
registerControllerExtensions in JS (no manifest entry) | sap.ui.controller() or inline object | Case A: ControllerExtension.extend() + override + add manifest entry with #stableId key |
The key differentiator is the manifest registration format:
- Case B (most common):
controllerNamealready in manifest under a simple key (e.g.,"sap.suite.ui.generic.template.ListReport.view.ListReport": { "controllerName": "..." }). Do not modernize these files. Report them and move on. - Case A: No
controllerNamein manifest yet — extension is registered programmatically viaregisterControllerExtensions. The modernization adds a manifest entry with the#stableIdkey format AND usesControllerExtension.extend().
Detection commands:
# Check manifest registration format
grep -B1 -A2 "controllerName" webapp/manifest.json
# Case B indicator: controllerName in manifest with simple key (no # in key)
# If the key does NOT contain "#", it's the merging mechanism → Case B → REPORT ONLY
# Case A indicator: registerControllerExtensions in JS
grep -rl "registerControllerExtensions" webapp/ --include="*.js"
Case B: Report and Skip (Most Common)
For extensions registered via controllerName in the manifest (simple key without #stableId), do NOT modify the controller files. These require careful manual modernization and are left to the developer.
Steps:
- Identify all controller files referenced by
controllerNameentries in manifest.json (under keys that do NOT contain#) - Report them to the user with this format:
⚠️ Fiori Elements controller extensions (Case B) — skipped, requires manual modernization:
- webapp/ext/controller/ListReportExt.controller.js
- webapp/ext/controller/ObjectPageExt.controller.js
These files use deprecated APIs (sap.ui.controller / Controller.extend) but are Fiori Elements
controller extensions registered via controllerName in the manifest. They require careful manual
modernization to a plain-object return pattern. The linter errors for these files will persist until
they are modernized manually.
- Do NOT (within this skill):
- Modify the controller .js files to change the controller extension pattern (no plain-object conversion)
- Change the manifest registration
- Add linter disable comments
- Attempt the plain-object modernization pattern
- Other skills CAN still process these files for unrelated fixes (e.g.,
fix-js-globalsreplacingsap.ui.getCore()calls within method bodies,fix-linter-blind-spotsfor namespace patterns). Only thesap.ui.controller()/Controller.extend()structure itself is left untouched. - Continue with the rest of the modernization (other skills, other linter findings)
Case A: Step 1 — Move Registration from JS to manifest.json
Only for Case A (extensions using registerControllerExtensions in JS).
Move controller extension registrations from JavaScript code to the manifest under sap.ui.controllerExtensions.
Key format: <FLOORPLAN_CONTROLLER>#<STABLE_ID_OF_VIEW>
// Before - no controller extensions in manifest
// After - manifest.json
{
"sap.ui5": {
"extends": {
"extensions": {
"sap.ui.controllerExtensions": {
"sap.suite.ui.generic.template.ListReport.view.ListReport#myApp::sap.suite.ui.generic.template.ListReport.view.ListReport::EntitySet": {
"controllerName": "my.app.ext.ListReportExtension"
},
"sap.suite.ui.generic.template.ObjectPage.view.Details#myApp::sap.suite.ui.generic.template.ObjectPage.view.Details::EntitySet": {
"controllerName": "my.app.ext.ObjectPageExtension"
}
}
}
}
}
}
Finding the correct key:
- The key has format:
<FloorplanController>#<StableViewId> - The floorplan controller name matches the view name (e.g.,
sap.suite.ui.generic.template.ListReport.view.ListReport) - The stable view ID is typically:
<AppId>::<FloorplanViewName>::<EntitySet> - Check the app's existing manifest for
sap.ui.generic.app/pagesconfiguration to find entity sets and page IDs
Case A: Step 2 — Restructure Controller Files
⚠️ This step only applies to Case A (extensions using
sap.ui.controller()factory). If the extension already usesController.extend()with manifestcontrollerNameregistration, use Case B below instead.
Convert controller files from sap.ui.controller style to ControllerExtension class.
Before:
sap.ui.controller("my.app.ext.ListReportExtension", {
onInit: function() {
// lifecycle code
},
onBeforeRebindTable: function(oEvent) {
// framework override
},
onCustomAction: function(oEvent) {
// custom method
}
});
After:
sap.ui.define([
"sap/ui/core/mvc/ControllerExtension"
], function(ControllerExtension) {
"use strict";
return ControllerExtension.extend("my.app.ext.ListReportExtension", {
// Lifecycle and framework overrides go inside "override"
override: {
onInit: function() {
// extensionAPI is injected by the framework into the extension instance
this._extensionAPI = this.extensionAPI;
},
onBeforeRebindTable: function(oEvent) {
// framework override
}
},
// Custom methods go OUTSIDE "override"
onCustomAction: function(oEvent) {
// custom method
}
});
});
Key rules for restructuring:
- Extend
sap/ui/core/mvc/ControllerExtensioninstead of usingsap.ui.controller - Lifecycle methods (
onInit,onExit,onBeforeRendering,onAfterRendering) → insideoverride - Framework callback methods (
onBeforeRebindTable,onBeforeRebindChart,onListNavigationExtension,adaptNavigationParameterExtension, etc.) → insideoverride - Custom action methods (handlers for custom buttons/actions) → OUTSIDE
override - Access
extensionAPIviathis.extensionAPI(the framework injects it into the extension instance)
Case A: Step 3 — Update Handler References
In the manifest and XML annotations, update all event handler references from the old format to the new qualified format.
Before:
// In manifest extensions or annotations
"Actions": {
"MyAction": {
"id": "MyAction",
"text": "Do Something",
"press": "onCustomAction"
}
}
After:
"Actions": {
"MyAction": {
"id": "MyAction",
"text": "Do Something",
"press": ".extension.my.app.ext.ListReportExtension.onCustomAction"
}
}
Reference format: .extension.<full.controller.name>.<methodName>
Important: Use the dotted module name as declared in ControllerExtension.extend("my.app.ext.ListReportExtension", ...) and in the manifest's controllerName — not the slash-separated path used in sap.ui.define imports (e.g., "my/app/ext/ListReportExtension").
This applies to:
presshandlers in manifest action definitions- Custom column/section handler references
- Any event handler references that previously used just the method name
Implementation Steps
Case B Implementation (Report Only)
- Identify extension controllers registered via
controllerNamein manifest (simple key, no#) - Report them to the user — list each file path and note that they require manual modernization
- Do not modify these files, their manifest entries, or their handler references
- Continue with other modernization tasks
Case A Implementation (registerControllerExtensions → ControllerExtension)
- Identify all
registerControllerExtensionscalls in the codebase (typically in Component.js or helper files) - For each registration:
a. Determine the floorplan controller name and view stable ID
b. Determine the extension controller name/path
c. Add entry to
manifest.jsonundersap.ui5/extends/extensions/sap.ui.controllerExtensions - For each extension controller file:
a. Replace
sap.ui.controller(...)withsap.ui.define([ControllerExtension], ...)b. Move lifecycle/framework methods intooverridesection c. Keep custom methods outsideoverrided. UpdateextensionAPIaccess pattern - For all handler references:
a. Search manifest.json for action
presshandlers using simple method names b. Search XML annotation files for handler references c. Update to.extension.<module>.<method>format - Remove the
registerControllerExtensionscalls from Component.js - Verify by re-running the linter
Example Full Modernization
Before — Component.js:
sap.ui.define([
"sap/suite/ui/generic/template/lib/AppComponent"
], function(AppComponent) {
"use strict";
return AppComponent.extend("my.app.Component", {
metadata: {
manifest: "json"
},
init: function() {
AppComponent.prototype.init.apply(this, arguments);
this.getExtensionComponent().registerControllerExtensions(
"sap.suite.ui.generic.template.ListReport.view.ListReport", {
onInit: function() {
// lifecycle code
},
onCustomAction: function(oEvent) {
// custom handler
}
}
);
}
});
});
After — Component.js:
sap.ui.define([
"sap/suite/ui/generic/template/lib/AppComponent"
], function(AppComponent) {
"use strict";
return AppComponent.extend("my.app.Component", {
metadata: {
manifest: "json"
}
// registerControllerExtensions call removed — now in manifest.json
});
});
After — manifest.json (additions):
{
"sap.ui5": {
"extends": {
"extensions": {
"sap.ui.controllerExtensions": {
"sap.suite.ui.generic.template.ListReport.view.ListReport#my.app::sap.suite.ui.generic.template.ListReport.view.ListReport::Products": {
"controllerName": "my.app.ext.ListReportExtension"
}
}
}
}
}
}
After — ext/ListReportExtension.controller.js:
sap.ui.define([
"sap/ui/core/mvc/ControllerExtension"
], function(ControllerExtension) {
"use strict";
return ControllerExtension.extend("my.app.ext.ListReportExtension", {
override: {
onInit: function() {
this._extensionAPI = this.extensionAPI;
}
},
onCustomAction: function(oEvent) {
// Custom action handler — accessible as
// ".extension.my.app.ext.ListReportExtension.onCustomAction"
}
});
});
Notes
- This modernization only applies to Fiori Elements V2 template apps (using
sap.suite.ui.generic.template) - Fiori Elements V4 (using
sap.fe.templates) uses a different extension mechanism — this skill does not apply - Case B is far more common in practice — extensions registered via
controllerNamein the manifest. These are not auto-modernized — the agent reports them and moves on - Case A only appears in older apps that never modernized from
registerControllerExtensions - The stable view ID format varies by floorplan and configuration — check the running app's DOM or the floorplan documentation
- If the app uses
templateSpecific.jsonfor handler references, those must also be updated (Case A only) - After Case A modernization, the extension controller file should be placed under a path matching its module name
Related Skills
- fix-js-globals: For plain
sap.ui.controller()definitions in custom apps (Case 9), or if extension controllers also use global access patterns (sap.ui.getCore(),jQuery.sap.*). Other skills CAN still be applied to Case B controller files for issues unrelated to the controller extension pattern itself (e.g., replacing global API usage within method bodies). - fix-manifest-json: The manifest changes for controller extensions are additive — they don't conflict with version/routing updates from fix-manifest-json
- fix-linter-blind-spots: Can still fix namespace patterns and other blind spots within Case B controller files
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/ui5/plugins-coding-agents/fix-fiori-elements-extensions">View fix-fiori-elements-extensions on skillZs</a>