Fix JavaScript Global Access (no-globals)
Fixes no-globals errors in JavaScript files that the UI5 linter detects but cannot auto-fix. Run npx @ui5/linter --details to get replacement suggestions and documentation links.
Key Rules — Read Before Applying Any Fix
jQuery/$ globals — preserve jQuery API calls: When fixing jQuery/$ globals, ONLY add the
sap/ui/thirdparty/jquerydependency and replace$withjQuery. Do NOT replace standard jQuery API calls (jQuery.each,jQuery.extend,jQuery.proxy,jQuery.isEmptyObject, etc.) with native JavaScript equivalents. These are standard jQuery methods, not deprecated SAP APIs.Case 9/10 — fix ALL globals in a single pass: When converting a file from
jQuery.sap.declare/requiretosap.ui.define(Case 9 or 10), you MUST also fix ALL other global-access patterns inside the file body in the same pass. There is no second pass — everything must be handled at once. Read the "Apply ALL Applicable Cases in a Single Pass" section below.Dead code — delete, don't import: If a global assignment stores a value that is never read anywhere else in the file (e.g.,
this.BarColor = sap.ui.core.BarColorwherethis.BarColornever appears again), delete the entire statement. Do NOT add an import for it.No intermediate forms for byId in controllers:
sap.ui.getCore().byId("prefix--id")orjQuery("#prefix--id").control(0)inside a controller →this.byId("id")directly. Never leave it asElement.getElementById("prefix--id"). After replacing, remove unusedElementorjQueryimports.merge, not deepExtend:
jQuery.sap.extend(true, ...)→merge()fromsap/base/util/merge. The modulesap/base/util/deepExtenddoes NOT exist.
Fix Strategies by Case
1. Assignments to Global Namespaces
Problem: Code creates custom namespaces on the global sap object.
// Before
sap.ui.demo = sap.ui.demo || {};
sap.ui.demo.myApp = { formatter: function() { ... } };
// After — convert to AMD module
sap.ui.define("sap/ui/demo/myApp", [], function() {
"use strict";
return { formatter: function() { ... } };
});
1b. Global Namespace Assignment Inside sap.ui.define
Problem: File is already in sap.ui.define but still assigns to a global namespace and returns the global reference (leftover from jQuery.sap.declare removal).
Not reported by linter — search manually: grep -rl "your\.project\.namespace\." webapp/ --include="*.js"
// Before
sap.ui.define([], function() {
"use strict";
com.example.app.utils.MyScripts = { runTests: function() { ... } };
return com.example.app.utils.MyScripts;
});
// After — local variable replaces global assignment
sap.ui.define([], function() {
"use strict";
var MyScripts = { runTests: function() { ... } };
return MyScripts;
});
Key rules: Extract short name from namespace end. Use var ShortName (scoping is essential). Replace all references to the full namespace within the file.
1c. Global Namespace Read-Only Reference Inside sap.ui.define
Problem: File reads from a global namespace via variable assignment instead of importing the module as a dependency.
Not reported by linter — search: grep -rn "var .* = your\.project\.namespace\." webapp/ --include="*.js"
// Before
sap.ui.define(["sap/ui/core/mvc/Controller"], function(Controller) {
var Helper = com.example.app.utils.Helper;
// ...
});
// After — add as dependency, remove local var assignment
sap.ui.define([
"com/example/app/utils/Helper",
"sap/ui/core/mvc/Controller"
], function(Helper, Controller) {
// Helper is now available via dependency parameter
});
Key rules:
- Convert dot-notation to slash-notation:
com.example.app.utils.Helper→"com/example/app/utils/Helper" - Add to dependency array at the beginning (see Notes), add corresponding parameter
- Remove the
var X = global.namespace.X;line - If multiple global reads exist, add all as dependencies in one pass
- Verify parameter name matches the module's short name
- Atomicity: Every global→local replacement MUST be paired with a
sap.ui.definedependency. Cycles introduced here are resolved later byfix-cyclic-deps - Post-fix validation: Grep for every introduced variable name — confirm it resolves to a parameter, var/let/const, or
sap.ui.requirecall
Before replacing, read the target module's return statement:
Module's return |
How to use the dependency parameter |
|---|---|
| Returns a class | new MyClass() or MyClass.staticMethod() |
| Returns a wrapper | MyModule.getInstance() |
| Returns an instance | myInstance.method() directly |
| Returns nothing (side-effect only) | Fix the target module first — add a return |
Side-effect modules: If the target ends with }); without a return, open it, replace the global namespace assignment with a local var, and add return varName;. Then import normally in the consuming module.
2. sap.ui.getCore() Calls
Problem: sap.ui.getCore() is deprecated; its methods have moved to dedicated modules.
// Before — standalone init script (NOT a controller)
sap.ui.getCore().attachInit(function() { ... });
// After
sap.ui.define(["sap/ui/core/Core"], function(Core) {
Core.ready().then(function() { ... });
});
Note: Core.ready() is for boot-phase init scripts. Inside controllers, UI5 is already initialized — don't use it there.
Key replacements (full table in references/core-api-replacements.md):
| Deprecated | Module | Call |
|---|---|---|
sap.ui.getCore().attachInit(fn) |
sap/ui/core/Core |
Core.ready().then(fn) |
sap.ui.getCore().byId(id) |
sap/ui/core/Element |
Element.getElementById(id) — in controllers prefer this.byId() |
sap.ui.getCore().getEventBus() |
sap/ui/core/EventBus |
EventBus.getInstance() |
sap.ui.getCore().getLibraryResourceBundle(lib) |
sap/ui/core/Lib |
Lib.getResourceBundleFor(lib) |
Use the UI5 MCP Server's get_api_reference tool for additional Core method replacements.
3. sap.ui.core.Core Direct Access
Add sap/ui/core/Core to the dependency array and remove the global access:
// Before: var Core = sap.ui.core.Core;
// After: add "sap/ui/core/Core" to deps, use Core parameter directly
4. jQuery/$ Global Access
IMPORTANT: The fix is adding the import, NOT replacing jQuery API calls. jQuery.sap.* (with .sap.) = deprecated, must be replaced (Case 4b). jQuery.* (without .sap.) or jQuery(...) = standard jQuery, keep as-is.
// Before
jQuery("#el").addClass("x");
$(".container").css("display", "block");
jQuery.each(items, function(i, item) { ... });
// After — add dependency, rename $ to jQuery, keep all API calls unchanged
sap.ui.define([..., "sap/ui/thirdparty/jquery"], function(..., jQuery) {
jQuery("#el").addClass("x");
jQuery(".container").css("display", "block");
jQuery.each(items, function(i, item) { ... });
});
NEVER replace these standard jQuery methods — they are not deprecated in UI5: jQuery.each, jQuery.extend, jQuery.proxy, jQuery.isEmptyObject, jQuery.isArray, jQuery.inArray, jQuery.grep, jQuery.map, jQuery.type, jQuery.trim.
4a. jQuery DOM Lookup for UI5 Controls → this.byId()
Problem: jQuery("#prefix--id").control(0) or Element.closestTo() to get a UI5 control inside a controller.
Detection patterns — all collapse to this.byId("<local-id>"):
jQuery("#<anything>--<id>").control(0)Element.closestTo(jQuery("#<anything>--<id>")[0])sap.ui.getCore().byId("<full-id>")where ID contains view prefixElement.getElementById("<full-id>")where ID contains--
The local ID is the part after the last --. After replacing, remove unused jQuery/Element imports.
4b. jQuery.sap.* Utility Access
Problem: jQuery.sap.* calls are deprecated UI5 utilities with dedicated replacement modules.
// Before
jQuery.sap.log.info("msg");
var sId = jQuery.sap.uid();
// After
sap.ui.define([..., "sap/base/Log", "sap/base/util/uid"], function(..., Log, uid) {
Log.info("msg");
var sId = uid();
});
Run npx @ui5/linter --details for suggested replacements. Full table in references/core-api-replacements.md.
jQuery.sap.extend decision:
- Deep copy (
trueas first arg) →sap/base/util/merge→merge({}, obj1, obj2) - Flat objects (single-level properties) →
Object.assign({}, obj1, obj2)(no import needed) - NEVER convert to
jQuery.extend(...)(introduces unnecessary dependency) - NEVER use
sap/base/util/deepExtend(does NOT exist)
5. Conditional/Probing Global Access
Problem: Code checks if a global exists: if (sap.ui.fl && sap.ui.fl.Utils) { ... }
Fix: For always-available modules, add as sap.ui.define dependency. For truly optional modules, use synchronous sap.ui.require:
var FlUtils = sap.ui.require("sap/ui/fl/Utils");
if (FlUtils) { FlUtils.getComponentClassName(this); }
For lazy loading, use async: sap.ui.require(["module/path"], function(Mod) { ... }).
6. Custom Namespace Definitions
Same pattern as Case 1 but for non-SAP namespaces (window.mycompany.myapp = {...}). Convert to sap.ui.define module returning the object. Consumers import via dependency.
7. Binding Type Strings Without Import
// Before — global reference as string
value: { path: "/amount", type: "sap.ui.model.type.Float" }
// After — import type module, use class reference
value: { path: "/amount", type: new FloatType() } // FloatType from "sap/ui/model/type/Float"
8. Delete Expressions
delete sap.ui.core.someTempProperty — usually a code smell. Remove entirely or use a local object.
9. sap.ui.controller() — Controller Definition via Global Factory
Scope: Plain controller definitions. NOT Fiori Elements V2 extensions (use fix-fiori-elements-extensions for those).
Detection: grep -rn 'sap\.ui\.controller(' webapp/ --include="*.js"
- Two arguments
sap.ui.controller("name", {...})= definition → fix here - One argument
sap.ui.controller("name")= instance lookup → document inMODERNIZATION-ISSUES.md
Pattern A: Inside existing sap.ui.define
// Before
sap.ui.define(["sap/m/MessageBox"], function(MessageBox) {
return sap.ui.controller("my.app.controller.Main", { ... });
});
// After — add Controller dep, replace factory with extend
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageBox"
], function(Controller, MessageBox) {
return Controller.extend("my.app.controller.Main", { ... });
});
Pattern B: Without sap.ui.define (legacy module system)
// Before
jQuery.sap.declare("my.app.controller.Detail");
jQuery.sap.require("sap.ui.core.mvc.Controller");
sap.ui.controller("my.app.controller.Detail", { onInit: function() { ... } });
// After
sap.ui.define(["sap/ui/core/mvc/Controller"], function(Controller) {
"use strict";
return Controller.extend("my.app.controller.Detail", { onInit: function() { ... } });
});
Steps: Remove jQuery.sap.declare/require. Wrap in sap.ui.define. Convert dot-notation deps to slash-notation. Replace sap.ui.controller with Controller.extend. Add return. Add "use strict". Apply all inline fixes to file body (see "Apply ALL" section).
Edge cases
- Missing
return:Controller.extend()only returns the class — always addreturnbefore it - Module-level variables before definition: Keep as-is, just wrap the extend call with
return - Controller name must match file path: Keep existing name even if mismatched (may be intentional)
- Mixed file (definition + instance lookups): Fix the definition, document instance lookups in
MODERNIZATION-ISSUES.md
10. jQuery.sap.declare/require — Legacy Module Definitions
Same structural conversion as Case 9 Pattern B but for non-controller modules:
// Before
jQuery.sap.declare("my.app.util.Formatter");
jQuery.sap.require("sap.ui.core.format.DateFormat");
my.app.util.Formatter = { formatDate: function(oDate) { ... } };
// After
sap.ui.define(["sap/ui/core/format/DateFormat"], function(DateFormat) {
"use strict";
return { formatDate: function(oDate) { ... } };
});
Key rules: Remove jQuery.sap.declare. Convert jQuery.sap.require to deps. Remove global assignment, return the object. If already has sap.ui.define, merge remaining requires into existing dep array. Dynamic/conditional requires → sap.ui.require(["..."], callback). Multiple declares or unclear exports → flag for manual review. Apply all inline fixes (see "Apply ALL" section).
11. Runtime Globals as Module Imports
Problem: Runtime modules like sap.ushell.Container accessed via global namespace chains.
// Before
if (sap.ushell && sap.ushell.Container) {
sap.ushell.Container.getService("CrossApplicationNavigation");
}
// After — add as dependency
sap.ui.define(["sap/ushell/Container", ...], function(Container, ...) {
if (Container && Container.getService) {
Container.getService("CrossApplicationNavigation");
}
});
Test-side: Stub the imported module directly with sinon. Do NOT set up global namespace chains (window.sap.ushell = {...}). sinon and QUnit are Test Starter globals — no import needed.
sap.ui.define(["sap/ushell/Container"], function(Container) {
var oSandbox = sinon.createSandbox();
QUnit.test("...", function(assert) {
oSandbox.stub(Container, "setDirtyFlag");
// ...
});
});
12. Sync XHR Guards After jQuery.sap.sjax Modernization
After modernizing jQuery.sap.sjax to native XMLHttpRequest, always guard xhr.responseText with a status check:
var xhr = new XMLHttpRequest();
xhr.open("GET", sUrl, false);
xhr.send();
if (xhr.readyState === 4 && xhr.status === 200) {
var oData = JSON.parse(xhr.responseText);
} else {
Log.error("Failed to load: " + sUrl);
}
| Context | Fallback on failure |
|---|---|
| Mock server response handler | oXhr.respondJSON(200, {}, JSON.stringify({"d": {"results": []}})) |
| JSON.parse of response | Return {} (lets existing if (oResponse.data) guards work) |
| Init-time config loading | Early return with Log.error(...) |
CRITICAL: Apply ALL Applicable Cases in a Single Pass
When a file triggers Case 9 or 10, fix ALL global-access patterns in the same pass:
jQuery("#...")orjQuery(...)calls? → Case 4a or 4sap.ui.getCore().byId(...)calls? → Case 2, then Case 4a if in controllerjQuery.sap.*calls? → Case 4bsap.ui.model.*,sap.m.*,sap.ui.core.*inline class references? → dependency imports- App-namespace global references? → dependency imports
- Unused imports after replacements? → remove from dep array and parameters
this.X = importedModule.Xwherethis.Xis never read elsewhere? → DELETE (dead code)
Implementation Steps
- Run
npx @ui5/linter --detailsto get replacement suggestions - Identify error pattern and determine case type
- Apply the appropriate transformation (add deps, replace globals, remove dead code)
- After replacing jQuery DOM lookups with
this.byId(), remove unused imports - Verify no other files depend on a removed global assignment
Notes
Dependency insertion position — critical: Always add new dependencies at the beginning of the array (and corresponding parameters at the beginning of the function). Many legacy files have dep/param count mismatches (trailing side-effect imports without parameters). Inserting at the end shifts existing mappings; inserting at the beginning preserves them.
// Before (3 deps, 2 params — mismatch is common in legacy code) sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast", "some/sideEffect/Module" ], function(Controller, MessageToast) { ... }); // After — new dep added at BEGINNING sap.ui.define(["sap/ui/core/Element", "sap/ui/core/mvc/Controller", "sap/m/MessageToast", "some/sideEffect/Module" ], function(Element, Controller, MessageToast) { ... });Parameter names should match the module's default export name (e.g.,
Logforsap/base/Log)QUnit,sinonare intentionally allowed globals in test filessap.ui.define,sap.ui.require,sap.ui.loader.configare allowed globalsUse
sap.ui.require("module/path")(sync, returns undefined if not loaded) for optional depsUse
sap.ui.require(["module/path"], callback)(async) for lazy loading
Example Fix Session
For a comprehensive before/after example combining multiple case types, read references/example-fix-session.md.
Related Skills
- fix-fiori-elements-extensions: For
sap.ui.controller()in Fiori Elements V2 apps withregisterControllerExtensionsor manifestsap.ui.controllerExtensions - fix-pseudo-modules: For
no-pseudo-modulesandno-implicit-globalserrors (enum imports, DataType imports, OData expression functions) - fix-control-renderer: For renderer-specific issues (
no-deprecated-control-renderer-declaration,apiVersion,IconPool,rerender) - fix-xml-globals: For
no-globalsin XML views/fragments (formatters, event handlers viacore:require) - fix-linter-blind-spots: For runtime-breaking global namespace patterns the linter doesn't detect (app-specific namespaces outside
sap.*). Cases 1b and 1c overlap with patterns 1-4 in that skill. - fix-cyclic-deps: When Case 1c fixes would create cyclic dependencies, use lazy
sap.ui.requireinstead of normalsap.ui.definedeps