| name | ui5-best-practices |
| description | UI5 development best practices and coding standards derived exclusively from official SAP UI5 guidelines. Use when writing UI5 applications to ensure modern, maintainable code following SAP standards. Covers: async module loading (sap.ui.define, ES6 imports, core:require), ComponentSupport initialization, data binding with OData types, i18n management, CSP compliance (no inline scripts), TypeScript event types (UI5 >= 1.115.0), MCP tooling (get_api_reference, run_ui5_linter), CAP integration patterns, and form creation rules (never SimpleForm, always Form with ColumnLayout).
Keywords: ui5 coding standards, async loading, sap.ui.define, data binding, odata types, i18n translation, CSP no inline scripts, TypeScript event handlers, Button$PressEvent, ui5 linter, API reference, ComponentSupport, form layout, ColumnLayout, CAP integration, cds watch
|
UI5 Best Practices and Coding Standards
Overview
This skill enforces UI5 development standards derived from official SAP guidelines. It covers the four critical areas: coding guidelines, tooling integration, CAP integration, and form creation rules.
1. Module Loading - CRITICAL
Never Use Global Access
NEVER access UI5 framework objects globally (e.g., sap.m.Button). Always declare dependencies explicitly for asynchronous loading.
JavaScript
var oButton = new sap.m.Button();
sap.ui.define(["sap/m/Button"], function(Button) {
var oButton = new Button();
});
sap.ui.require(["sap/m/MessageBox"], function(MessageBox) {
MessageBox.show("Hello");
});
TypeScript
const button: sap.m.Button;
import Button from "sap/m/Button";
const button: Button;
XML Views
<m:Button text="Click Me"/>
<ObjectListItem
core:require="{
Currency: 'sap/ui/model/type/Currency'
}"
number="{
parts: ['invoice>Price', 'view>/currency'],
type: 'Currency'
}"/>
Why: Ensures proper async loading, improves performance in production builds.
Reference: UI5 documentation page "Require Modules in XML View and Fragment"
2. Component Initialization
Use sap/ui/core/ComponentSupport for declarative initialization of the initial (root) component:
<script id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-on-init="module:sap/ui/core/ComponentSupport"
data-sap-ui-async="true"
data-sap-ui-resource-roots='{ "my.app": "./" }'>
</script>
<body class="sapUiBody">
<div data-sap-ui-component
data-name="my.app"
data-id="container">
</div>
</body>
Reference: UI5 documentation page "Declarative API for Initial Components"
Note: Nested components should be managed via component usages (declared in the manifest.json of the containing component)
3. Data Binding Best Practices
Always Use Built-in Data Types
ALWAYS use data binding in views to connect UI controls to data or i18n models.
Priority order:
- OData types (
sap/ui/model/odata/type/*) - Preferred
- Simple types (
sap/ui/model/type/*) - Only when no OData equivalent
- Custom types - For special two-way binding scenarios or complex validation
- Custom formatters - Only for unique business logic (one-way binding)
<Text text="{path: 'price', formatter: '.formatCurrency'}"/>
<Text text="{
path: 'price',
type: 'sap.ui.model.odata.type.Decimal',
formatOptions: {
style: 'currency',
currencyCode: 'EUR'
}
}"/>
<Text text="{
path: 'quantity',
type: 'sap.ui.model.odata.type.Decimal',
formatOptions: {
groupingEnabled: true
}
}"/>
Common OData Types:
sap.ui.model.odata.type.Decimal - Numbers with decimals
sap.ui.model.odata.type.String - Text with length constraints
sap.ui.model.odata.type.DateTime - Date and time
Common Simple Types (use only when no OData equivalent):
sap.ui.model.type.DateInterval - Date ranges
sap.ui.model.type.FileSize - File size formatting
Example: For number formatting with thousands separator, prefer sap.ui.model.odata.type.Decimal with formatOptions: {groupingEnabled: true} over sap.ui.model.type.Integer or a custom formatter.
When to Use Custom Types
Custom types are needed for special two-way binding scenarios where built-in types don't provide the required validation or conversion logic.
Example: Custom Type for Email Validation with Two-Way Binding
sap.ui.define(["sap/ui/model/SimpleType"], function(SimpleType) {
return SimpleType.extend("my.app.type.EmailType", {
formatValue: function(oValue) {
return oValue;
},
parseValue: function(oValue) {
return oValue;
},
validateValue: function(oValue) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (oValue && !emailRegex.test(oValue)) {
throw new sap.ui.model.ValidateException("Invalid email format");
}
}
});
});
Usage in View:
<Input value="{path: 'email', formatter: '.validateEmail'}"/>
<Input
core:require="{EmailType: 'my/app/type/EmailType'}"
value="{
path: 'email',
type: 'EmailType'
}"/>
Why Custom Types:
- ✅ Two-way binding support (formatValue + parseValue + validateValue)
- ✅ Real-time validation as user types
- ✅ Model updates immediately on valid input
- ❌ Custom formatters only work for one-way (display) binding
Data Binding in Views
ALWAYS use data binding to connect controls to models:
<Input value="{/customer/name}"/>
<List items="{/products}">
<StandardListItem title="{name}" description="{price}"/>
</List>
<Text text="{= ${quantity} * ${price} }" visible="{= ${stock} > 0 }"/>
4. Internationalization (i18n)
Translation Workflow Guidelines
When modifying .properties files, follow the appropriate workflow based on your project type:
For development and testing:
- Update
i18n.properties (base file) only
- Changes will be reflected immediately for development
Production translation workflows:
- SAP S/4HANA apps: NEVER manually edit localized files (
i18n_de.properties, i18n_fr.properties, etc.)
- Translation is handled through SAP's internal translation process
- Apps using SAP Translation Hub or Translation Export/Import (TEW): DO NOT touch localized files
- Translations are generated automatically from the base file
- Manually translated apps only: Apply changes to all locale files to maintain consistency
Why: Professional translation workflows generate localized files from the base i18n.properties file. Manual edits to localized files will be overwritten during the translation process.
5. Security - Content Security Policy
Never Use Inline Scripts or Styles
NEVER use inline scripts or inline styles in HTML. They violate the recommended CSP settings for UI5 applications.
<script>
alert("Hello");
</script>
<style>
.error { color: red; }
</style>
<div style="color: red;">Styled text</div>
<script src="controller/Main.controller.js"></script>
<link rel="stylesheet" href="css/style.css">
<div class="errorText">Styled text</div>
Requirements:
- All application logic must reside in dedicated JS or TS files
- All styling must reside in dedicated CSS files
- Inline
<script> tags violate CSP
- Inline
<style> tags violate CSP
- Inline
style attributes violate CSP
Reference: UI5 documentation page "Content Security Policy"
6. TypeScript Event Handling (UI5 >= 1.115.0)
Use Control-Specific Event Types
For UI5 1.115.0 and above, import and use the specific event type from the control's module.
Pattern: <ControlName>$<EventName>Event (notice the "Event" suffix)
import { Button$PressEvent } from "sap/m/Button";
import { Table$RowSelectionChangeEvent } from "sap/ui/table/Table";
import Controller from "sap/ui/core/mvc/Controller";
export default class MainController extends Controller {
public onPress(event: Button$PressEvent): void {
const button = event.getSource();
}
public onRowSelectionChange(event: Table$RowSelectionChangeEvent): void {
const selectedContext = event.getParameter("rowContext");
}
}
Fallback for Older Versions
UI5 < 1.115.0: Control-specific event types are NOT available. Use the generic Event type:
import Event from "sap/ui/base/Event";
import Controller from "sap/ui/core/mvc/Controller";
export default class MainController extends Controller {
public onPress(event: Event): void {
}
}
Benefits: Static type checking and autocompletion for event parameters without manual casting.
7. MCP Tooling Integration
API Lookup
ALWAYS use the get_api_reference tool to get information on UI5 controls and APIs. This provides direct access to the official UI5 API Reference for the UI5 version in use.
Usage: get_api_reference with project path
Returns: Official API documentation for controls, classes, and namespaces
Code Validation
ALWAYS use the run_ui5_linter tool to identify issues. It detects deprecated APIs, accessibility issues, and other potential bugs.
Usage: run_ui5_linter with project path
Returns: List of issues with severity levels
Code Fixes
To apply fixes suggested by the linter:
- ALWAYS confirm with the user first
- Use the
fix parameter of the run_ui5_linter tool
- The tool automatically corrects some identified issues
- Manually fix remaining issues using the context information provided
Local Server Behavior
When interacting with the UI5 CLI's development server:
CRITICAL: The server does NOT serve a default index file.
http://localhost:8080/
http://localhost:8080/index.html
Code Quality Checks
After making code changes, ALWAYS run the project's linter if available:
npm run lint
npm run eslint
eslint .
npm run ui5-lint
ui5lint .
Why: Linters catch common issues before committing:
- Missing imports or type errors
- Formatting inconsistencies
- Deprecated API usage
- Code style violations
Fix all linting errors before committing.
8. CAP Integration
When creating a UI5 project within a CAP (Cloud Application Programming Model) project:
Project Location
ALWAYS create UI5 projects within the app/ directory of the CAP project root.
cap-project/
├── app/ # ← UI5 apps go here
│ └── my-ui5-app/
├── srv/ # CAP services
├── db/ # Database models
└── package.json
Service Information
Get service information:
Service Integration
When creating the UI5 project, ALWAYS provide:
- Absolute OData V4 service URL
- Target entity set
Plugin Installation
ALWAYS run in CAP project root:
npm i -D cds-plugin-ui5
This plugin automatically handles serving the UI5 applications.
Running the Server
cd app/my-ui5-app
ui5 serve
npm start
cds watch
cds run
Why: Single command serves both backend services and all UI5 applications from the same origin (http://localhost:4004).
Data Connection
NEVER configure ui5-middleware-simpleproxy in ui5.yaml:
server:
customMiddleware:
- name: ui5-middleware-simpleproxy
Why: cds watch ensures UI and service are served from the same origin, making a proxy unnecessary.
Accessing the App
Check the CAP launch page (typically http://localhost:4004) for:
- List of available services
- Links to UI5 applications
9. Form Creation Rules
Never Use SimpleForm (Unless Explicitly Requested)
<form:SimpleForm>
<Label text="Name"/>
<Input value="{name}"/>
</form:SimpleForm>
<form:Form editable="true">
<form:layout>
<form:ColumnLayout
columnsM="2"
columnsL="3"
columnsXL="4"/>
</form:layout>
<form:formContainers>
<form:FormContainer title="Personal Data">
<form:formElements>
<form:FormElement label="Name">
<form:fields>
<Input value="{name}"/>
</form:fields>
</form:FormElement>
</form:formElements>
</form:FormContainer>
</form:formContainers>
</form:Form>
Default Column Configuration
ALWAYS use these defaults unless requested differently:
- M-size: 2 columns
- L-size: 3 columns
- XL-size: 4 columns
Documentation References
For additional information, consult these UI5 documentation pages:
- "Require Modules in XML View and Fragment"
- "Declarative API for Initial Components"
- "Content Security Policy"
- Official UI5 API Reference (use
get_api_reference tool)