fix-fiori-elements-extensions について
fix-fiori-elements-extensionsは、UI5のモダナイズ時に非推奨のFiori Elements V2コントローラー拡張を扱うスキルです。マニフェスト登録は変更せず報告し、プログラム登録はControllerExtensionとマニフェスト登録へ移行します。
主な機能
- 012種類の拡張登録パターンを判別
- 02マニフェスト登録のコントローラーは変更せず報告
- 03プログラム登録をControllerExtensionへ移行
- 04登録をマニフェストに移動
- 05ハンドラー参照を更新しLinterで確認
活用シーン
- 01非推奨コントローラーAPIを使うUI5アプリのモダナイズ
- 02手動でのモダナイズが必要な拡張の特定
- 03registerControllerExtensionsの利用箇所の移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ui5/ plugins-coding-agents --skill fix-fiori-elements-extensionsインストール後、Claude Code で/fix-fiori-elements-extensionsを入力して読み込まれたか確認します。
詳細
Fiori Elements コントローラー拡張の修正
このスキルは、UI5 のモダナイゼーションにおける Fiori Elements V2 コントローラー拡張に対応します。対応方法が異なるケースが 2 つあります。
- ケース B(最も一般的):manifest の
controllerNameで登録された拡張 → 報告のみ。コントローラーファイルは変更しないでください。 そのまま残し、手動で対応が必要なファイルをユーザーに伝えます。 - ケース A(まれ):
registerControllerExtensionsを使用する拡張 →ControllerExtension.extend()と manifest 登録へのモダナイゼーションをすべて実施します。
対応する Linter ルール
| ルール ID | メッセージパターン | このスキルの対応 |
|---|---|---|
no-deprecated-api | 非推奨の registerControllerExtensions の使用 | ケース A:manifest + ControllerExtension にモダナイズ |
no-deprecated-api | 非推奨の sap.ui.controller の使用 | ケース B(manifest に controllerName がある場合):報告のみ。修正しない;ケース A(registerControllerExtensions の場合):ControllerExtension クラス |
no-deprecated-api | 非推奨の Controller(sap/ui/core/mvc/Controller からのインポート)の使用 | ケース B:報告のみ。修正しない |
クイック判定:どちらのケースに該当しますか?
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
使用するタイミング
ケース B(最も一般的) — manifest にシンプルなキーの controllerName エントリーがすでにあり、コントローラーファイルが sap.ui.controller() または 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"
}
}
ソースコントローラーは、次のいずれかの形式です。
// 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", { ... });
});
ケース B を引き起こす Linter の出力:
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
ケース B での対応:これらのファイルは変更しないでください。 ユーザーに報告し、残りのモダナイゼーションを続けます。
ケース A(まれで、古いアプリのみ) — 次のような Linter の出力がある場合に適用します。
Component.js:25:5 error Use of deprecated function 'registerControllerExtensions' no-deprecated-api
または、アプリに次のようなパターンがある場合:
// 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() { ... }
});
修正方法
対応は、manifest で拡張がどのように登録されているかによって異なります。作業を進める前に、該当するケースを判定してください。
ケースの判定
| Manifest の登録方法 | ソースコード(いずれか) | 対応 |
|---|---|---|
manifest の controllerName(シンプルなキー。#stableId なし) | sap.ui.controller()、Controller.extend()、またはプレーンオブジェクト | ケース B:報告のみ — ファイルは変更しないでください |
JS の registerControllerExtensions(manifest エントリーなし) | sap.ui.controller() またはインラインオブジェクト | ケース A:ControllerExtension.extend() + override + #stableId キーで manifest エントリーを追加 |
重要な違いは、manifest の登録形式です。
- ケース B(最も一般的):
controllerNameが、manifest のシンプルなキー(例:"sap.suite.ui.generic.template.ListReport.view.ListReport": { "controllerName": "..." })の下にすでにあります。これらのファイルをモダナイズしないでください。 報告して、次に進みます。 - ケース A:manifest に
controllerNameがまだありません — 拡張はregisterControllerExtensionsを使ってプログラムで登録されています。モダナイゼーションでは、#stableId形式のキーを使った manifest エントリーを追加し、ControllerExtension.extend()を使用します。
検出コマンド:
# 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"
ケース B:報告してスキップする(最も一般的)
マニフェストの controllerName で登録された拡張機能(#stableId を含まない単純なキー)の場合、コントローラーファイルは変更しないでください。慎重な手動でのモダナイズが必要なため、開発者に委ねます。
手順:
- manifest.json の
controllerNameエントリー(キーに#を含まないもの)から参照されているコントローラーファイルをすべて特定します - 次の形式でユーザーに報告します:
⚠️ 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.
- このスキルでは、次のことを行わないでください:
- コントローラー拡張パターンを変更するためにコントローラーの .js ファイルを変更する(プレーンオブジェクトへの変換は行わない)
- マニフェストの登録を変更する
- リンターの無効化コメントを追加する
- プレーンオブジェクトへのモダナイズパターンを試みる
- 他のスキルでは、無関係な修正のためにこれらのファイルを引き続き処理できます(例:メソッド本体内の
sap.ui.getCore()呼び出しを置き換えるfix-js-globals、名前空間パターンに対応するfix-linter-blind-spots)。変更しないのは、sap.ui.controller()/Controller.extend()の構造自体のみです。 - 残りのモダナイズ(他のスキル、その他のリンター指摘事項)を続行します
ケース A:ステップ 1 — 登録を JS から manifest.json に移動する
ケース A(JS 内で registerControllerExtensions を使用する拡張機能)のみ対象です。
コントローラー拡張の登録を JavaScript コードから sap.ui.controllerExtensions 配下のマニフェストに移動します。
キーの形式:<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"
}
}
}
}
}
}
正しいキーの見つけ方:
- キーの形式は
<FloorplanController>#<StableViewId>です - フロアプランコントローラー名はビュー名と一致します(例:
sap.suite.ui.generic.template.ListReport.view.ListReport) - 安定したビュー ID は通常、
<AppId>::<FloorplanViewName>::<EntitySet>です - アプリの既存のマニフェストで
sap.ui.generic.app/pagesの設定を確認し、エンティティセットとページ ID を見つけます
ケース A:ステップ 2 — コントローラーファイルを再構成する
⚠️ このステップはケース A(
sap.ui.controller()ファクトリーを使用する拡張機能)のみに適用されます。 拡張機能がすでにController.extend()とマニフェストのcontrollerName登録を使用している場合は、代わりに以下のケース Bを使用してください。
コントローラーファイルを sap.ui.controller スタイルから ControllerExtension クラスに変換します。
変更前:
sap.ui.controller("my.app.ext.ListReportExtension", {
onInit: function() {
// lifecycle code
},
onBeforeRebindTable: function(oEvent) {
// framework override
},
onCustomAction: function(oEvent) {
// custom method
}
});
変更後:
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
}
});
});
再構成の主なルール:
sap.ui.controllerの代わりにsap/ui/core/mvc/ControllerExtensionを拡張します- ライフサイクルメソッド(
onInit、onExit、onBeforeRendering、onAfterRendering)→override内に配置します - フレームワークのコールバックメソッド(
onBeforeRebindTable、onBeforeRebindChart、onListNavigationExtension、adaptNavigationParameterExtensionなど)→override内に配置します - カスタムアクションメソッド(カスタムボタン/アクションのハンドラー)→
overrideの外に配置します this.extensionAPIを使ってextensionAPIにアクセスします(フレームワークが拡張インスタンスに注入します)
ケース A:ステップ 3 — ハンドラー参照を更新する
マニフェストと XML アノテーションで、すべてのイベントハンドラー参照を旧形式から新しい修飾形式に更新します。
変更前:
// In manifest extensions or annotations
"Actions": {
"MyAction": {
"id": "MyAction",
"text": "Do Something",
"press": "onCustomAction"
}
}
変更後:
"Actions": {
"MyAction": {
"id": "MyAction",
"text": "Do Something",
"press": ".extension.my.app.ext.ListReportExtension.onCustomAction"
}
}
参照形式:.extension.<full.controller.name>.<methodName>
重要:ControllerExtension.extend("my.app.ext.ListReportExtension", ...) およびマニフェストの controllerName で宣言されているドット区切りのモジュール名を使用してください。sap.ui.define のインポートで使用されるスラッシュ区切りのパス(例:"my/app/ext/ListReportExtension")は使用しないでください。
これは次のものに適用されます:
- マニフェストのアクション定義にある
pressハンドラー - カスタム列/セクションのハンドラー参照
- 以前はメソッド名のみを使用していたすべてのイベントハンドラー参照
実装手順
ケース B の実装(報告のみ)
- マニフェストの
controllerName(単純なキーで、#を含まないもの)を通じて登録された拡張コントローラーを特定します - それらをユーザーに報告します。各ファイルパスを列挙し、手動でのモダナイズが必要であることを明記します
- これらのファイル、マニフェストエントリー、ハンドラー参照は変更しないでください
- 他のモダナイズ作業を続行します
ケース A の実装(registerControllerExtensions → ControllerExtension)
- コードベース内のすべての
registerControllerExtensions呼び出しを特定します(通常は Component.js またはヘルパーファイル内にあります) - 登録ごとに:
a. フロアプランのコントローラー名とビューの安定 ID を特定します
b. 拡張コントローラーの名前/パスを特定します
c.
manifest.jsonのsap.ui5/extends/extensions/sap.ui.controllerExtensionsにエントリーを追加します - 拡張コントローラーファイルごとに:
a.
sap.ui.controller(...)をsap.ui.define([ControllerExtension], ...)に置き換えます b. ライフサイクル/フレームワークメソッドをoverrideセクションに移動します c. カスタムメソッドはoverrideの外に置きます d.extensionAPIのアクセスパターンを更新します - すべてのハンドラー参照について:
a. manifest.json で、シンプルなメソッド名を使ったアクションの
pressハンドラーを検索します b. XML アノテーションファイルでハンドラー参照を検索します c..extension.<module>.<method>形式に更新します - Component.js から
registerControllerExtensions呼び出しを削除します - 確認として、リンターを再実行します
完全なモダナイゼーションの例
変更前 — 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
}
}
);
}
});
});
変更後 — 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
});
});
変更後 — manifest.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"
}
}
}
}
}
}
変更後 — 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"
}
});
});
注意事項
- このモダナイゼーションは、Fiori Elements V2 テンプレートアプリ(
sap.suite.ui.generic.templateを使用)にのみ適用されます - Fiori Elements V4(
sap.fe.templatesを使用)では異なる拡張メカニズムが使われます。このスキルは適用されません - 実際には ケース B のほうがはるかに一般的です。manifest の
controllerNameで登録された拡張が該当します。これらは自動でモダナイズされません。エージェントは該当箇所を報告して処理を続行します - ケース A は、
registerControllerExtensionsからモダナイズされていない古いアプリでのみ発生します - 安定ビュー ID の形式はフロアプランと設定によって異なります。実行中のアプリの DOM またはフロアプランのドキュメントを確認してください
- アプリがハンドラー参照に
templateSpecific.jsonを使用している場合は、それらも更新する必要があります(ケース A のみ) - ケース A のモダナイゼーション後、拡張コントローラーファイルはモジュール名に一致するパスに配置してください
関連スキル
- fix-js-globals:カスタムアプリの通常の
sap.ui.controller()定義(ケース 9)、または拡張コントローラーでもグローバルアクセスパターン(sap.ui.getCore()、jQuery.sap.*)を使用している場合に使います。コントローラー拡張パターン自体とは無関係な問題(メソッド本体内のグローバル API 使用の置き換えなど)については、ケース B のコントローラーファイルにもほかのスキルを適用できます - fix-manifest-json:コントローラー拡張の manifest 変更は追加的なものです。fix-manifest-json によるバージョン/ルーティングの更新と競合しません
- fix-linter-blind-spots:ケース B のコントローラーファイル内の名前空間パターンや、その他の検出対象外の問題も引き続き修正できます