mcpField
F
fix-fiori-elements-extensions

fix-fiori-elements-extensions

github.com/ui5开发44156 次安装1 周前更新
代码仓库

关于 fix-fiori-elements-extensions

fix-fiori-elements-extensions 用于 UI5 现代化期间处理已弃用的 Fiori Elements V2 控制器扩展。它会区分清单注册与程序化注册:前者仅报告不修改,后者迁移至 ControllerExtension 和清单注册。

主要功能

  1. 01区分两种扩展注册模式
  2. 02仅报告清单注册的控制器,不修改
  3. 03将程序化注册迁移到 ControllerExtension
  4. 04将注册项移入清单
  5. 05更新处理器引用并运行 Linter 检查

适用场景

  1. 01处理使用已弃用控制器 API 的 UI5 应用
  2. 02识别需要手动现代化的扩展
  3. 03迁移 registerControllerExtensions 用法

安装

复制下面的命令,在终端中运行。

$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 控制器扩展。分为两种情况,需采取不同操作:

  • 情况 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 的简单键),请勿修改控制器文件。这些文件需要谨慎地手动现代化,留给开发者处理。

步骤:

  1. 找出 manifest.json 中 controllerName 条目引用的所有控制器文件(位于不含 # 的键下)
  2. 按以下格式向用户报告:
⚠️ 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.
  1. 在此技能中请勿:
    • 修改控制器 .js 文件以更改控制器扩展模式(不要转换为普通对象)
    • 更改清单注册
    • 添加禁用代码检查器的注释
    • 尝试普通对象现代化模式
  2. 其他技能仍可处理这些文件中的无关修复(例如,fix-js-globals 可替换方法体中的 sap.ui.getCore() 调用,fix-linter-blind-spots 可处理命名空间模式)。只有 sap.ui.controller() / Controller.extend() 结构本身保持不变。
  3. 继续执行其余现代化工作(其他技能、其他代码检查器发现项)

情况 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"
          }
        }
      }
    }
  }
}

查找正确的键:

  1. 键的格式为:<FloorplanController>#<StableViewId>
  2. Floorplan 控制器名称与视图名称相同(例如,sap.suite.ui.generic.template.ListReport.view.ListReport)
  3. 稳定视图 ID 通常为:<AppId>::<FloorplanViewName>::<EntitySet>
  4. 检查应用现有清单中的 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/core/mvc/ControllerExtension,而不是使用 sap.ui.controller
  • 生命周期方法(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 实施(仅报告)
  1. 识别通过清单中的 controllerName 注册的扩展控制器(简单键,不含 #)
  2. 向用户报告——列出每个文件路径,并注明需要手动现代化
  3. 不要修改这些文件、对应的清单条目或处理函数引用
  4. 继续执行其他现代化任务
Case A 实现(registerControllerExtensions → ControllerExtension)
  1. 识别代码库中的所有 registerControllerExtensions 调用(通常位于 Component.js 或辅助文件中)
  2. 针对每项注册: a. 确定 floorplan 控制器名称和视图稳定 ID b. 确定扩展控制器名称/路径 c. 在 manifest.json 的 sap.ui5/extends/extensions/sap.ui.controllerExtensions 下添加条目
  3. 针对每个扩展控制器文件: a. 将 sap.ui.controller(...) 替换为 sap.ui.define([ControllerExtension], ...) b. 将生命周期/框架方法移至 override 部分 c. 自定义方法保留在 override 外 d. 更新 extensionAPI 访问方式
  4. 针对所有处理程序引用: a. 在 manifest.json 中搜索使用简单方法名的 action press 处理程序 b. 在 XML 注解文件中搜索处理程序引用 c. 更新为 .extension.<module>.<method> 格式
  5. 从 Component.js 中移除 registerControllerExtensions 调用
  6. 验证:重新运行 linter

完整改造示例

之前 — 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)采用不同的扩展机制 — 此技能不适用
  • 实际中 Case B 更常见 — 通过 manifest 中的 controllerName 注册的扩展。这些扩展不会自动改造 — Agent 会报告它们并继续处理
  • Case A 仅出现在从未将 registerControllerExtensions 改造为较新方式的旧应用中
  • 稳定视图 ID 的格式因 floorplan 和配置而异 — 请检查运行中应用的 DOM 或 floorplan 文档
  • 如果应用使用 templateSpecific.json 引用处理程序,也必须更新这些引用(仅适用于 Case A)
  • 完成 Case A 改造后,扩展控制器文件应放在与其模块名称相匹配的路径下

相关技能

  • fix-js-globals:用于自定义应用中的纯 sap.ui.controller() 定义(Case 9),或扩展控制器也使用全局访问模式(sap.ui.getCore()、jQuery.sap.*)的情况。对于与控制器扩展模式本身无关的问题,其他技能也可以应用于 Case B 控制器文件(例如,替换方法体中的全局 API 用法)。
  • fix-manifest-json:控制器扩展的 manifest 更改是增量式的 — 与 fix-manifest-json 中的版本/路由更新不冲突
  • fix-linter-blind-spots:仍可修复 Case B 控制器文件中的命名空间模式和其他盲点

评论

支持 Markdown