Fix Fiori Elements Extensions

作者 UI52b8c4a944e39無授權條款收錄於 2026年10月8日更新於 2026年10月8日

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.

AI 產生的概覽

指導 UI5 現代化中 Fiori Elements V2 控制器擴充的處理:Case B 僅回報,Case A 進行改造。

功能
此技能為 UI5 現代化過程中處理 Fiori Elements V2 控制器擴充提供判斷規則。它區分已在 manifest 中透過 controllerName 註冊的擴充(Case B,僅回報且不修改檔案)與透過 registerControllerExtensions 以程式方式註冊的擴充(Case A,轉換為 ControllerExtension.extend,包含 override 區段、使用穩定 ID 鍵的 manifest 項目以及更新後的事件處理參照)。視情況產出被略過檔案的回報,或一組程式碼與 manifest 修改。
適用情境
在現代化使用 sap.suite.ui.generic.template 的 Fiori Elements V2 應用程式,且 linter 回報已棄用的 sap.ui.controller、Controller.extend 或 registerControllerExtensions 用法時使用。不適用於 Fiori Elements V4 應用程式,也不適用於自訂應用程式中的一般獨立控制器。
執行需求
不附帶指令碼,僅為說明性指示。需要存取應用程式原始碼,包括 manifest.json、控制器 JavaScript 檔案,以及選用的 XML 註解檔案,並需要能回報 no-deprecated-api 問題的 linter。

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 controllerName in 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 to ControllerExtension.extend() + manifest registration.

Linter Rules Handled

Rule IDMessage PatternThis Skill's Action
no-deprecated-apiUse of deprecated registerControllerExtensionsCase A: Modernize to manifest + ControllerExtension
no-deprecated-apiUse of deprecated sap.ui.controllerCase B (if manifest has controllerName): report only, do not fix; Case A (if registerControllerExtensions): ControllerExtension class
no-deprecated-apiUse 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 undersap.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():

json
// 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:

javascript
// Variant 1: sap.ui.controller() — NO sap.ui.define wrappersap.ui.controller("my.app.ext.controller.ListReportExt", {    onInit: function() { ... },    onCustomAction: function() { ... }});
javascript
// Variant 2: Controller.extend() — inside sap.ui.definesap.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-apiMyExtension.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:

javascript
// In Component.js or elsewhere — NO manifest entry exists yetthis.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 RegistrationSource Code (any of)Action
controllerName in manifest (simple key, no #stableId)sap.ui.controller(), Controller.extend(), or plain objectCase B: Report only — do NOT modify files
registerControllerExtensions in JS (no manifest entry)sap.ui.controller() or inline objectCase A: ControllerExtension.extend() + override + add manifest entry with #stableId key

The key differentiator is the manifest registration format:

  • Case B (most common): controllerName already 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 controllerName in manifest yet — extension is registered programmatically via registerControllerExtensions. The modernization adds a manifest entry with the #stableId key format AND uses ControllerExtension.extend().

Detection commands:

bash
# Check manifest registration formatgrep -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 JSgrep -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:

  1. Identify all controller files referenced by controllerName entries in manifest.json (under keys that do NOT contain #)
  2. 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.jsThese files use deprecated APIs (sap.ui.controller / Controller.extend) but are Fiori Elementscontroller extensions registered via controllerName in the manifest. They require careful manualmodernization to a plain-object return pattern. The linter errors for these files will persist untilthey are modernized manually.
  1. 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
  2. Other skills CAN still process these files for unrelated fixes (e.g., fix-js-globals replacing sap.ui.getCore() calls within method bodies, fix-linter-blind-spots for namespace patterns). Only the sap.ui.controller() / Controller.extend() structure itself is left untouched.
  3. 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>

json
// 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:

  1. The key has format: <FloorplanController>#<StableViewId>
  2. The floorplan controller name matches the view name (e.g., sap.suite.ui.generic.template.ListReport.view.ListReport)
  3. The stable view ID is typically: <AppId>::<FloorplanViewName>::<EntitySet>
  4. Check the app's existing manifest for sap.ui.generic.app / pages configuration 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 uses Controller.extend() with manifest controllerName registration, use Case B below instead.

Convert controller files from sap.ui.controller style to ControllerExtension class.

Before:

javascript
sap.ui.controller("my.app.ext.ListReportExtension", {    onInit: function() {        // lifecycle code    },    onBeforeRebindTable: function(oEvent) {        // framework override    },    onCustomAction: function(oEvent) {        // custom method    }});

After:

javascript
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/ControllerExtension instead of using sap.ui.controller
  • Lifecycle methods (onInit, onExit, onBeforeRendering, onAfterRendering) → inside override
  • Framework callback methods (onBeforeRebindTable, onBeforeRebindChart, onListNavigationExtension, adaptNavigationParameterExtension, etc.) → inside override
  • Custom action methods (handlers for custom buttons/actions) → OUTSIDE override
  • Access extensionAPI via this.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:

json
// In manifest extensions or annotations"Actions": {    "MyAction": {        "id": "MyAction",        "text": "Do Something",        "press": "onCustomAction"    }}

After:

json
"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:

  • press handlers 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)

  1. Identify extension controllers registered via controllerName in manifest (simple key, no #)
  2. Report them to the user — list each file path and note that they require manual modernization
  3. Do not modify these files, their manifest entries, or their handler references
  4. Continue with other modernization tasks

Case A Implementation (registerControllerExtensions → ControllerExtension)

  1. Identify all registerControllerExtensions calls in the codebase (typically in Component.js or helper files)
  2. 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.json under sap.ui5/extends/extensions/sap.ui.controllerExtensions
  3. For each extension controller file: a. Replace sap.ui.controller(...) with sap.ui.define([ControllerExtension], ...) b. Move lifecycle/framework methods into override section c. Keep custom methods outside override d. Update extensionAPI access pattern
  4. For all handler references: a. Search manifest.json for action press handlers using simple method names b. Search XML annotation files for handler references c. Update to .extension.<module>.<method> format
  5. Remove the registerControllerExtensions calls from Component.js
  6. Verify by re-running the linter

Example Full Modernization

Before — Component.js:

javascript
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:

javascript
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):

json
{  "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:

javascript
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 controllerName in 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.json for 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

來源與署名

來源:UI5/plugins-coding-agents位於plugins/ui5-modernization/skills/fix-fiori-elements-extensions提交2b8c4a9

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架