🎨fix-control-renderer
- プラグイン
- ui5-modernization
- ソース
- GitHub で見る ↗
説明
UI5 linterが検出するが自動修正できないControlレンダラーの問題を修正します。 次のような場合に使用: linterが以下のルールを出力している場合: - `no-deprecated-control-renderer-declaration` — レンダラー宣言の欠落、文字列ベースのレンダラー宣言、暗黙的なレンダラーの自動検出(モダンUI5では削除済み)に対応 - `no-deprecated-api` — レンダラーオブジェクト内の`apiVersion:2`の欠落、`oRm.icon()`使用時の`IconPool`インポートの欠落、非推奨の`rerender()`オーバーライドに対応。 **注意:** `Library.init()`の`apiVersion`エラーについては、このスキルではなく`fix-library-init`を使用してください。 - `ui5-class-declaration` — ES6クラスにおける非staticなレンダラープロパティに対応 **トリガーとなるキーワード:** 「missing a renderer declaration」「Deprecated declaration of renderer」「deprecated renderer」「apiVersion」(レンダラーのコンテキスト内)「IconPool」「rerender」「renderer must be a static property」 レガシーなレンダラーパターンを、適切なモジュールインポートを伴うモダンな`apiVersion: 2`形式に変換します。
原文を表示
Fix Control renderer issues that UI5 linter reports but cannot auto-fix. Use this skill when linter outputs these rules: - `no-deprecated-control-renderer-declaration` - For missing renderer declaration, string-based renderer declaration, implicit renderer auto-discovery (removed in modern UI5) - `no-deprecated-api` - For missing apiVersion:2 in renderer objects, missing IconPool import when using oRm.icon(), deprecated rerender() override. NOTE: For Library.init() apiVersion errors, use fix-library-init instead. - `ui5-class-declaration` - For non-static renderer property in ES6 classes Trigger on: "missing a renderer declaration", "Deprecated declaration of renderer", "deprecated renderer", "apiVersion" (in renderer context), "IconPool", "rerender", "renderer must be a static property" Converts legacy renderer patterns to modern apiVersion: 2 format with proper module imports.
ユースケース
- ✓UI5 linterがレンダラー宣言エラーを検出している
- ✓非推奨のレンダラー宣言パターンを修正する
- ✓レンダラーのapiVersionを2に更新する
- ✓IconPoolやrerenderなど非推奨APIを修正する
本文
Fix Control Renderer Issues
This skill fixes Control renderer issues that the UI5 linter detects but cannot auto-fix because they require understanding of the control's rendering behavior and module dependencies.
Linter Rules Handled
| Rule ID | Message Pattern | This Skill's Action |
|---|---|---|
no-deprecated-control-renderer-declaration |
Control '...' is missing a renderer declaration | Add renderer: null or import renderer |
no-deprecated-control-renderer-declaration |
Deprecated declaration of renderer '...' for control '...' | Import renderer module and assign directly |
no-deprecated-api |
Use of deprecated renderer detected. Define explicitly the {apiVersion: 2} | Add apiVersion: 2 to renderer object |
no-deprecated-api |
"sap/ui/core/IconPool" module must be imported when using RenderManager's icon() method | Add IconPool import |
no-deprecated-api |
Override of deprecated method 'rerender' in control '...' | Remove override, move code to lifecycle hooks |
ui5-class-declaration |
The control renderer must be a static property | Make renderer property static |
When to Use
Apply this skill when you see linter output like:
MyControl.js:5:1 error Control 'my.app.control.MyControl' is missing a renderer declaration no-deprecated-control-renderer-declaration
MyControl.js:10:5 error Deprecated declaration of renderer 'my.app.control.MyControlRenderer' for control 'my.app.control.MyControl' no-deprecated-control-renderer-declaration
MyControl.js:15:5 error Use of deprecated renderer detected. Define explicitly the {apiVersion: 2} parameter no-deprecated-api
MyControl.js:20:5 error "sap/ui/core/IconPool" module must be imported when using RenderManager's icon() method no-deprecated-api
MyControl.js:25:5 warning The control renderer of 'MyControl' must be a static property ui5-class-declaration
Background: Why apiVersion: 2?
UI5's rendering framework evolved from an immediate DOM manipulation model (apiVersion 1) to a semantic rendering model (apiVersion 2 and 4). The key differences:
- apiVersion 1 (deprecated): Direct DOM manipulation via
oRm.write(),oRm.writeAttribute(), etc. - apiVersion 2: Semantic methods like
oRm.openStart(),oRm.openEnd(),oRm.text(),oRm.close() - apiVersion 4: Same as 2, with additional performance optimizations (for modern UI5)
Without explicit apiVersion, UI5 assumes legacy rendering which causes synchronous loading and performance issues.
Implicit Renderer Auto-Discovery (Removed in modern UI5)
In UI5 1.x, if a control doesn't declare a renderer property, the framework automatically tries to load a renderer module by appending Renderer to the control's module path. For example, for sap/m/Button, it checks whether sap/m/ButtonRenderer exists — if so, that module is loaded and used as the renderer.
This implicit auto-discovery is removed in modern UI5. Every control must explicitly declare its renderer. If a control relied on auto-discovery and has no renderer property, it will break at runtime. The linter flags this as no-deprecated-control-renderer-declaration with the message "Control '...' is missing a renderer declaration".
The modernization workflow is: check whether a <ControlName>Renderer module exists at the default path → if yes, import it via sap.ui.define → assign it to the renderer property.
Fix Strategy
1. Missing Renderer Declaration
Problem: Control class doesn't declare a renderer at all.
// Before - triggers no-deprecated-control-renderer-declaration
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: {
properties: { ... }
}
// No renderer declaration!
});
});
Fix Strategy A - No rendering needed (control is abstract or uses child controls):
// After - explicitly declare no renderer
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: {
properties: { ... }
},
renderer: null
});
});
Fix Strategy B - Renderer exists in separate file (including implicit auto-discovery):
In UI5 1.x, many controls rely on the framework's implicit auto-discovery — they have no renderer property, but a <ControlName>Renderer.js file exists at the default path and gets loaded automatically. Since this auto-discovery is removed in modern UI5, you need to make the import explicit.
How to find the renderer:
- Derive the expected renderer path: take the control's module path and append
Renderer. Formy/app/control/MyControl, check formy/app/control/MyControlRenderer. - Look for the file in the project (e.g.,
MyControlRenderer.jsin the same directory as the control). - If the renderer file exists, import it and assign it. If it doesn't exist, use Fix Strategy A (
renderer: null) or Fix Strategy C (inline renderer).
// After - import and assign the renderer module
sap.ui.define([
"sap/ui/core/Control",
"./MyControlRenderer"
], function(Control, MyControlRenderer) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: {
properties: { ... }
},
renderer: MyControlRenderer
});
});
Important: After importing the renderer, also check whether the renderer module itself has apiVersion: 2. If not, that's a separate linter finding — see section 3 "Missing apiVersion in Renderer" below.
Fix Strategy C - Add inline renderer:
// After - define renderer inline with apiVersion: 2
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: {
properties: { ... }
},
renderer: {
apiVersion: 2,
render: function(oRm, oControl) {
oRm.openStart("div", oControl);
oRm.class("myControl");
oRm.openEnd();
// Render content here
oRm.close("div");
}
}
});
});
2. String-Based Renderer Declaration
Problem: Renderer declared as string causes synchronous loading.
// Before - triggers no-deprecated-control-renderer-declaration
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: { ... },
renderer: "my.app.control.MyControlRenderer" // String = sync loading!
});
});
Fix Strategy: Import the renderer module and assign directly.
// After - import renderer module
sap.ui.define([
"sap/ui/core/Control",
"my/app/control/MyControlRenderer"
], function(Control, MyControlRenderer) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: { ... },
renderer: MyControlRenderer
});
});
3. Missing apiVersion in Renderer
Problem: Renderer object or function without apiVersion declaration.
// Before - triggers no-deprecated-api
renderer: {
render: function(oRm, oControl) {
oRm.write("<div");
oRm.writeControlData(oControl);
oRm.write(">");
oRm.write("</div>");
}
}
// OR - function without apiVersion
renderer: function(oRm, oControl) {
oRm.write("<div>");
oRm.write("</div>");
}
Fix Strategy: Add apiVersion: 2 and convert to semantic rendering API.
// After - with apiVersion: 2 and semantic methods
renderer: {
apiVersion: 2,
render: function(oRm, oControl) {
oRm.openStart("div", oControl);
oRm.openEnd();
oRm.close("div");
}
}
apiVersion 1 to apiVersion 2 Method Conversions:
| Old Method (apiVersion 1) | New Method (apiVersion 2) |
|---|---|
oRm.write("<tag") |
oRm.openStart("tag") or oRm.voidStart("tag") |
oRm.write(">") |
oRm.openEnd() or oRm.voidEnd() |
oRm.write("</tag>") |
oRm.close("tag") |
oRm.write(text) |
oRm.text(text) |
oRm.writeControlData(oCtrl) |
Pass control as 2nd arg: oRm.openStart("div", oControl) |
oRm.addClass("cls") |
oRm.class("cls") |
oRm.writeAttribute("name", val) |
oRm.attr("name", val) |
For the complete conversion table with examples, read references/renderer-api-mapping.md.
4. Missing IconPool Import
Problem: Using oRm.icon() without importing IconPool.
// Before - triggers no-deprecated-api
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
renderer: {
apiVersion: 2,
render: function(oRm, oControl) {
oRm.openStart("div", oControl);
oRm.openEnd();
oRm.icon("sap-icon://accept"); // IconPool not imported!
oRm.close("div");
}
}
});
});
Fix Strategy: Add IconPool to the imports. The import is required even though it's not directly referenced in code.
// After - IconPool imported
sap.ui.define([
"sap/ui/core/Control",
"sap/ui/core/IconPool" // Required for oRm.icon()
], function(Control, IconPool) {
"use strict";
return Control.extend("my.app.control.MyControl", {
renderer: {
apiVersion: 2,
render: function(oRm, oControl) {
oRm.openStart("div", oControl);
oRm.openEnd();
oRm.icon("sap-icon://accept");
oRm.close("div");
}
}
});
});
5. Deprecated rerender() Override
Problem: Overriding rerender() method no longer works in UI5 1.121+.
// Before - triggers no-deprecated-api
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
renderer: { ... },
rerender: function() {
// Custom logic before rerendering
this._prepareForRender();
Control.prototype.rerender.apply(this, arguments);
// Custom logic after rerendering
this._finishRender();
}
});
});
Fix Strategy: Move pre-render logic to onBeforeRendering() and post-render logic to onAfterRendering().
// After - use lifecycle hooks instead
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
renderer: { ... },
onBeforeRendering: function() {
// Called before each render (initial + re-renders)
this._prepareForRender();
},
onAfterRendering: function() {
// Called after each render (initial + re-renders)
this._finishRender();
}
});
});
Implementation Steps
-
Run linter with --details to get additional context:
npx @ui5/linter --details -
Identify the error pattern from linter output (rule ID + message)
-
Determine the control's rendering needs:
- Does the control need custom rendering?
- Is there an existing separate renderer file? Check the default path:
<ControlName>Renderer.jsin the same directory (UI5 1.x auto-discovery path) - Does the renderer use
oRm.icon()?
-
Apply the appropriate transformation:
- For missing declaration: Check if
<ControlName>Renderer.jsexists at the default path (auto-discovery). If yes, import and assign it. If no, addrenderer: nullor create an inline renderer - For string declaration: Convert to module import
- For missing apiVersion: Add
apiVersion: 2and convert render methods - For IconPool: Add the import to sap.ui.define dependencies
- For rerender override: Move logic to lifecycle hooks
- For non-static: Add
statickeyword (ES6 classes)
- For missing declaration: Check if
-
Verify the fix by re-running the linter
Example Fix Session
Given linter output:
npx @ui5/linter --details
MyControl.js:5:1 error Deprecated declaration of renderer 'my.app.control.MyControlRenderer' for control 'my.app.control.MyControl' no-deprecated-control-renderer-declaration
Details: Defining the control renderer by its name may lead to synchronous loading of the control renderer module.
MyControl.js:20:5 error Use of deprecated renderer detected. Define explicitly the {apiVersion: 2} parameter in the renderer object no-deprecated-api
Details: See: https://ui5.sap.com/#/topic/c9ab34570cc14ea5ab72a6d1a4a03e3f
Before:
sap.ui.define([
"sap/ui/core/Control"
], function(Control) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: {
properties: {
text: { type: "string", defaultValue: "" }
}
},
renderer: "my.app.control.MyControlRenderer"
});
});
// MyControlRenderer.js (separate file)
sap.ui.define([], function() {
"use strict";
var MyControlRenderer = {};
MyControlRenderer.render = function(oRm, oControl) {
oRm.write("<div");
oRm.writeControlData(oControl);
oRm.addClass("myControl");
oRm.writeClasses();
oRm.write(">");
oRm.writeEscaped(oControl.getText());
oRm.write("</div>");
};
return MyControlRenderer;
});
After:
sap.ui.define([
"sap/ui/core/Control",
"./MyControlRenderer"
], function(Control, MyControlRenderer) {
"use strict";
return Control.extend("my.app.control.MyControl", {
metadata: {
properties: {
text: { type: "string", defaultValue: "" }
}
},
renderer: MyControlRenderer
});
});
// MyControlRenderer.js (separate file) - updated
sap.ui.define([], function() {
"use strict";
var MyControlRenderer = {
apiVersion: 2
};
MyControlRenderer.render = function(oRm, oControl) {
oRm.openStart("div", oControl);
oRm.class("myControl");
oRm.openEnd();
oRm.text(oControl.getText());
oRm.close("div");
};
return MyControlRenderer;
});
Notes
-
Controls that extend these base classes do NOT need a renderer declaration:
sap/ui/core/mvc/Viewsap/ui/core/XMLCompositesap/ui/core/webc/WebComponentsap/uxap/BlockBase
-
apiVersion: 4is also valid and provides additional optimizations for modern UI5 -
When converting from apiVersion 1 to 2, ensure all
write()calls are properly converted to semantic methods -
The IconPool import is needed at module load time for icon font registration, even if
IconPoolvariable is not used in code
Related Skills
- fix-js-globals: For
no-globalserrors in non-renderer JavaScript files (e.g., controllers, utilities), use fix-js-globals — it handlessap.ui.definedependency additions and global access replacement - fix-pseudo-modules: If renderer code also has enum or DataType pseudo module imports, use fix-pseudo-modules for those specific issues
- fix-library-init: For
Library.init()/Lib.init()apiVersion errors ("Deprecated call to ... Use the {apiVersion: 2} parameter"), use fix-library-init — it handles library initialization, not renderer objects
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。