🔧fix-fiori-elements-extensions
- プラグイン
- ui5-modernization
- ソース
- GitHub で見る ↗
説明
UI5 モダナイゼーション作業において、Fiori Elements V2 のコントローラー拡張を処理します。 次のような場合に使用: - 拡張コントローラーが `sap.ui.controller()` を使用しており、かつ manifest に対応する `controllerName` エントリが存在する場合(ケース B → レポートのみ、モダナイズ不要) - 拡張コントローラーが `Controller.extend()` を使用しており、manifest に `controllerName` として登録されている場合(ケース B → レポートのみ、モダナイズ不要) - コードに `registerControllerExtensions` の呼び出しが含まれている場合(ケース A → `ControllerExtension.extend` + `override` への変換が必要) - `manifest.json` の `sap.ui5/extends/extensions/sap.ui.controllerExtensions` に `controllerName` エントリが存在する場合 **ケース B(プレーンオブジェクト形式)** は圧倒的に多いパターンです。 ケース B の場合は、コントローラーファイルを **一切変更しないこと** — レポートに記録して次の処理へ進んでください。 ケース A のみ、実際のコードのモダナイゼーションが必要です。 カスタム(非 Fiori Elements)アプリにおけるプレーンなコントローラー定義で、`sap.ui.controller()` が単独のコントローラー定義として使われているだけの場合は、代わりに `fix-js-globals`(ケース 9)を使用してください。
原文を表示
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.
ユースケース
- ✓Controller.extend() で manifest に登録されている
本文
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
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。