Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:55:36 +08:00
commit cc90ad1792
20 changed files with 8932 additions and 0 deletions

723
references/accessibility.md Normal file
View File

@@ -0,0 +1,723 @@
# SAPUI5 Accessibility Guide
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps)
**Last Updated**: 2025-11-21
---
## Overview
Accessibility in SAPUI5 ensures applications are usable by everyone, including people with disabilities. SAPUI5 controls include built-in accessibility features, but developers must implement them correctly.
**Standards**: SAPUI5 follows WCAG 2.1 (Web Content Accessibility Guidelines) Level AA.
**Key Principles**:
1. **Perceivable**: Information must be presentable to users in ways they can perceive
2. **Operable**: User interface components must be operable
3. **Understandable**: Information and operation must be understandable
4. **Robust**: Content must be robust enough to work with assistive technologies
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: accessibility)
---
## Screen Reader Support
### Overview
Screen readers announce UI content to visually impaired users. SAPUI5 controls provide built-in screen reader support through ARIA attributes.
**Supported Screen Readers**:
- JAWS (Windows)
- NVDA (Windows)
- VoiceOver (macOS, iOS)
- TalkBack (Android)
### Implementation
**Automatic Support**:
Most SAP UI5 controls include screen reader support automatically:
```xml
<Button
text="Save"
icon="sap-icon://save"
press=".onSave"/>
<!-- Automatically announces: "Save button" -->
```
**Custom Labels**:
```xml
<Input
value="{/email}"
ariaLabelledBy="emailLabel"/>
<Label
id="emailLabel"
text="Email Address"
labelFor="emailInput"/>
```
**ARIA Descriptions**:
```xml
<Button
text="Delete"
ariaDescribedBy="deleteHint"/>
<InvisibleText
id="deleteHint"
text="This action cannot be undone"/>
```
### Invisible Text
Provide information only for screen readers:
```xml
<InvisibleText
id="statusHint"
text="Status: {status}. Last updated: {lastUpdate}"/>
<ObjectStatus
text="{status}"
state="{statusState}"
ariaDescribedBy="statusHint"/>
```
**Use Cases**:
- Additional context not visible on screen
- Status changes announcements
- Navigation instructions
- Form validation messages
### Invisible Messaging
Announce dynamic content changes to screen readers:
```javascript
sap.ui.require([
"sap/ui/core/InvisibleMessage",
"sap/ui/core/library"
], function(InvisibleMessage, coreLibrary) {
var InvisibleMessageMode = coreLibrary.InvisibleMessageMode;
// Get instance
var oInvisibleMessage = InvisibleMessage.getInstance();
// Announce politely (after current announcement)
oInvisibleMessage.announce("Data loaded successfully", InvisibleMessageMode.Polite);
// Announce assertively (interrupts current announcement)
oInvisibleMessage.announce("Error: Form submission failed", InvisibleMessageMode.Assertive);
});
```
**Modes**:
- `Polite`: Wait for current announcement to finish
- `Assertive`: Interrupt current announcement immediately
**Use Cases**:
- Loading states
- Error messages
- Success confirmations
- Dynamic content updates
---
## Keyboard Navigation
### Overview
All functionality must be accessible via keyboard without requiring a mouse.
**Standard Keys**:
- **Tab**: Move focus forward
- **Shift+Tab**: Move focus backward
- **Enter/Space**: Activate buttons, links
- **Arrow Keys**: Navigate within components
- **Esc**: Close dialogs, cancel actions
- **Home/End**: Jump to first/last item
### Focus Management
**Visible Focus Indicator**:
All focusable elements must have a visible focus indicator (automatically provided by SAPUI5 controls).
**Focus Order**:
```xml
<!-- Logical focus order -->
<VBox>
<Input id="firstName" value="{/firstName}"/>
<Input id="lastName" value="{/lastName}"/>
<Input id="email" value="{/email}"/>
<Button text="Submit" press=".onSubmit"/>
</VBox>
```
**Programmatic Focus**:
```javascript
// Set focus to control
this.byId("emailInput").focus();
// Set focus after dialog opens
oDialog.attachAfterOpen(function() {
oDialog.getInitialFocus().focus();
});
```
### Keyboard Handling
**Handling Keyboard Events**:
```javascript
onKeyDown: function(oEvent) {
// Check for specific key
if (oEvent.keyCode === jQuery.sap.KeyCodes.ENTER) {
this.onSave();
oEvent.preventDefault();
}
// Check for Escape
if (oEvent.keyCode === jQuery.sap.KeyCodes.ESCAPE) {
this.onCancel();
}
}
```
**Item Navigation**:
For custom list-like controls:
```javascript
sap.ui.require([
"sap/ui/core/delegate/ItemNavigation"
], function(ItemNavigation) {
onAfterRendering: function() {
// Create item navigation
this._oItemNavigation = new ItemNavigation();
// Set root element and items
this._oItemNavigation.setRootDomRef(this.getDomRef());
this._oItemNavigation.setItemDomRefs(this.$().find(".item").toArray());
// Configure navigation
this._oItemNavigation.setCycling(false);
this._oItemNavigation.setPageSize(10);
// Attach to element
this.addDelegate(this._oItemNavigation);
},
onExit: function() {
if (this._oItemNavigation) {
this._oItemNavigation.destroy();
}
}
});
```
### Fast Navigation
F6 key for quick navigation between major screen regions:
```javascript
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/ui/core/CustomData"
], function(Controller, CustomData) {
"use strict";
return Controller.extend("my.app.controller.Main", {
onInit: function() {
// Mark sections for F6 navigation
this.byId("headerSection").addCustomData(
new CustomData({
key: "sap-ui-fastnavgroup",
value: "true"
})
);
this.byId("contentSection").addCustomData(
new CustomData({
key: "sap-ui-fastnavgroup",
value: "true"
})
);
}
});
});
```
**Use Cases**:
- Skip from header to content
- Jump to footer
- Navigate between major sections
---
## ARIA Implementation
### ARIA Attributes
**role**: Defines element type:
```xml
<div role="navigation">...</div>
<div role="main">...</div>
<div role="complementary">...</div>
```
**aria-label**: Provides accessible name:
```xml
<Button
icon="sap-icon://delete"
aria-label="Delete item"/>
```
**aria-labelledby**: References label element:
```xml
<Label id="nameLabel" text="Full Name"/>
<Input ariaLabelledBy="nameLabel"/>
```
**aria-describedby**: References description:
```xml
<Input
value="{/password}"
ariaDescribedBy="passwordHint"/>
<Text id="passwordHint" text="Must be at least 8 characters"/>
```
**aria-live**: Announces dynamic updates:
```xml
<Text
text="{statusMessage}"
aria-live="polite"/>
```
**aria-expanded**: Indicates expanded/collapsed state:
```xml
<Button
text="Show Details"
aria-expanded="{detailsVisible}"
press=".onToggleDetails"/>
```
### Landmark Regions
Define major page sections using landmarks:
```javascript
sap.ui.require([
"sap/ui/core/AccessibleLandmarkRole"
], function(AccessibleLandmarkRole) {
// In XML view
<Page
landmarkInfo="{
rootRole: 'Region',
rootLabel: 'Product Details',
contentRole: 'Main',
contentLabel: 'Product Information',
headerRole: 'Banner',
headerLabel: 'Page Header',
footerRole: 'Region',
footerLabel: 'Page Actions'
}">
</Page>
});
```
**Standard Roles**:
- `banner`: Site header
- `navigation`: Navigation section
- `main`: Main content
- `complementary`: Supporting content
- `contentinfo`: Site footer
- `region`: Generic landmark
- `search`: Search functionality
---
## Labeling & Tooltips
### Labels
**Always Provide Labels**:
```xml
<!-- Good -->
<Label text="First Name" labelFor="firstName"/>
<Input id="firstName" value="{/firstName}"/>
<!-- Bad (no visible label) -->
<Input placeholder="First Name"/> <!-- Placeholders are not labels -->
```
**Required Fields**:
```xml
<Label
text="Email"
required="true"
labelFor="email"/>
<Input
id="email"
value="{/email}"
required="true"/>
```
### Tooltips
Provide additional information:
```xml
<Button
icon="sap-icon://hint"
tooltip="Click for more information"
press=".onShowHelp"/>
```
**Rich Tooltips**:
```xml
<Button icon="sap-icon://action-settings">
<customData>
<core:CustomData
key="tooltip"
value="Settings (Ctrl+,)"/>
</customData>
</Button>
```
---
## Form Accessibility
### Field Labels
```xml
<form:SimpleForm>
<Label text="First Name" required="true"/>
<Input value="{/firstName}" required="true"/>
<Label text="Last Name" required="true"/>
<Input value="{/lastName}" required="true"/>
<Label text="Email"/>
<Input value="{/email}" type="Email"/>
<Label text="Phone"/>
<Input value="{/phone}" type="Tel"/>
</form:SimpleForm>
```
### Error Messages
```xml
<Input
value="{/email}"
valueState="{= ${/emailValid} ? 'None' : 'Error'}"
valueStateText="Please enter a valid email address"/>
```
### Field Groups
```xml
<VBox>
<Title text="Personal Information"/>
<Input value="{/firstName}" ariaLabelledBy="personalInfoTitle"/>
<Input value="{/lastName}" ariaLabelledBy="personalInfoTitle"/>
</VBox>
```
---
## Table Accessibility
### Column Headers
```xml
<Table items="{/products}">
<columns>
<Column>
<Text text="Product Name"/>
</Column>
<Column>
<Text text="Price"/>
</Column>
<Column>
<Text text="Status"/>
</Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{name}"/>
<Text text="{price}"/>
<ObjectStatus text="{status}" state="{statusState}"/>
</cells>
</ColumnListItem>
</items>
</Table>
```
### Row Actions
```xml
<ColumnListItem type="Active" press=".onRowPress">
<cells>
<Text text="{name}"/>
</cells>
<customData>
<core:CustomData
key="aria-label"
value="View details for {name}"/>
</customData>
</ColumnListItem>
```
---
## High Contrast Themes
### Overview
High contrast themes help users with visual impairments.
**Available Themes**:
- `sap_fiori_3_hcb`: High Contrast Black
- `sap_fiori_3_hcw`: High Contrast White
- `sap_horizon_hcb`: Horizon High Contrast Black
- `sap_horizon_hcw`: Horizon High Contrast White
### Testing
Test your app with high contrast themes:
```javascript
// Switch theme programmatically
sap.ui.getCore().applyTheme("sap_horizon_hcb");
```
**In URL**:
```
[http://myapp.com/index.html?sap-ui-theme=sap_horizon_hcb](http://myapp.com/index.html?sap-ui-theme=sap_horizon_hcb)
```
### CSS Considerations
Use theme parameters, not hard-coded colors:
```css
/* Good */
.myClass {
color: var(--sapUiContentForegroundColor);
background-color: var(--sapUiBaseBG);
}
/* Bad */
.myClass {
color: #333333;
background-color: #ffffff;
}
```
---
## Right-to-Left (RTL) Support
### Overview
Support languages written right-to-left (Arabic, Hebrew, etc.).
### Configuration
**Enable RTL**:
```html
<script
src="resources/sap-ui-core.js"
data-sap-ui-rtl="true">
</script>
```
**Or programmatically**:
```javascript
sap.ui.getCore().getConfiguration().setRTL(true);
```
### CSS for RTL
Use logical properties:
```css
/* Good (automatically flipped in RTL) */
.myClass {
padding-inline-start: 1rem;
margin-inline-end: 0.5rem;
border-inline-start: 1px solid;
}
/* Bad (not flipped) */
.myClass {
padding-left: 1rem;
margin-right: 0.5rem;
border-left: 1px solid;
}
```
### Text Direction
```xml
<Text text="{description}" textDirection="Inherit"/>
<!-- Force LTR for codes/IDs -->
<Text text="{productId}" textDirection="LTR"/>
```
---
## Testing Accessibility
### Manual Testing
1. **Keyboard Navigation**:
- Tab through all interactive elements
- Verify focus visibility
- Test Enter/Space activation
- Test Esc key behavior
2. **Screen Reader**:
- Test with JAWS, NVDA, or VoiceOver
- Verify all content is announced
- Check announcements are meaningful
- Test dynamic content updates
3. **High Contrast**:
- Switch to high contrast theme
- Verify all content is visible
- Check color contrast ratios
4. **Zoom**:
- Zoom to 200%
- Verify layout doesn't break
- Check text remains readable
### Automated Testing
**Use UI5 Support Assistant**:
```javascript
// Enable support assistant
sap.ui.require(["sap/ui/support/RuleAnalyzer"], function(RuleAnalyzer) {
RuleAnalyzer.analyze();
});
```
Or via keyboard: `Ctrl+Alt+Shift+S`
**Checks**:
- Missing labels
- Invalid ARIA attributes
- Keyboard navigation issues
- Color contrast problems
---
## Accessibility Checklist
### General
- [ ] All functionality keyboard accessible
- [ ] Focus indicators visible
- [ ] Logical focus order
- [ ] No keyboard traps
- [ ] Esc key closes dialogs
### Labeling
- [ ] All form fields have labels
- [ ] Required fields marked
- [ ] Error messages clear and accessible
- [ ] Buttons have meaningful labels
- [ ] Images have alt text
### ARIA
- [ ] Proper ARIA roles used
- [ ] aria-label or aria-labelledby on inputs
- [ ] aria-describedby for additional info
- [ ] aria-live for dynamic updates
- [ ] Landmark regions defined
### Screen Readers
- [ ] Test with screen reader
- [ ] All content announced
- [ ] Announcements meaningful
- [ ] InvisibleText used where needed
- [ ] Dynamic updates announced
### Visual
- [ ] Color contrast sufficient (4.5:1 for text)
- [ ] Works with high contrast themes
- [ ] Text remains readable at 200% zoom
- [ ] No information by color alone
### Tables
- [ ] Column headers defined
- [ ] Row headers where appropriate
- [ ] Summary/caption provided
- [ ] Complex tables avoided
---
## Common Issues
### Issue: Input without label
**Bad**:
```xml
<Input placeholder="Search..."/>
```
**Good**:
```xml
<Label text="Search" labelFor="searchInput"/>
<Input id="searchInput" placeholder="Enter search term..."/>
```
### Issue: Button with only icon
**Bad**:
```xml
<Button icon="sap-icon://delete" press=".onDelete"/>
```
**Good**:
```xml
<Button
icon="sap-icon://delete"
tooltip="Delete item"
ariaLabel="Delete item"
press=".onDelete"/>
```
### Issue: Dynamic content not announced
**Bad**:
```javascript
this.byId("statusText").setText("Loading complete");
```
**Good**:
```javascript
this.byId("statusText").setText("Loading complete");
// Announce to screen reader
var oInvisibleMessage = sap.ui.core.InvisibleMessage.getInstance();
oInvisibleMessage.announce("Loading complete", "Polite");
```
---
## Official Documentation
- **Accessibility**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: accessibility)
- **Screen Reader Support**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: screen-reader)
- **Keyboard Handling**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: keyboard)
- **ARIA**: [https://www.w3.org/WAI/ARIA/](https://www.w3.org/WAI/ARIA/)
- **WCAG 2.1**: [https://www.w3.org/WAI/WCAG21/quickref/](https://www.w3.org/WAI/WCAG21/quickref/)
---
**Note**: This document covers accessibility implementation in SAPUI5. Accessibility is not optional - it's a requirement for enterprise applications. Always test with keyboard and screen readers.

View File

@@ -0,0 +1,545 @@
# SAPUI5 Core Architecture & Concepts
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials)
**Last Updated**: 2025-11-21
---
## Framework Architecture
### Component-Based Architecture
SAPUI5 applications are built around **Components** - self-contained, reusable units with:
- **manifest.json**: Application descriptor with metadata and configuration
- **Component.js**: Component controller with initialization logic
- **View/Controller pairs**: UI definition and business logic
- **i18n**: Internationalization resources
- **model**: Optional local data models
**Key Benefits**:
- Reusability across applications
- Clear separation of concerns
- Standardized configuration via manifest
- Dependency management
- Lifecycle management
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: component)
---
### Model-View-Controller (MVC) Pattern
SAPUI5 implements MVC for clean separation:
**Model** (Data Layer):
- Provides data to UI
- Notifies views of changes
- Types: JSON, OData v2/v4, XML, Resource
- Supports binding for automatic UI updates
**View** (Presentation Layer):
- Defines UI structure
- Available types: XML (recommended), JSON, JavaScript, HTML
- Contains controls and layout
- No business logic
**Controller** (Logic Layer):
- Event handlers for user interactions
- Business logic
- Data formatting
- Navigation
- Lifecycle hooks: onInit, onBeforeRendering, onAfterRendering, onExit
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: mvc, controller, view)
---
### Module System
SAPUI5 uses AMD (Asynchronous Module Definition):
**Module Definition**:
```javascript
sap.ui.define([
"sap/ui/core/mvc/Controller",
"sap/m/MessageToast"
], function(Controller, MessageToast) {
"use strict";
return Controller.extend("my.namespace.controller.Main", {
onPress: function() {
MessageToast.show("Button pressed");
}
});
});
```
**Module Loading**:
```javascript
sap.ui.require([
"sap/m/MessageBox"
], function(MessageBox) {
MessageBox.success("Loaded asynchronously");
});
```
**Key Points**:
- **sap.ui.define**: For defining modules (always use this)
- **sap.ui.require**: For loading modules dynamically
- Async loading prevents blocking
- Dependencies declared explicitly
- Supports lazy loading
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: module, define, require)
---
### Bootstrapping
Initialize SAPUI5 in index.html:
**Basic Bootstrap**:
```html
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-theme="sap_horizon"
data-sap-ui-libs="sap.m"
data-sap-ui-resourceroots='{
"my.namespace": "./"
}'
data-sap-ui-async="true"
data-sap-ui-onInit="module:my/namespace/index"
data-sap-ui-compatVersion="edge">
</script>
```
**Key Configuration Options**:
- `data-sap-ui-theme`: UI theme (sap_horizon, sap_fiori_3, etc.)
- `data-sap-ui-libs`: Preloaded libraries
- `data-sap-ui-resourceroots`: Namespace-to-path mapping
- `data-sap-ui-async="true"`: Async loading (always use)
- `data-sap-ui-compatVersion="edge"`: Latest features
- `data-sap-ui-onInit`: Module to run after initialization
**CDN Options**:
- SAP CDN: `[https://sapui5.hana.ondemand.com/resources/sap-ui-core.js`](https://sapui5.hana.ondemand.com/resources/sap-ui-core.js`)
- OpenUI5 CDN: `[https://openui5.hana.ondemand.com/resources/sap-ui-core.js`](https://openui5.hana.ondemand.com/resources/sap-ui-core.js`)
- Specific version: `[https://sapui5.hana.ondemand.com/1.120.0/resources/sap-ui-core.js`](https://sapui5.hana.ondemand.com/1.120.0/resources/sap-ui-core.js`)
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: bootstrap, initialization)
---
### Libraries
SAPUI5 provides multiple control libraries:
**Main Libraries**:
- **sap.m**: Mobile/responsive controls (Button, Table, List, etc.)
- **sap.ui.core**: Core framework (mvc, routing, etc.)
- **sap.ui.table**: High-performance tables
- **sap.f**: SAP Fiori controls (FlexibleColumnLayout, etc.)
- **sap.uxap**: UX Add-on (ObjectPageLayout, etc.)
- **sap.ui.layout**: Layout controls (Grid, Splitter, etc.)
- **sap.tnt**: Tool Navigation Template (SideNavigation, etc.)
- **sap.suite.ui.commons**: Suite controls (charts, micro-charts)
**Library Loading**:
```javascript
// In manifest.json
{
"sap.ui5": {
"dependencies": {
"libs": {
"sap.m": {},
"sap.ui.table": {},
"sap.f": {}
}
}
}
}
```
**Lazy Loading**:
```javascript
sap.ui.getCore().loadLibrary("sap.ui.table", { async: true })
.then(function() {
// Library loaded
});
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First](https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First) (search: library, supported-library-combinations)
---
### Namespacing
Proper namespacing prevents conflicts:
**Structure**:
```
com.mycompany.myapp/
├── Component.js
├── manifest.json
├── controller/
│ └── Main.controller.js
├── view/
│ └── Main.view.xml
├── model/
│ └── formatter.js
└── i18n/
└── i18n.properties
```
**Naming Conventions**:
- Reverse domain: `com.mycompany.myapp`
- PascalCase for classes: `Main.controller.js`
- camelCase for files: `formatter.js`
- Folder names: lowercase (controller, view, model)
**Resource Root Registration**:
```javascript
// In index.html
data-sap-ui-resourceroots='{
"com.mycompany.myapp": "./"
}'
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: folder-structure, namespace)
---
### Control Tree & Rendering
SAPUI5 builds a tree of controls:
**Control Hierarchy**:
- **Root Element**: `<body>` or specific `<div>`
- **Component Container**: Hosts component
- **View**: Contains controls
- **Controls**: UI elements (Button, Input, Table, etc.)
- **Aggregations**: Child controls (items, content, etc.)
**Rendering Process**:
1. **Initial Rendering**: Creates HTML from control tree
2. **Re-rendering**: Updates DOM when model/property changes
3. **Invalidation**: Marks controls for re-rendering
4. **Batching**: Groups re-renders for performance
**Lifecycle Hooks**:
- `onBeforeRendering()`: Before DOM update
- `onAfterRendering()`: After DOM update (DOM manipulation here)
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: rendering, control-tree)
---
### Fragments
Reusable UI snippets without controller:
**XML Fragment** (recommended):
```xml
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<Dialog
title="{i18n>dialogTitle}"
type="Message">
<Text text="{i18n>dialogText}"/>
<buttons>
<Button text="{i18n>close}" press=".onCloseDialog"/>
</buttons>
</Dialog>
</core:FragmentDefinition>
```
**Loading Fragments**:
```javascript
// In controller
onOpenDialog: function() {
if (!this.pDialog) {
this.pDialog = this.loadFragment({
name: "my.namespace.view.fragments.MyDialog"
});
}
this.pDialog.then(function(oDialog) {
oDialog.open();
});
}
```
**Benefits**:
- Reuse across views
- Smaller view files
- Modular UI definition
- No separate controller
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: fragment)
---
### Application Descriptor (manifest.json)
Central configuration file:
**Structure**:
```json
{
"sap.app": {
"id": "com.mycompany.myapp",
"type": "application",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"mainService": {
"uri": "/sap/opu/odata/sap/SERVICE_SRV/",
"type": "OData",
"settings": {
"odataVersion": "2.0"
}
}
}
},
"sap.ui": {
"technology": "UI5",
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.ui5": {
"rootView": {
"viewName": "com.mycompany.myapp.view.App",
"type": "XML",
"async": true,
"id": "app"
},
"dependencies": {
"minUI5Version": "1.120.0",
"libs": {
"sap.m": {},
"sap.ui.core": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "com.mycompany.myapp.i18n.i18n"
}
},
"": {
"dataSource": "mainService",
"settings": {
"defaultBindingMode": "TwoWay"
}
}
},
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"type": "View",
"viewType": "XML",
"path": "com.mycompany.myapp.view",
"controlId": "app",
"controlAggregation": "pages",
"async": true
},
"routes": [],
"targets": {}
}
}
}
```
**Key Sections**:
- **sap.app**: General app info and data sources
- **sap.ui**: UI technology and device types
- **sap.ui5**: UI5-specific config (models, routing, dependencies)
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: manifest, descriptor)
---
## Core Concepts
### Properties, Events, Aggregations
**Properties**:
- Simple values (text, enabled, visible)
- Accessed via getters/setters
- Support data binding
- Example: `oButton.setText("Click me")`
**Events**:
- User interactions or state changes
- Attach handlers with `.attachEvent()` or in XML
- Event object contains source and parameters
- Example: `press`, `change`, `selectionChange`
**Aggregations**:
- Child controls (items, content, buttons)
- One-to-many relationships
- Managed by parent control
- Example: Table has `items`, Page has `content`
**Associations**:
- References to other controls
- Not parent-child relationship
- Example: Label's `labelFor` association
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: property, event, aggregation)
---
### Device Adaptation
Responsive design features:
**Content Density**:
- **Cozy**: Touch-friendly (larger targets) - default on phones
- **Compact**: Mouse-friendly (smaller) - default on desktops
- Set via CSS class: `sapUiSizeCozy` or `sapUiSizeCompact`
**Device Detection**:
```javascript
sap.ui.Device.system.phone
sap.ui.Device.system.tablet
sap.ui.Device.system.desktop
```
**Responsive Controls**:
- Use `sap.m` controls (designed for responsive)
- FlexBox for flexible layouts
- Grid for responsive grids
- Avoid fixed pixel sizes
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: device, responsive, content-density)
---
### Theming
Visual design system:
**Available Themes**:
- **sap_horizon**: Latest SAP theme (recommended)
- **sap_fiori_3**: SAP Fiori 3.0 theme
- **sap_fiori_3_dark**: Dark variant
- **sap_fiori_3_hcb**: High contrast black
- **sap_fiori_3_hcw**: High contrast white
**Setting Theme**:
```html
<!-- In index.html -->
data-sap-ui-theme="sap_horizon"
```
```javascript
// Dynamically
sap.ui.getCore().applyTheme("sap_horizon");
```
**Theme Parameters**:
- Use in custom CSS for consistency
- Example: `@sapUiBaseColor`, `@sapUiBaseBG`
- Access via: `Parameters.get("sapUiBaseColor")`
**Custom Themes**:
- Use UI Theme Designer
- Based on SAP theme
- Only override needed parameters
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First](https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First) (search: theme, supported-combinations)
---
## Performance Optimization
**Best Practices**:
1. **Async Loading**:
- Always use `data-sap-ui-async="true"`
- Use `sap.ui.define` for modules
- Lazy load libraries when needed
2. **Component Preload**:
- Build creates Component-preload.js
- Bundles all component resources
- Reduces HTTP requests
3. **Data Binding**:
- Use one-way binding when possible
- Avoid complex formatters in loops
- Use `bindingMode: "OneTime"` for static data
4. **List/Table Optimization**:
- Use growing lists for large datasets
- Enable table virtualization
- Use OData paging ($skip, $top)
5. **Model Management**:
- Use batch requests for OData
- Set size limits appropriately
- Destroy models when not needed
6. **Rendering**:
- Avoid frequent re-renders
- Use `busy` state during loading
- Minimize DOM manipulations
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: performance)
---
## Security
**Key Security Features**:
1. **XSS Prevention**:
- Automatic output encoding
- Use data binding (never innerHTML)
- Sanitize user input
2. **Content Security Policy (CSP)**:
- SAPUI5 supports CSP
- Avoid inline scripts
- Use nonce or hash for inline styles
3. **Clickjacking Prevention**:
- Frame-options header
- CSP frame-ancestors directive
4. **Input Validation**:
- Use data types
- Validate on client and server
- Use constraints (maxLength, pattern)
5. **Secure Communication**:
- Always use HTTPS in production
- Enable CORS properly
- Use CSRF tokens
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: security, secure-programming)
---
## Links to Official Documentation
- **Core Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials)
- **App Development**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps)
- **Getting Started**: [https://github.com/SAP-docs/sapui5/tree/main/docs/03_Get-Started](https://github.com/SAP-docs/sapui5/tree/main/docs/03_Get-Started)
- **Read Me First**: [https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First](https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First)
- **API Reference**: [https://sapui5.hana.ondemand.com/#/api](https://sapui5.hana.ondemand.com/#/api)
- **Demo Kit**: [https://sapui5.hana.ondemand.com/](https://sapui5.hana.ondemand.com/)
---
**Note**: This document provides core architecture concepts for SAPUI5 development. For specific implementation details, refer to the official documentation links provided throughout this document.

View File

@@ -0,0 +1,879 @@
# SAPUI5 Data Binding & Models
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials)
**Last Updated**: 2025-11-21
---
## Data Binding Overview
Data binding connects UI controls to data sources, automatically synchronizing changes bidirectionally.
**Key Benefits**:
- Automatic UI updates when data changes
- Reduced boilerplate code
- Clean separation of data and presentation
- Type conversion and formatting
- Validation support
**Binding Types**:
1. **Property Binding**: Single value (text, enabled, visible)
2. **Aggregation Binding**: Collections (table items, list items)
3. **Element Binding**: Object context
4. **Expression Binding**: Inline calculations
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: binding, data-binding)
---
## Binding Modes
**One-Way Binding**:
- Data flows model → view only
- UI updates when model changes
- User input doesn't update model
- Use for read-only data
```javascript
// In manifest.json
"models": {
"": {
"dataSource": "mainService",
"settings": {
"defaultBindingMode": "OneWay"
}
}
}
```
**Two-Way Binding**:
- Data flows model ↔ view bidirectionally
- Model updates when user changes input
- View updates when model changes
- Use for editable forms
```javascript
"defaultBindingMode": "TwoWay"
```
**One-Time Binding**:
- Data loaded once at initialization
- No updates after initial load
- Best performance for static data
```xml
<Text text="{path: '/title', mode: 'OneTime'}"/>
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: binding-mode)
---
## Model Types
### JSON Model
Client-side model for JavaScript objects. Best for small datasets and local data.
**Creation**:
```javascript
// In controller
var oModel = new JSONModel({
products: [
{ id: 1, name: "Product 1", price: 100 },
{ id: 2, name: "Product 2", price: 200 }
],
selectedProduct: null
});
this.getView().setModel(oModel);
```
**From File**:
```javascript
var oModel = new JSONModel();
oModel.loadData("model/data.json");
this.getView().setModel(oModel);
```
**Usage**:
```xml
<List items="{/products}">
<StandardListItem
title="{name}"
description="Price: {price}"/>
</List>
```
**Key Methods**:
- `setData(oData)`: Set complete data
- `setProperty(sPath, oValue)`: Set single property
- `getProperty(sPath)`: Get property value
- `loadData(sURL)`: Load from URL
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: json-model)
---
### OData V2 Model
Server-side model for OData v2 services. Automatic CRUD operations.
**Creation**:
```javascript
// In manifest.json
{
"sap.app": {
"dataSources": {
"mainService": {
"uri": "/sap/opu/odata/sap/SERVICE_SRV/",
"type": "OData",
"settings": {
"odataVersion": "2.0",
"localUri": "localService/metadata.xml"
}
}
}
},
"sap.ui5": {
"models": {
"": {
"dataSource": "mainService",
"settings": {
"defaultBindingMode": "TwoWay",
"defaultCountMode": "Inline",
"useBatch": true
}
}
}
}
}
```
**Reading Data**:
```javascript
// Simple read
this.getView().getModel().read("/Products", {
success: function(oData) {
console.log(oData);
},
error: function(oError) {
MessageBox.error("Failed to load data");
}
});
// With filters and sorters
this.getView().getModel().read("/Products", {
filters: [new Filter("Price", FilterOperator.GT, 100)],
sorters: [new Sorter("Name", false)],
urlParameters: {
"$expand": "Category"
},
success: function(oData) {
console.log(oData);
}
});
```
**Creating Entries**:
```javascript
var oModel = this.getView().getModel();
oModel.create("/Products", {
Name: "New Product",
Price: 150,
CategoryID: 1
}, {
success: function() {
MessageToast.show("Product created");
},
error: function(oError) {
MessageBox.error("Failed to create product");
}
});
```
**Updating Entries**:
```javascript
var oModel = this.getView().getModel();
oModel.update("/Products(1)", {
Price: 200
}, {
success: function() {
MessageToast.show("Product updated");
}
});
```
**Deleting Entries**:
```javascript
oModel.remove("/Products(1)", {
success: function() {
MessageToast.show("Product deleted");
}
});
```
**Batch Requests**:
```javascript
oModel.setUseBatch(true);
oModel.setDeferredGroups(["myGroup"]);
// Add to batch
oModel.create("/Products", oData, { groupId: "myGroup" });
oModel.create("/Products", oData2, { groupId: "myGroup" });
// Submit batch
oModel.submitChanges({
groupId: "myGroup",
success: function() {
MessageToast.show("Batch successful");
}
});
```
**Key Settings**:
- `useBatch`: Enable batch requests
- `defaultCountMode`: How to get counts (Inline, Request, None)
- `refreshAfterChange`: Auto-refresh after updates
- `defaultBindingMode`: One-way or two-way
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: odata-v2-model)
---
### OData V4 Model
Modern OData v4 model with improved performance and features.
**Creation**:
```javascript
// In manifest.json
{
"sap.app": {
"dataSources": {
"mainService": {
"uri": "/sap/opu/odata4/sap/service/srvd/sap/api/0001/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
}
},
"sap.ui5": {
"models": {
"": {
"dataSource": "mainService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true,
"earlyRequests": true
}
}
}
}
}
```
**Key Differences from V2**:
- Server-side operations (filter, sort, page)
- Automatic $expand and $select
- Better performance
- Stricter adherence to OData standard
- No client-side models
**Reading with List Binding**:
```xml
<Table items="{
path: '/Products',
parameters: {
$expand: 'Category',
$select: 'ID,Name,Price',
$filter: 'Price gt 100',
$orderby: 'Name'
}
}">
```
**Creating Entries**:
```javascript
var oListBinding = this.byId("table").getBinding("items");
var oContext = oListBinding.create({
Name: "New Product",
Price: 150
});
// Save
oContext.created().then(function() {
MessageToast.show("Product created");
});
```
**Updating**:
```javascript
var oContext = this.byId("table").getSelectedItem().getBindingContext();
oContext.setProperty("Price", 200);
// Save changes
oContext.getModel().submitBatch("$auto").then(function() {
MessageToast.show("Updated");
});
```
**Deleting**:
```javascript
var oContext = this.byId("table").getSelectedItem().getBindingContext();
oContext.delete("$auto").then(function() {
MessageToast.show("Deleted");
});
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: odata-v4-model)
---
### Resource Model (i18n)
Model for internationalization texts.
**Setup**:
```javascript
// In manifest.json
{
"sap.ui5": {
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "com.mycompany.myapp.i18n.i18n",
"supportedLocales": ["en", "de", "fr"],
"fallbackLocale": "en"
}
}
}
}
}
```
**i18n.properties**:
```properties
appTitle=My Application
appDescription=A sample SAPUI5 application
# Buttons
btnSave=Save
btnCancel=Cancel
btnDelete=Delete
# Messages
msgSaveSuccess=Data saved successfully
msgDeleteConfirm=Do you want to delete this item?
# Placeholders with parameters
msgItemCount=You have {0} items selected
msgWelcome=Welcome, {0}!
```
**Usage in XML**:
```xml
<Page title="{i18n>appTitle}">
<Button text="{i18n>btnSave}" press=".onSave"/>
<Text text="{i18n>appDescription}"/>
</Page>
```
**Usage in Controller**:
```javascript
var oBundle = this.getView().getModel("i18n").getResourceBundle();
var sTitle = oBundle.getText("appTitle");
// With parameters
var sMessage = oBundle.getText("msgItemCount", [5]);
MessageBox.success(sMessage);
```
**Locale Files**:
- `i18n.properties`: Default (fallback)
- `i18n_de.properties`: German
- `i18n_en.properties`: English
- `i18n_fr.properties`: French
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: resource-model, i18n)
---
### XML Model
Client-side model for XML data structures.
**Creation**:
```javascript
var oModel = new XMLModel();
oModel.loadData("model/data.xml");
this.getView().setModel(oModel, "xml");
```
**Usage**:
```xml
<List items="{xml>/products/product}">
<StandardListItem
title="{xml>name}"
description="{xml>price}"/>
</List>
```
**Use Cases**:
- Legacy XML data sources
- Configuration files
- Small datasets
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: xml-model)
---
## Binding Syntax
### Property Binding
**Simple Binding**:
```xml
<Text text="{/companyName}"/>
<Input value="{/employeeName}"/>
```
**Named Models**:
```xml
<Text text="{invoice>/company/name}"/>
<Input value="{customer>/email}"/>
```
**Binding Options**:
```xml
<Text text="{
path: '/price',
type: 'sap.ui.model.type.Currency',
formatOptions: {
showMeasure: false
},
constraints: {
minimum: 0
}
}"/>
```
**Composite Binding**:
```xml
<Text text="{
parts: [
{path: '/firstName'},
{path: '/lastName'}
],
formatter: '.formatFullName'
}"/>
```
**Controller**:
```javascript
formatFullName: function(sFirstName, sLastName) {
return sFirstName + " " + sLastName;
}
```
---
### Aggregation Binding
**List Binding**:
```xml
<List items="{/products}">
<StandardListItem
title="{name}"
description="{description}"
info="{price} EUR"/>
</List>
```
**Table Binding**:
```xml
<Table items="{
path: '/products',
sorter: {
path: 'name'
},
filters: {
path: 'price',
operator: 'GT',
value1: 50
}
}">
<columns>
<Column><Text text="Name"/></Column>
<Column><Text text="Price"/></Column>
</columns>
<items>
<ColumnListItem>
<cells>
<Text text="{name}"/>
<Text text="{price}"/>
</cells>
</ColumnListItem>
</items>
</Table>
```
**With Parameters**:
```xml
<Table items="{
path: '/Products',
parameters: {
expand: 'Category',
select: 'ID,Name,Price'
}
}">
```
---
### Element Binding
Sets binding context for entire control:
```xml
<Panel binding="{/selectedProduct}">
<VBox>
<Text text="{name}"/>
<Text text="{description}"/>
<Text text="{price} EUR"/>
</VBox>
</Panel>
```
```javascript
// In controller
onProductSelect: function(oEvent) {
var oItem = oEvent.getParameter("listItem");
var sPath = oItem.getBindingContext().getPath();
this.byId("detailPanel").bindElement({
path: sPath,
parameters: {
expand: "Category"
}
});
}
```
---
### Expression Binding
Inline calculations without formatter:
```xml
<!-- Conditional text color -->
<Text
text="{price}"
color="{= ${price} > 100 ? 'red' : 'green'}"/>
<!-- Conditional visibility -->
<Button
visible="{= ${status} === 'approved'}"
text="Process"/>
<!-- Calculations -->
<Text text="{= ${quantity} * ${price}}"/>
<!-- String operations -->
<Text text="{= ${firstName} + ' ' + ${lastName}}"/>
<!-- Comparisons -->
<Button enabled="{= ${quantity} > 0 &amp;&amp; ${stock} >= ${quantity}}"/>
```
**Supported Operations**:
- Arithmetic: `+`, `-`, `*`, `/`, `%`
- Comparison: `===`, `!==`, `>`, `<`, `>=`, `<=`
- Logical: `&&`, `||`, `!`
- Ternary: `condition ? true : false`
- String concatenation: `+`
**Limitations**:
- Simple expressions only
- No function calls
- Use formatters for complex logic
---
## Formatters & Data Types
### Custom Formatters
**Definition**:
```javascript
// In controller or separate formatter.js
formatPrice: function(sPrice) {
if (!sPrice) return "";
return parseFloat(sPrice).toFixed(2) + " EUR";
},
formatStatus: function(sStatus) {
var mStatusText = {
"A": "Approved",
"R": "Rejected",
"P": "Pending"
};
return mStatusText[sStatus] || sStatus;
},
formatDate: function(oDate) {
if (!oDate) return "";
var oDateFormat = sap.ui.core.format.DateFormat.getDateInstance({
pattern: "dd.MM.yyyy"
});
return oDateFormat.format(oDate);
}
```
**Usage**:
```xml
<Text text="{path: 'price', formatter: '.formatPrice'}"/>
<Text text="{path: 'status', formatter: '.formatStatus'}"/>
<Text text="{path: 'createdAt', formatter: '.formatDate'}"/>
```
**Multiple Parameters**:
```xml
<Text text="{
parts: ['quantity', 'price'],
formatter: '.formatTotal'
}"/>
```
```javascript
formatTotal: function(iQuantity, fPrice) {
return (iQuantity * fPrice).toFixed(2) + " EUR";
}
```
---
### Built-in Data Types
**String Type**:
```xml
<Input value="{
path: '/name',
type: 'sap.ui.model.type.String',
constraints: {
maxLength: 50,
minLength: 2
}
}"/>
```
**Integer Type**:
```xml
<Input value="{
path: '/quantity',
type: 'sap.ui.model.type.Integer',
constraints: {
minimum: 1,
maximum: 999
}
}"/>
```
**Float Type**:
```xml
<Input value="{
path: '/price',
type: 'sap.ui.model.type.Float',
constraints: {
minimum: 0,
maximum: 99999.99
},
formatOptions: {
minFractionDigits: 2,
maxFractionDigits: 2
}
}"/>
```
**Date Type**:
```xml
<DatePicker value="{
path: '/orderDate',
type: 'sap.ui.model.type.Date',
formatOptions: {
pattern: 'dd.MM.yyyy'
}
}"/>
```
**DateTime Type**:
```xml
<DateTimePicker value="{
path: '/createdAt',
type: 'sap.ui.model.type.DateTime',
formatOptions: {
pattern: 'dd.MM.yyyy HH:mm:ss'
}
}"/>
```
**Currency Type**:
```xml
<Text text="{
parts: [
{path: 'price'},
{path: 'currency'}
],
type: 'sap.ui.model.type.Currency',
formatOptions: {
showMeasure: true
}
}"/>
```
**Boolean Type**:
```xml
<CheckBox selected="{
path: '/isActive',
type: 'sap.ui.model.type.Boolean'
}"/>
```
---
## Filters & Sorters
### Filters
**Simple Filter**:
```javascript
var oFilter = new Filter("price", FilterOperator.GT, 100);
var oBinding = this.byId("table").getBinding("items");
oBinding.filter([oFilter]);
```
**Multiple Filters (AND)**:
```javascript
var aFilters = [
new Filter("price", FilterOperator.GT, 100),
new Filter("category", FilterOperator.EQ, "Electronics")
];
oBinding.filter(aFilters); // AND condition
```
**Multiple Filters (OR)**:
```javascript
var aFilters = [
new Filter("status", FilterOperator.EQ, "Approved"),
new Filter("status", FilterOperator.EQ, "Pending")
];
var oCombinedFilter = new Filter({
filters: aFilters,
and: false // OR condition
});
oBinding.filter([oCombinedFilter]);
```
**Complex Filters**:
```javascript
var oPriceFilter = new Filter({
filters: [
new Filter("price", FilterOperator.GT, 100),
new Filter("price", FilterOperator.LT, 500)
],
and: true
});
var oStatusFilter = new Filter({
filters: [
new Filter("status", FilterOperator.EQ, "A"),
new Filter("status", FilterOperator.EQ, "P")
],
and: false
});
var oCombinedFilter = new Filter({
filters: [oPriceFilter, oStatusFilter],
and: true
});
oBinding.filter([oCombinedFilter]);
```
**Filter Operators**:
- `EQ`: Equals
- `NE`: Not equals
- `GT`: Greater than
- `GE`: Greater or equal
- `LT`: Less than
- `LE`: Less or equal
- `Contains`: Contains text
- `StartsWith`: Starts with text
- `EndsWith`: Ends with text
- `BT`: Between (requires value1 and value2)
**Custom Filter Function**:
```javascript
var oFilter = new Filter({
path: "price",
test: function(oValue) {
return oValue > 100 && oValue < 500;
}
});
```
---
### Sorters
**Simple Sort**:
```javascript
var oSorter = new Sorter("name", false); // false = ascending
var oBinding = this.byId("table").getBinding("items");
oBinding.sort(oSorter);
```
**Multiple Sorters**:
```javascript
var aSorters = [
new Sorter("category", false),
new Sorter("price", true) // true = descending
];
oBinding.sort(aSorters);
```
**Sort with Grouping**:
```javascript
var oSorter = new Sorter("category", false, true); // third param = group
oBinding.sort(oSorter);
```
**Custom Group Function**:
```javascript
var oSorter = new Sorter("price", false, function(oContext) {
var fPrice = oContext.getProperty("price");
if (fPrice < 100) return { key: "low", text: "Low Price" };
if (fPrice < 500) return { key: "medium", text: "Medium Price" };
return { key: "high", text: "High Price" };
});
```
---
## Links to Official Documentation
- **Data Binding**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: data-binding)
- **Models**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: model)
- **OData**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: odata)
- **Get Started Tutorials**: [https://github.com/SAP-docs/sapui5/tree/main/docs/03_Get-Started](https://github.com/SAP-docs/sapui5/tree/main/docs/03_Get-Started)
- **API Reference**: [https://sapui5.hana.ondemand.com/#/api](https://sapui5.hana.ondemand.com/#/api)
---
**Note**: This document covers data binding and model usage in SAPUI5. For specific implementation details and advanced scenarios, refer to the official documentation links provided.

View File

@@ -0,0 +1,813 @@
# SAP Fiori Elements Guide
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements)
**Last Updated**: 2025-11-21
---
## Overview
SAP Fiori Elements provides metadata-driven templates for creating enterprise applications without writing JavaScript UI code. Applications are configured through OData annotations and manifest.json settings.
**Key Benefits**:
- Rapid application development
- Consistent UX across apps
- Automatic updates with framework upgrades
- Reduced maintenance effort
- Built-in best practices
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements)
---
## Application Types
### List Report
Displays data in searchable, filterable tables or charts.
**Use Cases**:
- Product catalogs
- Sales orders
- Employee lists
- Any tabular data display
**Key Features**:
- Smart filter bar
- Multi-view (table/chart)
- Export to Excel/PDF
- Variant management
- Mass editing
**Annotations**:
```xml
<!-- In metadata annotations -->
<Annotations Target="Service.Products">
<!-- Selection fields (filter bar) -->
<Annotation Term="UI.SelectionFields">
<Collection>
<PropertyPath>Category</PropertyPath>
<PropertyPath>Price</PropertyPath>
<PropertyPath>Status</PropertyPath>
</Collection>
</Annotation>
<!-- Table columns -->
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="ProductID"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="Name"/>
<PropertyValue Property="Label" String="Product Name"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="Price"/>
<PropertyValue Property="Label" String="Price"/>
</Record>
<Record Type="UI.DataFieldForAnnotation">
<PropertyValue Property="Target" AnnotationPath="@UI.DataPoint#Rating"/>
<PropertyValue Property="Label" String="Rating"/>
</Record>
</Collection>
</Annotation>
</Annotations>
```
**manifest.json Configuration**:
```json
{
"sap.ui5": {
"routing": {
"targets": {
"ProductsList": {
"type": "Component",
"id": "ProductsList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"contextPath": "/Products",
"variantManagement": "Page",
"initialLoad": true,
"tableSettings": {
"type": "ResponsiveTable",
"selectAll": true
}
}
}
}
}
}
}
}
```
---
### Object Page
Displays detailed information about a single business object across multiple sections.
**Use Cases**:
- Product details
- Sales order details
- Employee profile
- Any detailed view with related data
**Key Features**:
- Header with key info
- Sections and subsections
- Facets (forms, tables, charts)
- Edit mode
- Actions (approve, reject, etc.)
- Related objects navigation
**Annotations**:
```xml
<Annotations Target="Service.Product">
<!-- Header info -->
<Annotation Term="UI.HeaderInfo">
<Record>
<PropertyValue Property="TypeName" String="Product"/>
<PropertyValue Property="TypeNamePlural" String="Products"/>
<PropertyValue Property="Title">
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="Name"/>
</Record>
</PropertyValue>
<PropertyValue Property="Description">
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="Description"/>
</Record>
</PropertyValue>
</Record>
</Annotation>
<!-- Header facets (quick view) -->
<Annotation Term="UI.HeaderFacets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Target" AnnotationPath="@UI.DataPoint#Price"/>
</Record>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Target" AnnotationPath="@UI.DataPoint#Stock"/>
</Record>
</Collection>
</Annotation>
<!-- Sections -->
<Annotation Term="UI.Facets">
<Collection>
<!-- General section -->
<Record Type="UI.CollectionFacet">
<PropertyValue Property="Label" String="General Information"/>
<PropertyValue Property="ID" String="GeneralInfo"/>
<PropertyValue Property="Facets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#General"/>
</Record>
</Collection>
</PropertyValue>
</Record>
<!-- Related items table -->
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="Sales Orders"/>
<PropertyValue Property="Target" AnnotationPath="SalesOrders/@UI.LineItem"/>
</Record>
</Collection>
</Annotation>
<!-- Field group -->
<Annotation Term="UI.FieldGroup" Qualifier="General">
<Record>
<PropertyValue Property="Data">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="ProductID"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="Category"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="Price"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
</Annotations>
```
**manifest.json Configuration**:
```json
{
"sap.ui5": {
"routing": {
"targets": {
"ProductObjectPage": {
"type": "Component",
"id": "ProductObjectPage",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"contextPath": "/Products",
"editableHeaderContent": true,
"showRelatedApps": true
}
}
}
}
}
}
}
```
---
### Analytical List Page
Combines visual filters, charts, and tables for analytical data exploration.
**Use Cases**:
- Sales analytics
- Financial reporting
- KPI dashboards
- Performance monitoring
**Key Features**:
- Visual filters (bar, line, donut charts)
- Interactive charts
- Smart filter bar
- Table view
- Drill-down capabilities
**Annotations**:
```xml
<Annotations Target="Service.SalesData">
<!-- Chart definition -->
<Annotation Term="UI.Chart">
<Record>
<PropertyValue Property="Title" String="Sales by Region"/>
<PropertyValue Property="ChartType" EnumMember="UI.ChartType/Column"/>
<PropertyValue Property="Dimensions">
<Collection>
<PropertyPath>Region</PropertyPath>
</Collection>
</PropertyValue>
<PropertyValue Property="Measures">
<Collection>
<PropertyPath>Sales</PropertyPath>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<!-- Presentation variant -->
<Annotation Term="UI.PresentationVariant">
<Record>
<PropertyValue Property="Visualizations">
<Collection>
<AnnotationPath>@UI.Chart</AnnotationPath>
<AnnotationPath>@UI.LineItem</AnnotationPath>
</Collection>
</PropertyValue>
<PropertyValue Property="SortOrder">
<Collection>
<Record Type="Common.SortOrderType">
<PropertyValue Property="Property" PropertyPath="Sales"/>
<PropertyValue Property="Descending" Bool="true"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
</Annotations>
```
---
### Overview Page
Card-based dashboard displaying key metrics and lists.
**Use Cases**:
- Executive dashboards
- Overview screens
- KPI monitoring
- Multi-source data aggregation
**Key Features**:
- Cards (list, analytical, table)
- Automatic refresh
- Filter bar
- Navigation to detail apps
- Responsive layout
**Card Configuration**:
```json
{
"sap.ovp": {
"cards": {
"salesCard": {
"model": "mainService",
"template": "sap.ovp.cards.charts.analytical",
"settings": {
"title": "Sales by Region",
"subTitle": "Current Year",
"entitySet": "SalesData",
"chartAnnotationPath": "com.sap.vocabularies.UI.v1.Chart",
"selectionAnnotationPath": "com.sap.vocabularies.UI.v1.SelectionVariant",
"presentationAnnotationPath": "com.sap.vocabularies.UI.v1.PresentationVariant"
}
},
"productsCard": {
"model": "mainService",
"template": "sap.ovp.cards.list",
"settings": {
"title": "Top Products",
"entitySet": "Products",
"listType": "extended",
"sortBy": "Sales",
"sortOrder": "desc"
}
}
}
}
}
```
---
### Worklist
Simplified list report for task-oriented applications.
**Use Cases**:
- Task lists
- Approval workflows
- Simple data management
- To-do lists
**Key Features**:
- Table with basic filtering
- Search
- Item count
- Quick navigation
- Simplified UI compared to List Report
---
## Common Annotations
### UI Annotations
**@UI.LineItem**: Table columns
**@UI.SelectionFields**: Filter bar fields
**@UI.HeaderInfo**: Object page header
**@UI.HeaderFacets**: Header quick view
**@UI.Facets**: Object page sections
**@UI.FieldGroup**: Grouped fields
**@UI.DataPoint**: KPI or micro-chart
**@UI.Chart**: Chart definition
**@UI.Identification**: Form fields
### Common Annotations
**@Common.Label**: Field label
**@Common.Text**: Display text for coded values
**@Common.ValueList**: Value help
**@Common.SemanticObject**: Navigation target
### Capabilities Annotations
**@Capabilities.FilterRestrictions**: Filter limitations
**@Capabilities.SortRestrictions**: Sort limitations
**@Capabilities.InsertRestrictions**: Create permissions
**@Capabilities.UpdateRestrictions**: Edit permissions
**@Capabilities.DeleteRestrictions**: Delete permissions
### Communication Annotations
**@Communication.Contact**: Contact information
**@Communication.Address**: Address fields
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: annotations)
---
## Actions
### Standard Actions
Automatically available for editable entities:
- Create
- Edit
- Delete
- Save
- Cancel
### Custom Actions
**OData Action Definition**:
```xml
<Action Name="ApproveOrder" IsBound="true">
<Parameter Name="_it" Type="Service.SalesOrder"/>
<ReturnType Type="Service.SalesOrder"/>
</Action>
```
**Annotation**:
```xml
<Annotations Target="Service.SalesOrder">
<Annotation Term="UI.LineItem">
<Collection>
<!-- Regular fields -->
<Record Type="UI.DataField">
<PropertyValue Property="Value" PropertyPath="OrderID"/>
</Record>
<!-- Action button -->
<Record Type="UI.DataFieldForAction">
<PropertyValue Property="Label" String="Approve"/>
<PropertyValue Property="Action" String="Service.ApproveOrder"/>
<PropertyValue Property="InvocationGrouping" EnumMember="UI.OperationGroupingType/Isolated"/>
</Record>
</Collection>
</Annotation>
</Annotations>
```
**Determining Actions**:
Actions shown in object page footer:
```xml
<Annotation Term="UI.Identification">
<Collection>
<Record Type="UI.DataFieldForAction">
<PropertyValue Property="Label" String="Approve"/>
<PropertyValue Property="Action" String="Service.ApproveOrder"/>
<PropertyValue Property="Determining" Bool="true"/>
</Record>
</Collection>
</Annotation>
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: actions)
---
## Draft Handling
Enable users to save incomplete work:
**OData Service**:
```xml
<EntityType Name="SalesOrder">
<Property Name="OrderID" Type="Edm.Int32"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean"/>
</EntityType>
```
**Annotations**:
```xml
<Annotations Target="Service.SalesOrder">
<Annotation Term="Common.DraftRoot">
<Record>
<PropertyValue Property="ActivationAction" String="Service.draftActivate"/>
<PropertyValue Property="EditAction" String="Service.draftEdit"/>
<PropertyValue Property="PreparationAction" String="Service.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
```
**Behavior**:
- Edit creates draft copy
- Save updates draft
- Save & Exit activates draft
- Cancel discards draft
- Warning on navigation if unsaved changes
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: draft)
---
## Flexible Column Layout
Multi-column responsive layout for master-detail-detail views:
**manifest.json**:
```json
{
"sap.ui5": {
"routing": {
"config": {
"routerClass": "sap.f.routing.Router",
"flexibleColumnLayout": {
"defaultTwoColumnLayoutType": "TwoColumnsMidExpanded",
"defaultThreeColumnLayoutType": "ThreeColumnsMidExpanded"
}
},
"routes": [
{
"pattern": "",
"name": "ProductsList",
"target": ["ProductsList"]
},
{
"pattern": "Products({key})",
"name": "ProductDetail",
"target": ["ProductsList", "ProductDetail"]
},
{
"pattern": "Products({key})/Items({itemKey})",
"name": "ItemDetail",
"target": ["ProductsList", "ProductDetail", "ItemDetail"]
}
]
}
}
}
```
**Layout Types**:
- OneColumn
- TwoColumnsBeginExpanded
- TwoColumnsMidExpanded
- ThreeColumnsMidExpanded
- ThreeColumnsEndExpanded
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/10_More_About_Controls](https://github.com/SAP-docs/sapui5/tree/main/docs/10_More_About_Controls) (search: flexible-column-layout)
---
## Building Blocks
Reusable UI components for custom pages:
**Usage**:
```xml
<macros:Table
id="productTable"
contextPath="/Products"
metaPath="@com.sap.vocabularies.UI.v1.LineItem"
readOnly="true"/>
<macros:FilterBar
id="filterBar"
contextPath="/Products"
metaPath="@com.sap.vocabularies.UI.v1.SelectionFields"/>
<macros:Form
id="productForm"
contextPath="/Products"
metaPath="@com.sap.vocabularies.UI.v1.FieldGroup#General"/>
```
**Available Building Blocks**:
- Table
- Chart
- FilterBar
- Form
- Field
- MicroChart
- ValueHelp
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: building-blocks)
---
## Extension Points
Customize Fiori Elements apps without modifying templates:
### Controller Extensions
**manifest.json**:
```json
{
"sap.ui5": {
"extends": {
"extensions": {
"sap.ui.controllerExtensions": {
"sap.fe.templates.ListReport.ListReportController": {
"controllerName": "com.mycompany.myapp.ext.ListReportExtension"
}
}
}
}
}
}
```
**ext/ListReportExtension.controller.js**:
```javascript
sap.ui.define([
"sap/ui/core/mvc/ControllerExtension"
], function(ControllerExtension) {
"use strict";
return ControllerExtension.extend("com.mycompany.myapp.ext.ListReportExtension", {
override: {
onInit: function() {
// Custom initialization
},
routing: {
onBeforeBinding: function(oBindingContext) {
// Custom logic before binding
}
}
},
customAction: function() {
// Custom function
}
});
});
```
### Fragment Extensions
Add custom content to specific locations:
**manifest.json**:
```json
{
"sap.ui5": {
"extends": {
"extensions": {
"sap.ui.viewExtensions": {
"sap.fe.templates.ListReport.ListReport": {
"ResponsiveTableColumnsExtension::Products": {
"className": "sap.ui.core.Fragment",
"fragmentName": "com.mycompany.myapp.ext.CustomColumns",
"type": "XML"
}
}
}
}
}
}
}
```
**ext/CustomColumns.fragment.xml**:
```xml
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<Column>
<Text text="Custom Column"/>
</Column>
</core:FragmentDefinition>
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: extensibility, extension-points)
---
## manifest.json Configuration
**Complete Example**:
```json
{
"_version": "1.42.0",
"sap.app": {
"id": "com.mycompany.products",
"type": "application",
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"mainService": {
"uri": "/sap/opu/odata/sap/PRODUCT_SRV/",
"type": "OData",
"settings": {
"annotations": ["annotation"],
"localUri": "localService/metadata.xml",
"odataVersion": "2.0"
}
},
"annotation": {
"type": "ODataAnnotation",
"uri": "annotations/annotation.xml",
"settings": {
"localUri": "annotations/annotation.xml"
}
}
}
},
"sap.ui5": {
"dependencies": {
"minUI5Version": "1.120.0",
"libs": {
"sap.fe.templates": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "com.mycompany.products.i18n.i18n"
}
},
"": {
"dataSource": "mainService",
"preload": true,
"settings": {
"defaultBindingMode": "TwoWay",
"defaultCountMode": "Inline",
"refreshAfterChange": false,
"metadataUrlParams": {
"sap-value-list": "none"
}
}
}
},
"routing": {
"config": {
"flexibleColumnLayout": {
"defaultTwoColumnLayoutType": "TwoColumnsMidExpanded",
"defaultThreeColumnLayoutType": "ThreeColumnsMidExpanded"
},
"routerClass": "sap.f.routing.Router"
},
"routes": [
{
"pattern": ":?query:",
"name": "ProductsList",
"target": ["ProductsList"]
},
{
"pattern": "Products({key}):?query:",
"name": "ProductDetail",
"target": ["ProductsList", "ProductDetail"]
}
],
"targets": {
"ProductsList": {
"type": "Component",
"id": "ProductsList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"contextPath": "/Products",
"variantManagement": "Page",
"navigation": {
"Products": {
"detail": {
"route": "ProductDetail"
}
}
},
"initialLoad": true,
"tableSettings": {
"type": "ResponsiveTable",
"selectAll": true,
"selectionMode": "Multi"
}
}
}
},
"ProductDetail": {
"type": "Component",
"id": "ProductDetail",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"contextPath": "/Products",
"editableHeaderContent": true
}
}
}
}
}
},
"sap.fiori": {
"registrationIds": [],
"archeType": "transactional"
}
}
```
---
## Links to Official Documentation
- **Fiori Elements Overview**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements)
- **Annotations**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: annotations)
- **Building Blocks**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: building-blocks)
- **Extensions**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: extensibility)
- **Draft Handling**: [https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements](https://github.com/SAP-docs/sapui5/tree/main/docs/06_SAP_Fiori_Elements) (search: draft)
---
**Note**: This document covers SAP Fiori Elements configuration and usage. For specific templates and advanced scenarios, refer to the official documentation links provided.

326
references/glossary.md Normal file
View File

@@ -0,0 +1,326 @@
# SAPUI5 Glossary
**Source**: [https://github.com/SAP-docs/sapui5/blob/main/docs/glossary-9ef211e.md](https://github.com/SAP-docs/sapui5/blob/main/docs/glossary-9ef211e.md)
**Last Updated**: 2025-11-21
---
## Core UI Concepts
**Aggregation**: A special relationship defining parent-child hierarchies in UI elements. The parent end has cardinality 0..1, while children may have 0..1 or 0..*. Used to build complex UI structures.
**Association**: A loose coupling between UI element types independent of tree structure, implemented by storing target element instance IDs. Used for references without ownership.
**Control**: Independent UI elements like Button, Label, TextField, or Table that manage rectangular screen regions and serve as entry points for rendering. Controls are the building blocks of SAPUI5 applications.
**Element**: The basic building block of user interfaces—reusable entities with properties, events, methods, and relations forming tree structures.
**Property**: Named attributes with associated data types, accessible via getters and setters, with well-defined default values. Examples: text, enabled, visible.
**Event**: Named occurrences with parameters; the element's API supports managing event subscriptions. Examples: press, change, selectionChange.
---
## Architecture & Development
**Library**: Top-level structural units bundling controls, types, and making them consumable by applications. Examples: sap.m (mobile), sap.ui.table (tables), sap.f (fiori).
**Model**: Data provider assigned to UI where controls bind to data. Various types available depending on server-side data formats (JSON, OData, XML, Resource).
**View**: Application unit defining control definitions for the user interface layer's appearance. Available types: XML, JSON, JavaScript, HTML.
**Controller**: Active application unit serving as the logical interface between model and view, embodying MVC concepts. Contains event handlers and business logic.
**Component**: Self-contained, reusable unit with manifest.json configuration. Represents a complete application or reusable part.
**Bootstrap**: Process of loading and initializing SAPUI5 runtime in HTML pages via the sap-ui-core.js script tag.
**Fragment**: Reusable UI snippet without its own controller. Can be XML, JSON, JavaScript, or HTML format.
---
## Data & Integration
**Data Binding**: Technique synchronizing two data sources, with changes in one automatically reflected in the other. Supports one-way, two-way, property, aggregation, and element binding.
**Data Type**: First-class entities in the meta model allowing reuse across libraries and system extensibility. Examples: sap.ui.model.type.String, sap.ui.model.type.Date, sap.ui.model.type.Currency.
**OData**: "Standard protocol that defines best practices for building and consuming an interface to backend systems over the web." Based on REST principles.
**OData Model**: Implementation supporting the Open Data Protocol format for structured data communication. Available in v2 and v4 versions.
**Mock Server**: Mocking framework for HTTP/HTTPS simplifying integration testing and decoupling development teams. Simulates OData services.
**Resource Model**: Model for internationalization (i18n) containing translated texts. Uses Java properties file format.
**JSON Model**: Client-side model for JavaScript object data. Suitable for small datasets and configuration data.
**XML Model**: Client-side model for XML data structures.
---
## SAP Fiori Elements
**SAP Fiori Elements**: Predefined template views and controllers enabling developers to create applications based on OData services and annotations "requiring no JavaScript UI coding."
**Annotations**: Metadata accompanying application data, controlling display, position, sort order, measures, dimensions, and navigation enablement in UI controls. Examples: @UI.LineItem, @UI.SelectionFields, @UI.FieldGroup.
**Key Annotations**: Critical annotations defining rendering or behavior. Examples: LineItem, SelectionFields, InsertRestrictions, UpdateRestrictions.
**Manifest**: Main configuration file (manifest.json) where developers define application settings and interface behavior, including filter fields, table properties, and routing.
**Action**: "Business function in the application backend that can be triggered by the user from the UI using an action button." Can be bound or unbound.
**Facet**: Basic building block of object pages—can be header facets, reference facets (rendered as forms/tables/charts), or collection facets (sections holding multiple references).
**Section**: Top-level grouping of object page facets below the header, containing subsections with associated facets.
**Entity**: Business object in OData definitions; associations define relationships enabling navigation. Example: SalesOrder with SalesOrderItems.
**Main Entity**: Entry entity holding information displayed in SAP Fiori templates. Example: SalesOrder in a Manage Sales Order application.
**Navigation Entity**: Associated entities with detailed information (1:n or 1:1 relationships with main objects). Example: SalesOrderItems related to SalesOrder.
**iAppState**: Inner application state storing information required to retrieve app state when URLs reload, including filters and sort orders.
**xAppState**: External application state representing source app context passed during navigation, including filter values and selected row data.
**Determining Action**: Finalizing action at page bottom applying to entire page context (object/subobject pages). Example: Approve, Reject.
**Paginator Buttons**: Toolbar buttons enabling navigation between previous/next object pages displayed in list reports.
**Draft**: Temporary version of a business entity allowing users to save incomplete work. Drafts are stored separately from active data.
**Flexible Column Layout**: Responsive UI pattern showing up to 3 columns (list, detail, detail-detail) based on screen size.
---
## MVC Pattern
**Model-View-Controller (MVC)**: Architectural pattern separating application logic (controller), data (model), and presentation (view).
**Binding Context**: Link between a UI element and data in a model. Determines which data is displayed.
**Binding Path**: String expression pointing to data in a model. Absolute paths start with "/", relative paths don't.
**Formatter**: Function converting model data to display format. Example: formatting date, currency, or status.
**Expression Binding**: Inline data binding with simple expressions. Example: {= ${quantity} > 10 ? 'high' : 'low'}.
---
## Testing & Quality
**QUnit**: JavaScript unit testing framework packaged with SAPUI5. Used for testing individual functions and modules.
**OPA5**: "API for SAPUI5 controls that hides asynchronicity and eases access," helping test user interactions, navigation, and data binding. Stands for One Page Acceptance tests.
**wdi5**: Webdriver.IO service for cross-platform end-to-end testing on SAPUI5 applications with OPA5-compatible selectors.
**Mock Server**: Tool simulating OData backend services for testing without real backend connection.
**Test Automation**: Automated execution of tests using tools like OPA5, wdi5, or UIVeri5.
**Support Assistant**: Built-in tool for checking applications against best practices and identifying common issues.
**Diagnostics**: Available window in SAPUI5 applications accessed via [Ctrl]+[Shift]+[Alt]/[Option][S]. Shows technical information, debug options, and support tools.
**Demo Kit**: "SAPUI5 software development kit providing documentation, API reference, samples, and demo apps." Available at [https://sapui5.hana.ondemand.com/](https://sapui5.hana.ondemand.com/)
---
## Performance & Security
**Cache Buster**: Mechanism notifying browsers to refresh resources only when application resources change. Uses timestamp or hash in URLs.
**Content Density**: Adaptive design feature allowing controls to adjust for touch-enabled devices (larger - cozy) or mouse-operated devices (compact).
**Asynchronous Processing**: Non-blocking mode executing tasks in background with callback triggers; "highly recommended for performance reasons and to not freeze the UI."
**Synchronous Processing**: Blocking mode keeping browser thread busy until tasks complete; not recommended for long-running operations.
**Lazy Loading**: Loading resources (views, controls, libraries) only when needed, improving initial load time.
**Bundling**: Combining multiple files into fewer files to reduce HTTP requests.
**Minification**: Removing whitespace and shortening variable names to reduce file size.
**Preload Files**: Bundled resources loaded at application startup. Generated by UI5 Tooling.
**Component Preload**: Single file containing all component resources for faster loading.
**Clickjacking**: "UI redressing that tricks users into triggering actions by redirecting clicks," often using invisible iFrames. Prevented using frame-options and CSP.
**Cross-Site Scripting (XSS)**: "Injecting script code into web pages executed in page context," potentially accessing displayed information and session cookies. SAPUI5 provides automatic XSS protection via output encoding.
**Content Security Policy (CSP)**: Security standard preventing XSS by controlling which resources can be loaded. SAPUI5 supports CSP-compliant development.
**Input Validation**: Checking user input for valid format and content. Use data types and validators.
**Output Encoding**: Converting special characters to prevent XSS. Automatically applied by SAPUI5.
---
## Accessibility & Internationalization
**ARIA**: "Accessible Rich Internet Applications Suite defining ways to make Web content and applications more accessible to people with disabilities," especially for dynamic content and advanced controls. SAPUI5 controls implement ARIA attributes.
**Screen Reader**: Assistive technology reading UI content aloud. SAPUI5 controls provide screen reader support.
**Keyboard Navigation**: Navigating and operating UI using keyboard only. SAPUI5 follows accessibility guidelines.
**High Contrast Theme**: Theme with increased contrast for visually impaired users. Suffix: _hc or _hcb.
**Right-to-Left (RTL) Text Directionality**: "Essential for enabling HTML in right-to-left scripts such as Arabic, Hebrew, Syriac, and Thaana." Enabled via configuration.
**Internationalization (i18n)**: Making applications work in multiple languages and regions. Uses resource bundles.
**Localization (l10n)**: Translating applications to specific languages. Uses i18n resource model.
**Resource Bundle**: Properties file containing translated texts. Format: key=value.
**Text Symbol**: Placeholder in resource bundle. Example: {i18n>title}.
---
## Routing & Navigation
**Router**: Component managing application URLs and navigation. Configured in manifest.json.
**Route**: URL pattern mapped to a specific view. Example: "product/{productId}".
**Route Pattern**: URL template with parameters. Example: "details/{orderId}".
**Navigation Parameter**: Data passed via URL. Example: productId in "product/123".
**Hash**: URL part after "#". Used for client-side routing. Example: #/products/123.
**Target**: Definition of which view to display for a route.
**Routing Configuration**: Section in manifest.json defining routes and targets.
---
## Theming & Styling
**Theme**: Visual design of controls including colors, fonts, spacing. Examples: sap_fiori_3, sap_horizon.
**Theme Parameter**: CSS variable controlling theme appearance. Example: @sapUiBaseColor.
**Custom Theme**: User-created theme based on SAP theme. Built with UI Theme Designer.
**Content Density Class**: CSS class controlling size of controls. Values: sapUiSizeCozy, sapUiSizeCompact.
**Semantic Colors**: Theme colors with meaning. Examples: sapUiCriticalText, sapUiPositiveElement.
**Custom CSS**: Application-specific styles. Should use theme parameters for consistency.
---
## Build & Deployment
**UI5 Tooling**: Official build and development tools for SAPUI5/OpenUI5. Command-line tool: ui5.
**UI5 CLI**: Command-line interface for UI5 Tooling. Installed via npm: npm install -g @ui5/cli.
**UI5 Server**: Local development server with live reload. Started via: ui5 serve.
**UI5 Build**: Production build process creating optimized resources. Executed via: ui5 build.
**Namespace**: Unique identifier for application resources. Example: com.mycompany.myapp.
**Application Descriptor**: manifest.json file describing application configuration.
**Component Configuration**: Settings in manifest.json controlling component behavior.
---
## Supporting Technologies
**jQuery**: "Fast, small, feature-rich JavaScript library" packaged with SAPUI5, simplifying DOM manipulation, events, animation, and Ajax. Version bundled with SAPUI5 may differ from latest.
**AMD (Asynchronous Module Definition)**: "Mechanism for defining modules so that modules and their dependencies can be loaded asynchronously." SAPUI5 uses sap.ui.define for AMD.
**DOM (Document Object Model)**: "Platform- and language-neutral interface allowing programs to dynamically access and update document content, structure and style."
**SVG (Scalable Vector Graphics)**: "Markup language for describing two-dimensional graphics applications and images" with related script interfaces. Used for icons and charts.
**XML**: Markup language used for XML views, fragments, and OData metadata.
**JSON**: JavaScript Object Notation used for JSON models, manifest files, and configuration.
---
## APF (Analysis Path Framework)
**Analysis Path Framework (APF)**: Framework for building analytical applications with configurable analysis paths.
**Analysis Path**: Sequence of analytical steps exploring data. Users build paths interactively.
**Analysis Step**: Individual view of data (chart or table) in an analysis path.
**Representation**: Visual display of step data (chart type or table).
**Configuration**: Definition of available steps, representations, filters, and navigation targets.
**Configuration Modeler**: Tool for creating and editing APF configurations without coding.
**Smart Filter Bar**: Filter control with value help and personalization in APF applications.
---
## SAPUI5-Specific Features
**SAPUI5 ABAP Repository**: Used for storing apps, components, and libraries; "based on the Business Server Page (BSP) repository of the ABAP server."
**Composite Control**: Intended for reuse within control development, allowing inclusion of existing controls in complex controls.
**Notepad Control**: Control defined on-the-fly "without a library definition or running generation steps." For prototyping only.
**Flexibility**: Framework enabling UI adaptation at runtime. Used for key user adaptation and personalization.
**Variant Management**: Saving and loading different UI configurations (filters, table columns, sort orders).
**Personalization**: User-specific UI customizations (column order, filter values).
**Adaptation**: Modifying applications without changing code. Types: key user adaptation, developer adaptation.
**Extension Point**: Predefined location where applications can be extended with custom content.
**Side Effects**: Allow applications to refresh entire controls or properties when source properties update. Example: recalculating discounts when quantity changes.
**Text Arrangement**: Annotation property controlling ID-type field display. Values: TextOnly, TextLast, TextFirst, TextSeparate.
**LineItem**: Annotation defining table columns visible during rendering in Fiori Elements apps.
**Value Help**: Dialog or dropdown showing possible values for input field. Uses annotations: @Common.ValueList.
**Quick View**: Popover showing additional information without navigation. Example: product details on hover.
---
## Additional Concepts
**Entity Set**: Collection of entities of same type in OData service. Example: Products, SalesOrders.
**Navigation Property**: OData property linking entities. Example: SalesOrder to SalesOrderItems.
**Service Metadata**: OData XML document describing service structure (entities, properties, associations).
**Function Import**: OData operation exposed by service. Can be functions (read-only) or actions (with side effects).
**Batch Request**: Single HTTP request containing multiple OData operations. Reduces network overhead.
**Deep Insert**: Creating entity with related entities in single request. Example: creating order with order items.
**Expand**: OData query option loading related entities. Example: /SalesOrders?$expand=Items.
**Filter**: OData query option filtering entities. Example: /Products?$filter=Price gt 100.
**Distribution Layer**: Contains control and theme libraries within SAPUI5 architecture.
**Service**: Backend code delivering functionalities like data retrieval or action execution, reusable by various client applications.
---
**Note**: This glossary is extracted from official SAP SAPUI5 documentation and should be used as a reference for understanding SAPUI5 terminology and concepts.

View File

@@ -0,0 +1,721 @@
# Metadata-Driven Controls (MDC) and TypeScript Control Library Development
**Sources**:
- MDC Tutorial: [https://github.com/SAP-samples/ui5-mdc-json-tutorial](https://github.com/SAP-samples/ui5-mdc-json-tutorial)
- TypeScript Control Library: [https://github.com/SAP-samples/ui5-typescript-control-library](https://github.com/SAP-samples/ui5-typescript-control-library)
**Last Updated**: 2025-11-22
---
## Overview
This reference covers two advanced SAPUI5 TypeScript topics:
1. **Metadata-Driven Controls (MDC)**: Building dynamic UIs driven by metadata at runtime
2. **TypeScript Control Libraries**: Developing reusable UI5 control libraries in TypeScript
Both topics use TypeScript as the implementation language and represent modern best practices for SAPUI5 development.
---
## Part A: Metadata-Driven Controls (MDC)
### What is MDC?
Metadata-Driven Controls (sap.ui.mdc) are powerful UI5 controls that allow dynamic UI creation at runtime based on metadata. Instead of explicitly defining every control, you configure and modify them based on provided metadata.
**Official Documentation**:
- API Reference: [https://sdk.openui5.org/api/sap.ui.mdc](https://sdk.openui5.org/api/sap.ui.mdc)
- Documentation: [https://sdk.openui5.org/topic/1dd2aa91115d43409452a271d11be95b](https://sdk.openui5.org/topic/1dd2aa91115d43409452a271d11be95b)
**Key Benefits**:
- Dynamic UI generation at runtime
- Flexible customization through delegates
- Built-in personalization and variant management
- Consistent filter and table patterns
---
### Core MDC Concepts
#### 1. Control Delegates
Delegates implement service or application-specific behavior for MDC controls. They customize default behavior depending on specific needs.
**Delegate Responsibilities**:
- Custom control creation
- Metadata provision
- Data binding configuration
- Service-specific adaptations
**Example Delegate Structure**:
```typescript
import TableDelegate from "sap/ui/mdc/TableDelegate";
const CustomTableDelegate = Object.assign({}, TableDelegate);
CustomTableDelegate.fetchProperties = async function(table) {
return [
{
name: "name",
label: "Name",
dataType: "sap.ui.model.type.String"
},
{
name: "price",
label: "Price",
dataType: "sap.ui.model.type.Float"
}
];
};
export default CustomTableDelegate;
```
#### 2. PropertyInfo
PropertyInfo defines metadata that controls how MDC components behave. It describes the data characteristics and control settings.
**PropertyInfo Structure**:
```typescript
interface PropertyInfo {
name: string; // Unique identifier
label: string; // Display label
dataType: string; // Data type (sap.ui.model.type.*)
path?: string; // Binding path
sortable?: boolean; // Can be sorted
filterable?: boolean; // Can be filtered
groupable?: boolean; // Can be grouped
maxConditions?: number; // Max filter conditions
}
```
**Example**:
```typescript
const propertyInfo: PropertyInfo[] = [
{
name: "productId",
label: "Product ID",
dataType: "sap.ui.model.type.String",
sortable: true,
filterable: true
},
{
name: "price",
label: "Price",
dataType: "sap.ui.model.type.Float",
sortable: true,
filterable: true,
maxConditions: -1 // Unlimited conditions
}
];
```
#### 3. TypeMap
TypeMap enables custom types in MDC controls. When standard types are insufficient, you can add custom types.
**Registering Custom Types**:
```typescript
import DefaultTypeMap from "sap/ui/mdc/DefaultTypeMap";
import BaseType from "sap/ui/mdc/enums/BaseType";
// Register custom type
DefaultTypeMap.set(
"my.custom.Type",
BaseType.String,
{
// Type configuration
}
);
```
#### 4. VariantManagement
VariantManagement saves user personalization settings (table layout, filter conditions, etc.) for later retrieval.
**Enabling Variant Management**:
```xml
<mdc:Table
id="mdcTable"
p13nMode="Sort,Filter,Column"
variantManagement="Page">
<!-- Table content -->
</mdc:Table>
```
---
### MDC Controls
#### MDC Table
Display data in tabular format with dynamic columns:
```xml
<mvc:View
xmlns:mvc="sap.ui.core.mvc"
xmlns:mdc="sap.ui.mdc"
xmlns:mdcTable="sap.ui.mdc.table">
<mdc:Table
id="mdcTable"
delegate='{name: "my/app/delegate/TableDelegate", payload: {}}'
p13nMode="Sort,Filter,Column"
type="ResponsiveTable">
<mdc:columns>
<mdcTable:Column
propertyKey="name"
header="Name">
<Text text="{name}"/>
</mdcTable:Column>
<mdcTable:Column
propertyKey="price"
header="Price">
<Text text="{price}"/>
</mdcTable:Column>
</mdc:columns>
</mdc:Table>
</mvc:View>
```
#### MDC FilterBar
Complex filtering with PropertyInfo:
```xml
<mdc:FilterBar
id="filterBar"
delegate='{name: "my/app/delegate/FilterBarDelegate", payload: {}}'
p13nMode="Item,Value">
<mdc:filterItems>
<mdc:FilterField
propertyKey="name"
label="Name"
dataType="sap.ui.model.type.String"
conditions="{$filters>/conditions/name}"/>
<mdc:FilterField
propertyKey="price"
label="Price"
dataType="sap.ui.model.type.Float"
conditions="{$filters>/conditions/price}"/>
</mdc:filterItems>
</mdc:FilterBar>
```
#### MDC Value Help
Assisted data input with suggestions:
```xml
<mdc:FilterField
propertyKey="category"
label="Category"
valueHelp="categoryValueHelp">
</mdc:FilterField>
<mdc:ValueHelp
id="categoryValueHelp"
delegate='{name: "my/app/delegate/ValueHelpDelegate", payload: {}}'>
<mdc:typeahead>
<mdcvh:Popover title="Categories">
<mdcvh:content>
<mdcvh:MTable
keyPath="categoryId"
descriptionPath="categoryName">
</mdcvh:MTable>
</mdcvh:content>
</mdcvh:Popover>
</mdc:typeahead>
</mdc:ValueHelp>
```
---
### MDC Tutorial Exercises
The official SAP MDC tutorial ([https://github.com/SAP-samples/ui5-mdc-json-tutorial](https://github.com/SAP-samples/ui5-mdc-json-tutorial)) covers:
| Exercise | Topic | Key Learnings |
|----------|-------|---------------|
| ex0 | Project Setup | TypeScript configuration, dependencies |
| ex1 | MDC Table | Table delegate, columns, PropertyInfo |
| ex2 | MDC FilterBar | Filter delegate, filter fields, conditions |
| ex3 | Value Helps | Value help delegate, typeahead, popover |
| ex4 | Custom Types | TypeMap, custom type registration |
| ex5 | VariantManagement | Personalization, saving variants |
**Running the Tutorial**:
```bash
git clone [https://github.com/SAP-samples/ui5-mdc-json-tutorial.git](https://github.com/SAP-samples/ui5-mdc-json-tutorial.git)
cd ui5-mdc-json-tutorial/ex5 # Or any exercise
npm install
npm start
```
---
## Part B: TypeScript Control Library Development
### Overview
Developing UI5 control libraries in TypeScript provides type safety, better tooling support, and improved maintainability for reusable components.
**Source Repository**: [https://github.com/SAP-samples/ui5-typescript-control-library](https://github.com/SAP-samples/ui5-typescript-control-library)
---
### Project Setup
#### Package.json
```json
{
"name": "my-ui5-control-library",
"version": "1.0.0",
"scripts": {
"build": "ui5 build --config ui5-dist.yaml",
"start": "ui5 serve --open /test-resources/index.html",
"watch": "npm-run-all --parallel watch:ts start:server",
"watch:ts": "npx @ui5/ts-interface-generator --watch",
"start:server": "ui5 serve --open /test-resources/index.html",
"test": "karma start karma-ci.conf.js",
"build:jsdoc": "ui5 build jsdoc --config ui5-jsdoc.yaml"
},
"devDependencies": {
"@sapui5/types": "^1.120.0",
"@ui5/cli": "^3.0.0",
"@ui5/ts-interface-generator": "^0.8.0",
"typescript": "^5.0.0",
"ui5-tooling-transpile": "^3.0.0"
}
}
```
#### tsconfig.json
```json
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "node",
"lib": ["es2022", "dom"],
"types": ["@sapui5/types"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"skipLibCheck": true,
"paths": {
"com/myorg/myui5lib/*": ["./src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
```
**Important**: The `paths` mapping enables references using the library name to work correctly.
#### ui5.yaml
```yaml
specVersion: "3.0"
metadata:
name: com.myorg.myui5lib
type: library
framework:
name: SAPUI5
version: "1.120.0"
libraries:
- name: sap.m
- name: sap.ui.core
builder:
customTasks:
- name: ui5-tooling-transpile-task
afterTask: replaceVersion
server:
customMiddleware:
- name: ui5-tooling-transpile-middleware
afterMiddleware: compression
- name: ui5-middleware-livereload
afterMiddleware: compression
```
---
### Control Implementation
#### Control File (src/Example.ts)
```typescript
import Control from "sap/ui/core/Control";
import RenderManager from "sap/ui/core/RenderManager";
import type { MetadataOptions } from "sap/ui/core/Element";
/**
* Custom Example Control
* @namespace com.myorg.myui5lib
* @name com.myorg.myui5lib.Example
*/
export default class Example extends Control {
// Metadata definition
static readonly metadata: MetadataOptions = {
library: "com.myorg.myui5lib",
properties: {
/**
* The text to display
*/
text: {
type: "string",
defaultValue: ""
},
/**
* The color variant
*/
color: {
type: "com.myorg.myui5lib.ExampleColor",
defaultValue: "Default"
}
},
events: {
/**
* Fired when the control is pressed
*/
press: {}
},
aggregations: {
/**
* Hidden aggregation for internal content
*/
_content: {
type: "sap.ui.core.Control",
multiple: false,
visibility: "hidden"
}
}
};
// Constructor signatures for TypeScript
constructor(id?: string | $ExampleSettings);
constructor(id?: string, settings?: $ExampleSettings);
constructor(id?: string, settings?: $ExampleSettings) {
super(id, settings);
}
// Renderer
static renderer = {
apiVersion: 2,
render: function(rm: RenderManager, control: Example): void {
rm.openStart("div", control);
rm.class("myExampleControl");
rm.openEnd();
rm.text(control.getText());
rm.close("div");
}
};
// Event handler
onclick(): void {
this.fireEvent("press");
}
}
```
**Key Points**:
- Use `@name` JSDoc tag with full control name for proper transformation
- Import `MetadataOptions` from `sap/ui/core/Element` for type safety
- Include constructor signatures for TypeScript awareness
- Export as default export immediately with class definition
#### Interface Generation
The `@ui5/ts-interface-generator` creates TypeScript interfaces for generated accessor methods (getText, setText, etc.):
```bash
# Run once
npx @ui5/ts-interface-generator
# Watch mode during development
npx @ui5/ts-interface-generator --watch
```
This generates `Example.gen.d.ts` next to each control file with declarations like:
```typescript
interface $ExampleSettings extends $ControlSettings {
text?: string;
color?: ExampleColor;
press?: (event: Event) => void;
}
interface Example {
getText(): string;
setText(text: string): this;
getColor(): ExampleColor;
setColor(color: ExampleColor): this;
attachPress(handler: Function): this;
detachPress(handler: Function): this;
firePress(): this;
}
```
---
### Library File (src/library.ts)
```typescript
import ObjectPath from "sap/base/util/ObjectPath";
import { registerLibrary } from "sap/ui/core/Lib";
/**
* My UI5 Control Library
* @namespace com.myorg.myui5lib
*/
// Library version (replaced during build)
const version = "${version}";
// Register library
registerLibrary("com.myorg.myui5lib", {
name: "com.myorg.myui5lib",
version: version,
dependencies: ["sap.ui.core", "sap.m"],
types: ["com.myorg.myui5lib.ExampleColor"],
interfaces: [],
controls: ["com.myorg.myui5lib.Example"],
elements: [],
noLibraryCSS: false
});
// Define enum
export enum ExampleColor {
Default = "Default",
Highlight = "Highlight",
Warning = "Warning"
}
// CRITICAL: Attach enum to global namespace for UI5 runtime
const thisLib = ObjectPath.get("com.myorg.myui5lib") as Record<string, unknown>;
thisLib.ExampleColor = ExampleColor;
```
**Critical Note**: Enums must be attached to the global namespace using `ObjectPath.get()`. UI5 needs this to find enum types for control properties. Forgetting this can cause XSS vulnerabilities!
---
### Renderer File (src/ExampleRenderer.ts)
For complex renderers, create a separate file:
```typescript
import RenderManager from "sap/ui/core/RenderManager";
import Example from "./Example";
/**
* Example renderer
* @namespace com.myorg.myui5lib
*/
export default {
apiVersion: 2,
render: function(rm: RenderManager, control: Example): void {
rm.openStart("div", control);
rm.class("myorgMyui5libExample");
// Add color class
const color = control.getColor();
if (color) {
rm.class("myorgMyui5libExample" + color);
}
rm.openEnd();
// Render text
rm.openStart("span");
rm.class("myorgMyui5libExampleText");
rm.openEnd();
rm.text(control.getText());
rm.close("span");
rm.close("div");
}
};
```
---
### Testing
#### Karma Configuration (karma-ci.conf.js)
```javascript
module.exports = function(config) {
config.set({
frameworks: ["ui5", "qunit"],
browsers: ["ChromeHeadless"],
ui5: {
configPath: "ui5.yaml"
},
singleRun: true
});
};
```
#### QUnit Test (test/Example.qunit.ts)
```typescript
import Example from "com/myorg/myui5lib/Example";
import { ExampleColor } from "com/myorg/myui5lib/library";
QUnit.module("Example Control Tests");
QUnit.test("Should create control with default values", (assert) => {
const control = new Example();
assert.strictEqual(control.getText(), "", "Default text is empty");
assert.strictEqual(control.getColor(), ExampleColor.Default, "Default color");
control.destroy();
});
QUnit.test("Should set and get text", (assert) => {
const control = new Example({ text: "Hello" });
assert.strictEqual(control.getText(), "Hello", "Text set correctly");
control.destroy();
});
QUnit.test("Should fire press event", (assert) => {
const done = assert.async();
const control = new Example();
control.attachPress(() => {
assert.ok(true, "Press event fired");
control.destroy();
done();
});
control.firePress();
});
```
---
### Build and Distribution
```bash
# Development
npm start # Start dev server with live reload
npm run watch # Watch mode with interface generation
# Testing
npm test # Run Karma tests
npm run test:coverage # With coverage
# Production build
npm run build # Build for distribution
# Documentation
npm run build:jsdoc # Generate JSDoc documentation
```
---
### Usage in Applications
#### Non-TypeScript Applications
TypeScript libraries work in JavaScript apps:
```javascript
sap.ui.define([
"com/myorg/myui5lib/Example"
], function(Example) {
var oExample = new Example({
text: "Hello",
color: "Highlight"
});
});
```
#### TypeScript Applications
```typescript
import Example from "com/myorg/myui5lib/Example";
import { ExampleColor } from "com/myorg/myui5lib/library";
const example = new Example({
text: "Hello",
color: ExampleColor.Highlight
});
```
---
## Best Practices
### MDC Best Practices
1. **Use TypeScript for MDC**: SAP recommends TypeScript for MDC development
2. **Design delegates carefully**: Delegates define behavior; keep them focused
3. **Define PropertyInfo completely**: Include all necessary metadata upfront
4. **Enable personalization**: Use p13nMode for user customization
5. **Test with multiple models**: MDC works with JSON, OData v2, and OData v4
### TypeScript Control Library Best Practices
1. **Use @ui5/ts-interface-generator**: Essential for accessor method types
2. **Register enums globally**: Critical for UI5 runtime to find types
3. **Export defaults immediately**: Combine export with class definition
4. **Type metadata as MetadataOptions**: Enables type checking and inheritance
5. **Use forward slashes in paths**: Cross-platform compatibility
6. **Version types with UI5 version**: Match @sapui5/types version to framework
---
## Common Issues
### Issue: TypeScript doesn't know control accessor methods
**Solution**: Run the interface generator:
```bash
npx @ui5/ts-interface-generator
```
### Issue: Enum not found at runtime
**Solution**: Ensure enum is attached to global namespace in library.ts:
```typescript
const thisLib = ObjectPath.get("com.myorg.myui5lib") as Record<string, unknown>;
thisLib.ExampleColor = ExampleColor;
```
### Issue: Delegate methods not called
**Solution**: Verify delegate path in XML view matches actual module path:
```xml
delegate='{name: "my/app/delegate/TableDelegate", payload: {}}'
```
### Issue: HMR crashes with interface generator
**Solution**: Use separate terminal for interface generator or use npm-run-all:
```bash
npm-run-all --parallel watch:ts start:server
```
---
## Resources
### MDC Resources
- **MDC Tutorial**: [https://github.com/SAP-samples/ui5-mdc-json-tutorial](https://github.com/SAP-samples/ui5-mdc-json-tutorial)
- **MDC API Reference**: [https://sdk.openui5.org/api/sap.ui.mdc](https://sdk.openui5.org/api/sap.ui.mdc)
- **MDC Documentation**: [https://sdk.openui5.org/topic/1dd2aa91115d43409452a271d11be95b](https://sdk.openui5.org/topic/1dd2aa91115d43409452a271d11be95b)
- **MDC Demokit Sample**: [https://sdk.openui5.org/entity/sap.ui.mdc/sample/sap.ui.mdc.demokit.sample.TableFilterBarJson](https://sdk.openui5.org/entity/sap.ui.mdc/sample/sap.ui.mdc.demokit.sample.TableFilterBarJson)
### TypeScript Control Library Resources
- **Sample Repository**: [https://github.com/SAP-samples/ui5-typescript-control-library](https://github.com/SAP-samples/ui5-typescript-control-library)
- **TypeScript Interface Generator**: [https://github.com/SAP/ui5-typescript/tree/main/packages/ts-interface-generator](https://github.com/SAP/ui5-typescript/tree/main/packages/ts-interface-generator)
- **UI5 Tooling Transpile**: [https://www.npmjs.com/package/ui5-tooling-transpile](https://www.npmjs.com/package/ui5-tooling-transpile)
- **TypeScript Hello World**: [https://github.com/SAP-samples/ui5-typescript-helloworld](https://github.com/SAP-samples/ui5-typescript-helloworld)
---
**Note**: Both tutorials are actively maintained by SAP. The TypeScript control library sample was last updated November 14, 2025, and the MDC tutorial April 29, 2025.

View File

@@ -0,0 +1,794 @@
# SAPUI5 Performance Optimization
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps)
**Last Updated**: 2025-11-21
---
## Core Loading Strategies
### 1. Asynchronous Loading (Critical)
Always enable asynchronous loading for optimal performance.
**Bootstrap Configuration**:
```html
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true"
data-sap-ui-on-init="module:sap/ui/core/ComponentSupport"
data-sap-ui-resource-roots='{
"my.app": "./"
}'>
</script>
```
**Component Interface**:
```javascript
sap.ui.define([
"sap/ui/core/UIComponent"
], function(UIComponent) {
"use strict";
return UIComponent.extend("my.app.Component", {
interfaces: ["sap.ui.core.IAsyncContentCreation"],
metadata: {
manifest: "json"
}
});
});
```
**Benefits**:
- Non-blocking module loading
- Parallel resource fetching
- Faster initial load time
- Better user experience
---
### 2. Manifest-First Approach
Configure dependencies in manifest.json instead of bootstrap.
**manifest.json**:
```json
{
"sap.ui5": {
"dependencies": {
"minUI5Version": "1.120.0",
"libs": {
"sap.m": {},
"sap.ui.core": {},
"sap.f": {}
}
}
}
}
```
**Benefits**:
- Dependency reuse across contexts
- Earlier rendering
- Parallel loading optimization
- Design-time tool support
---
### 3. Lazy Loading Libraries
Load heavy libraries only when needed:
**manifest.json**:
```json
{
"sap.ui5": {
"dependencies": {
"libs": {
"sap.m": {},
"sap.ui.table": {
"lazy": true
}
}
}
}
}
```
**Load Before Use**:
```javascript
sap.ui.require(["sap/ui/core/Lib"], function(Library) {
Library.load({ name: "sap.ui.table" }).then(function() {
// Library loaded, now create table
});
});
```
---
## Resource Optimization
### 1. CDN Distribution
Load SAPUI5 from CDN for better performance:
```html
<script
src="[https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"](https://sapui5.hana.ondemand.com/resources/sap-ui-core.js")
data-sap-ui-async="true">
</script>
```
**Benefits**:
- Global CDN distribution (AKAMAI)
- Reduced latency
- Caching across applications
- No server load
---
### 2. Component Preload
Enable component preload to bundle resources:
**Automatic** (UI5 Tooling):
```bash
ui5 build
```
Creates `Component-preload.js` containing:
- All views
- All controllers
- All fragments
- manifest.json
- i18n files
**Load Component Preload**:
```javascript
{
"sap.ui5": {
"dependencies": {
"components": {
"my.other.component": {
"lazy": false // Preload enabled by default
}
}
}
}
}
```
---
### 3. Library Preloads
Ensure library preloads are enabled (default):
**Check Configuration**:
```javascript
// Preloads enabled by default
// Don't set data-sap-ui-preload="sync" (synchronous loading)
```
**Verify Preload Loading**:
- Open Network tab in browser
- Look for `library-preload.js` files
- Should see ONE request per library, not many
---
### 4. i18n Configuration
Prevent 404 errors for missing language files:
**manifest.json**:
```json
{
"sap.ui5": {
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"settings": {
"bundleName": "my.app.i18n.i18n",
"supportedLocales": ["en", "de", "fr"],
"fallbackLocale": "en"
}
}
}
}
}
```
**File Structure**:
```
i18n/
├── i18n.properties (fallback)
├── i18n_en.properties (English)
├── i18n_de.properties (German)
└── i18n_fr.properties (French)
```
---
## Code Optimization
### 1. Replace Deprecated jQuery
Migrate from deprecated `jquery.sap.*` modules:
**Before** (deprecated):
```javascript
jQuery.sap.require("sap.m.MessageBox");
jQuery.sap.delayedCall(100, this, function() {});
```
**After** (modern):
```javascript
sap.ui.require(["sap/m/MessageBox"], function(MessageBox) {});
setTimeout(function() {}, 100);
```
**Migration Guide**:
- `jQuery.sap.delayedCall``setTimeout`
- `jQuery.sap.log``sap/base/Log`
- `jQuery.sap.uid``sap/base/util/uid`
- `jQuery.sap.getUriParameters``sap/base/util/UriParameters`
---
### 2. Asynchronous Factories
Use async variants for better performance:
**Components**:
```javascript
// Old (sync)
var oComponent = sap.ui.component({
name: "my.app"
});
// New (async)
sap.ui.require(["sap/ui/core/Component"], function(Component) {
Component.create({
name: "my.app",
manifest: true
}).then(function(oComponent) {
// Component ready
});
});
```
**Views**:
```javascript
// Old (sync)
var oView = sap.ui.view({
viewName: "my.app.view.Main",
type: "XML"
});
// New (async)
sap.ui.require(["sap/ui/core/mvc/XMLView"], function(XMLView) {
XMLView.create({
viewName: "my.app.view.Main"
}).then(function(oView) {
// View ready
});
});
```
**Controllers**:
```javascript
// Controllers loaded automatically with views (async)
```
**Resource Bundles**:
```javascript
// Old (sync)
var oBundle = jQuery.sap.resources({
url: "i18n/i18n.properties"
});
// New (async)
sap.ui.require(["sap/base/i18n/ResourceBundle"], function(ResourceBundle) {
ResourceBundle.create({
url: "i18n/i18n.properties",
async: true
}).then(function(oBundle) {
// Bundle ready
});
});
```
---
## Data Handling
### 1. OData Model Preload
Enable metadata preloading:
**manifest.json**:
```json
{
"sap.ui5": {
"models": {
"": {
"dataSource": "mainService",
"preload": true,
"settings": {
"defaultBindingMode": "TwoWay"
}
}
}
}
}
```
**Benefits**:
- Metadata loads during component init
- Faster first data request
- No metadata loading delay
---
### 2. Use $select for OData
Fetch only needed properties:
**Without $select** (bad):
```javascript
// Fetches ALL properties
this.getView().bindElement("/Products('123')");
```
**With $select** (good):
```javascript
// Fetches only specified properties
this.getView().bindElement({
path: "/Products('123')",
parameters: {
select: "ProductID,Name,Price,Category"
}
});
```
**In List/Table**:
```xml
<Table
items="{
path: '/Products',
parameters: {
select: 'ProductID,Name,Price',
expand: 'Category'
}
}">
```
**Benefits**:
- Smaller payload
- Faster backend queries
- Reduced network transfer
- Better performance
---
### 3. OData V4 Migration
Prefer OData V4 over V2:
**manifest.json**:
```json
{
"sap.app": {
"dataSources": {
"mainService": {
"uri": "/odata/v4/catalog/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
}
},
"sap.ui5": {
"models": {
"": {
"dataSource": "mainService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true
}
}
}
}
}
```
**Benefits**:
- Better performance
- Server-side operations
- Automatic $expand and $select
- Modern features
---
### 4. Batch Requests
Combine multiple requests:
**OData V2**:
```javascript
var oModel = this.getView().getModel();
oModel.setUseBatch(true);
oModel.setDeferredGroups(["changes"]);
// Add to batch
oModel.create("/Products", oData1, { groupId: "changes" });
oModel.create("/Products", oData2, { groupId: "changes" });
oModel.update("/Products('1')", oData3, { groupId: "changes" });
// Submit batch
oModel.submitChanges({
groupId: "changes",
success: function() {
MessageToast.show("All changes saved");
}
});
```
**Benefits**:
- Single HTTP request
- Reduced network overhead
- Better performance
- Atomic operations
---
### 5. Metadata Caching
Enable metadata caching for ABAP backends:
**Automatic**: SAPUI5 uses cache tokens automatically
**Check Network**:
- Look for `sap-context-token` parameter
- Metadata should load from cache on subsequent visits
---
## UI Control Performance
### 1. Table Selection
Choose appropriate table control:
**sap.m.Table** (Mobile/Responsive):
- Good for: < 100 rows
- Keeps all rows in DOM
- Better for responsive layouts
- Growing/scrolling support
**sap.ui.table.Table** (Grid/Analytical):
- Good for: 100+ rows
- Virtual scrolling (only visible rows in DOM)
- High performance for large datasets
- Fixed column layout
**Example**:
```xml
<!-- For < 100 rows -->
<m:Table
items="{/Products}"
growing="true"
growingThreshold="20">
</m:Table>
<!-- For 100+ rows -->
<table:Table
rows="{/Products}"
visibleRowCount="20"
threshold="50">
</table:Table>
```
---
### 2. Reduce Control Complexity
Minimize controls in repeated aggregations:
**Bad** (heavy):
```xml
<items>
<ColumnListItem>
<cells>
<VBox>
<Text text="{Name}"/>
<Text text="{Description}"/>
<HBox>
<Button text="Edit"/>
<Button text="Delete"/>
</HBox>
</VBox>
</cells>
</ColumnListItem>
</items>
```
**Good** (light):
```xml
<items>
<ColumnListItem>
<cells>
<ObjectIdentifier
title="{Name}"
text="{Description}"/>
</cells>
</ColumnListItem>
</items>
```
---
### 3. Remove Hidden Columns
Don't hide columns with `visible="false"` in tables - remove them entirely:
**Bad**:
```xml
<columns>
<Column visible="false">
<Text text="Hidden Column"/>
</Column>
</columns>
```
**Good**:
```javascript
// Add/remove columns dynamically based on device
if (sap.ui.Device.system.desktop) {
oTable.addColumn(oColumn);
}
```
---
## Network & Debugging
### 1. Network Inspection
Monitor browser Network tab:
**Check for**:
- Synchronous blocking requests (waterfall shows blocks)
- Excessive request count (> 50 for initial load)
- Large response sizes
- Slow backend responses
- 404 errors (missing files)
- Duplicate requests
**Tools**:
- Chrome DevTools Network tab
- Firefox Network Monitor
- UI5 Inspector extension
---
### 2. Avoid Debug Mode
Never use debug mode in production:
**Development**:
```
[http://localhost:8080/index.html?sap-ui-debug=true](http://localhost:8080/index.html?sap-ui-debug=true)
```
**Production** (don't do this):
```
[http://myapp.com/index.html?sap-ui-debug=true](http://myapp.com/index.html?sap-ui-debug=true)
```
**Why**: Debug mode loads individual source files instead of preloads.
---
### 3. Performance Measurement
Use built-in performance tools:
**Enable Performance Trace**:
```javascript
sap.ui.require(["sap/ui/performance/Measurement"], function(Measurement) {
Measurement.setActive(true);
// Custom measurements
Measurement.start("myOperation");
// ... operation ...
Measurement.end("myOperation");
// Get results
var aMeasurements = Measurement.getAllMeasurements();
console.table(aMeasurements);
});
```
**UI5 Inspector**:
- Open UI5 Inspector extension
- Go to Performance tab
- See loading times, rendering times, etc.
---
## Anti-Patterns to Avoid
### 1. setTimeout with Delays
**Bad**:
```javascript
setTimeout(function() {
this.doSomething();
}.bind(this), 100); // 100ms delay
```
**Good**:
```javascript
// Do immediately when possible
this.doSomething();
// Or use events
this.attachEventOnce("dataReceived", this.doSomething, this);
```
---
### 2. Visibility-Based Lazy Loading
**Bad**:
```javascript
onAfterRendering: function() {
if (this.byId("panel").getVisible()) {
this.loadData();
}
}
```
**Good**:
```javascript
// Use routing or explicit user actions
onButtonPress: function() {
this.loadData();
}
```
---
### 3. Blocking Rendering
**Bad**:
```javascript
onInit: function() {
// Blocks rendering until data loaded
var oData = this.loadDataSync(); // Synchronous
this.getView().setModel(new JSONModel(oData));
}
```
**Good**:
```javascript
onInit: function() {
// Show loading, load async
this.getView().setBusy(true);
this.loadDataAsync().then(function(oData) {
this.getView().setModel(new JSONModel(oData));
this.getView().setBusy(false);
}.bind(this));
}
```
---
### 4. Excessive Dependencies
**Bad**:
```javascript
sap.ui.define([
"sap/m/Button",
"sap/m/Input",
"sap/m/Text",
// ... 50 more controls
], function(Button, Input, Text, ...) {
// Only using 5 of them
});
```
**Good**:
```javascript
sap.ui.define([
"sap/m/Button",
"sap/m/Input"
], function(Button, Input) {
// Only what's needed
});
```
---
## Performance Checklist
### Loading
- [ ] Async loading enabled (`data-sap-ui-async="true"`)
- [ ] Component implements `IAsyncContentCreation`
- [ ] Dependencies in manifest.json
- [ ] Heavy libraries lazy loaded
- [ ] Component preload generated
- [ ] CDN used for SAPUI5
### Data
- [ ] OData metadata preload enabled
- [ ] $select used for queries
- [ ] Batch requests for multiple operations
- [ ] OData V4 used when possible
- [ ] Metadata caching working
### UI
- [ ] Appropriate table control chosen
- [ ] Virtual scrolling for large lists
- [ ] Control complexity minimized
- [ ] No hidden columns in tables
- [ ] Growing lists for mobile
### Code
- [ ] No deprecated jQuery.sap modules
- [ ] Async factories used
- [ ] No setTimeout delays
- [ ] No visibility-based lazy loading
- [ ] No blocking rendering
### Network
- [ ] < 50 requests for initial load
- [ ] No 404 errors
- [ ] No duplicate requests
- [ ] Debug mode disabled
- [ ] Preloads loading correctly
---
## Performance Metrics
**Target Initial Load Time**:
- **3G Network**: < 5 seconds
- **LTE Network**: < 2 seconds
- **Desktop/WiFi**: < 1 second
**Measure Performance**:
```bash
# Lighthouse audit
lighthouse [https://myapp.com](https://myapp.com) --view
# WebPageTest
# Visit [https://www.webpagetest.org/](https://www.webpagetest.org/)
```
---
## Official Documentation
- **Performance**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: performance)
- **Best Practices**: [https://sapui5.hana.ondemand.com/#/topic/408b40efed3c416681e1bd8cdd8910d4](https://sapui5.hana.ondemand.com/#/topic/408b40efed3c416681e1bd8cdd8910d4)
- **Performance Measurement**: [https://sapui5.hana.ondemand.com/#/api/sap.ui.performance.Measurement](https://sapui5.hana.ondemand.com/#/api/sap.ui.performance.Measurement)
---
**Note**: This document covers performance optimization for SAPUI5 applications. Implement these practices for production-ready, high-performance applications.

View File

@@ -0,0 +1,725 @@
# SAPUI5 Routing & Navigation
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials)
**Last Updated**: 2025-11-21
---
## Overview
SAPUI5 implements hash-based routing for single-page applications, enabling deep linking, browser history support, and seamless navigation without page reloads.
**Key Benefits**:
- Browser back/forward button support
- Bookmarkable URLs (deep linking)
- State preservation through URL parameters
- SEO-friendly with hash-based navigation
- Mobile back button handling
---
## Core Concepts
### Hash-Based Navigation
SAPUI5 uses URL hash to manage application state:
```
[https://myapp.com/index.html#/products/123?tab=details](https://myapp.com/index.html#/products/123?tab=details)
^^^^^^^^^^^^^^^^^^^^^^^^^
Hash
```
**Components**:
- **Pattern**: `/products/{productId}`
- **Parameters**: `{productId}` = `123`
- **Query**: `?tab=details`
### Routes
Routes match hash patterns and trigger handlers:
```javascript
{
"pattern": "products/{productId}",
"name": "productDetail",
"target": "productDetail"
}
```
**When matched**: Loads target view and passes parameters.
### Targets
Targets define what to display and where:
```javascript
{
"productDetail": {
"viewName": "ProductDetail",
"viewLevel": 2,
"viewId": "productDetail",
"controlId": "app",
"controlAggregation": "pages"
}
}
```
---
## Configuration
### manifest.json Routing Configuration
**Complete Example**:
```json
{
"sap.ui5": {
"routing": {
"config": {
"routerClass": "sap.m.routing.Router",
"type": "View",
"viewType": "XML",
"path": "my.app.view",
"controlId": "app",
"controlAggregation": "pages",
"transition": "slide",
"bypassed": {
"target": "notFound"
},
"async": true
},
"routes": [
{
"pattern": "",
"name": "home",
"target": "home"
},
{
"pattern": "products",
"name": "productList",
"target": "productList"
},
{
"pattern": "products/{productId}",
"name": "productDetail",
"target": ["productList", "productDetail"]
},
{
"pattern": "products/{productId}/items/{itemId}",
"name": "itemDetail",
"target": ["productList", "productDetail", "itemDetail"]
}
],
"targets": {
"home": {
"viewName": "Home",
"viewId": "home",
"viewLevel": 1
},
"productList": {
"viewName": "ProductList",
"viewId": "productList",
"viewLevel": 1
},
"productDetail": {
"viewName": "ProductDetail",
"viewId": "productDetail",
"viewLevel": 2
},
"itemDetail": {
"viewName": "ItemDetail",
"viewId": "itemDetail",
"viewLevel": 3
},
"notFound": {
"viewName": "NotFound",
"viewId": "notFound"
}
}
}
}
}
```
### Configuration Options
**Router Config**:
- `routerClass`: Router implementation (sap.m.routing.Router, sap.f.routing.Router)
- `type`: View type (View, Component)
- `viewType`: View format (XML, JSON, JS, HTML)
- `path`: View namespace prefix
- `controlId`: Container control ID
- `controlAggregation`: Aggregation to add views to
- `transition`: Animation (slide, show, flip, fade)
- `async`: Asynchronous view loading (always use true)
- `bypassed`: Target for unmatched routes
**Route Properties**:
- `pattern`: URL pattern to match
- `name`: Route name (for navigation)
- `target`: Single target or array of targets
- `subroutes`: Nested routes (deprecated, use multiple targets)
- `greedy`: Match even if longer patterns exist
**Target Properties**:
- `viewName`: View name (without path)
- `viewId`: View instance ID
- `viewLevel`: Hierarchy level (for transitions)
- `controlId`: Parent control ID
- `controlAggregation`: Parent aggregation
- `clearControlAggregation`: Clear before adding
---
## Initializing Router
### In Component.js
```javascript
sap.ui.define([
"sap/ui/core/UIComponent"
], function(UIComponent) {
"use strict";
return UIComponent.extend("my.app.Component", {
metadata: {
manifest: "json"
},
init: function() {
// Call parent init
UIComponent.prototype.init.apply(this, arguments);
// Initialize router
this.getRouter().initialize();
}
});
});
```
**Important**: Always call `getRouter().initialize()` in component's init method.
---
## Navigation
### Programmatic Navigation
**Navigate to Route**:
```javascript
// Simple navigation
this.getOwnerComponent().getRouter().navTo("productList");
// With parameters
this.getOwnerComponent().getRouter().navTo("productDetail", {
productId: "123"
});
// With query parameters
this.getOwnerComponent().getRouter().navTo("productDetail", {
productId: "123"
}, {
query: {
tab: "details",
mode: "edit"
}
});
// Replace history (no back button)
this.getOwnerComponent().getRouter().navTo("home", {}, {}, true);
```
**Navigate Back**:
```javascript
// Browser back
window.history.go(-1);
// Or use NavContainer
var oNavContainer = this.byId("app");
oNavContainer.back();
```
### Link in XML View
**sap.m.Link**:
```xml
<Link
text="View Product"
href="{
parts: ['productId'],
formatter: '.formatProductLink'
}"/>
```
**Controller**:
```javascript
formatProductLink: function(sProductId) {
var oRouter = this.getOwnerComponent().getRouter();
return oRouter.getURL("productDetail", {
productId: sProductId
});
}
```
---
## Route Matching
### Pattern Matched Event
**Attach in Controller**:
```javascript
sap.ui.define([
"sap/ui/core/mvc/Controller"
], function(Controller) {
"use strict";
return Controller.extend("my.app.controller.ProductDetail", {
onInit: function() {
var oRouter = this.getOwnerComponent().getRouter();
oRouter.getRoute("productDetail").attachPatternMatched(this._onPatternMatched, this);
},
_onPatternMatched: function(oEvent) {
var mArguments = oEvent.getParameter("arguments");
var sProductId = mArguments.productId;
// Load product data
this._loadProduct(sProductId);
// Handle query parameters
var mQuery = oEvent.getParameter("arguments")["?query"];
if (mQuery && mQuery.tab) {
this._selectTab(mQuery.tab);
}
},
_loadProduct: function(sProductId) {
var sPath = "/Products('" + sProductId + "')";
this.getView().bindElement({
path: sPath,
events: {
dataRequested: function() {
this.getView().setBusy(true);
}.bind(this),
dataReceived: function() {
this.getView().setBusy(false);
}.bind(this)
}
});
},
_selectTab: function(sTabKey) {
this.byId("iconTabBar").setSelectedKey(sTabKey);
}
});
});
```
### Route Matched Event
Match any route:
```javascript
oRouter.attachRouteMatched(function(oEvent) {
var sRouteName = oEvent.getParameter("name");
console.log("Route matched: " + sRouteName);
});
```
### Bypassed Event
No route matched:
```javascript
oRouter.attachBypassed(function(oEvent) {
var sHash = oEvent.getParameter("hash");
console.log("No route matched: " + sHash);
});
```
---
## Route Parameters
### Mandatory Parameters
```javascript
// Route pattern
"products/{productId}"
// Navigation
this.getRouter().navTo("productDetail", {
productId: "123" // Required
});
// Access in controller
var sProductId = mArguments.productId;
```
### Optional Parameters
```javascript
// Route pattern with :optional: prefix
"products/:productId:"
// Navigation (parameter optional)
this.getRouter().navTo("productList", {
// productId can be omitted
});
// Or with parameter
this.getRouter().navTo("productList", {
productId: "123"
});
```
### Query Parameters
```javascript
// Not in pattern, passed via options
this.getRouter().navTo("productDetail", {
productId: "123"
}, {
query: {
tab: "specifications",
mode: "edit",
highlight: "true"
}
});
// Access in controller
var mQuery = oEvent.getParameter("arguments")["?query"];
var sTab = mQuery.tab; // "specifications"
var sMode = mQuery.mode; // "edit"
var bHighlight = mQuery.highlight === "true";
```
### Rest Parameters
Capture remaining path:
```javascript
// Pattern with rest parameter
"files/{path*}"
// Matches
"/files/documents/reports/2025.pdf"
// Access
var sPath = mArguments.path; // "documents/reports/2025.pdf"
```
---
## Master-Detail Pattern
### Flexible Column Layout
**manifest.json**:
```json
{
"sap.ui5": {
"routing": {
"config": {
"routerClass": "sap.f.routing.Router",
"flexibleColumnLayout": {
"defaultTwoColumnLayoutType": "TwoColumnsMidExpanded",
"defaultThreeColumnLayoutType": "ThreeColumnsMidExpanded"
}
},
"routes": [
{
"pattern": "",
"name": "master",
"target": ["master"]
},
{
"pattern": "products/{productId}",
"name": "detail",
"target": ["master", "detail"]
},
{
"pattern": "products/{productId}/items/{itemId}",
"name": "detailDetail",
"target": ["master", "detail", "detailDetail"]
}
],
"targets": {
"master": {
"viewName": "Master",
"viewLevel": 1,
"controlAggregation": "beginColumnPages"
},
"detail": {
"viewName": "Detail",
"viewLevel": 2,
"controlAggregation": "midColumnPages"
},
"detailDetail": {
"viewName": "DetailDetail",
"viewLevel": 3,
"controlAggregation": "endColumnPages"
}
}
}
}
}
```
**Layout Management**:
```javascript
// Get FCL
var oFCL = this.byId("flexibleColumnLayout");
// Change layout
oFCL.setLayout("TwoColumnsMidExpanded");
// Available layouts
// - OneColumn
// - TwoColumnsBeginExpanded
// - TwoColumnsMidExpanded
// - ThreeColumnsMidExpanded
// - ThreeColumnsEndExpanded
// - ThreeColumnsMidExpandedEndHidden
// - ThreeColumnsBeginExpandedEndHidden
```
---
## Advanced Patterns
### Nested Routing
Use multiple targets instead of subroutes:
```javascript
// Bad (deprecated subroutes)
{
"pattern": "products",
"name": "products",
"subroutes": [...]
}
// Good (multiple targets)
{
"pattern": "products/{productId}",
"name": "productDetail",
"target": ["productList", "productDetail"]
}
```
### Greedy Routes
Match even when longer patterns exist:
```javascript
{
"pattern": "products/{productId}",
"name": "productDetail",
"greedy": true
}
```
### Title Propagation
Set page title based on route:
```javascript
// In controller
_onPatternMatched: function(oEvent) {
var sProductId = oEvent.getParameter("arguments").productId;
this.getView().bindElement({
path: "/Products('" + sProductId + "')",
events: {
dataReceived: function(oData) {
var sTitle = oData.getParameter("data").Name;
// Set page title
this.getOwnerComponent().getRouter().getTitleTarget("productDetail").setTitle(sTitle);
}.bind(this)
}
});
}
```
---
## Error Handling
### Not Found Page
**Configure in manifest.json**:
```json
{
"routing": {
"config": {
"bypassed": {
"target": "notFound"
}
},
"targets": {
"notFound": {
"viewName": "NotFound",
"viewId": "notFound"
}
}
}
}
```
**NotFound.view.xml**:
```xml
<mvc:View
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<MessagePage
title="{i18n>notFoundTitle}"
text="{i18n>notFoundText}"
description="{i18n>notFoundDescription}"
icon="sap-icon://documents"
showNavButton="true"
navButtonPress=".onNavBack"/>
</mvc:View>
```
### Object Not Found
Handle when object doesn't exist:
```javascript
_loadProduct: function(sProductId) {
this.getView().bindElement({
path: "/Products('" + sProductId + "')",
events: {
dataRequested: function() {
this.getView().setBusy(true);
}.bind(this),
dataReceived: function(oData) {
this.getView().setBusy(false);
// Check if data exists
if (!oData.getParameter("data")) {
this._showObjectNotFound();
}
}.bind(this)
}
});
},
_showObjectNotFound: function() {
this.getOwnerComponent().getRouter().getTargets().display("objectNotFound");
}
```
---
## Best Practices
### 1. Always Use Async
```json
{
"routing": {
"config": {
"async": true
}
}
}
```
### 2. Initialize in Component
```javascript
init: function() {
UIComponent.prototype.init.apply(this, arguments);
this.getRouter().initialize();
}
```
### 3. Use Pattern Matched
Don't use onBeforeRendering/onAfterRendering for navigation logic:
```javascript
// Good
onInit: function() {
this.getRouter().getRoute("detail").attachPatternMatched(this._onPatternMatched, this);
}
// Bad
onBeforeRendering: function() {
// Don't load data here
}
```
### 4. Clean Up Event Handlers
```javascript
onExit: function() {
this.getRouter().getRoute("detail").detachPatternMatched(this._onPatternMatched, this);
}
```
### 5. Use getURL for Links
```javascript
// Generate URL
var sURL = this.getRouter().getURL("productDetail", {
productId: "123"
});
// Use in href
oLink.setHref(sURL);
```
---
## Troubleshooting
### Issue: Router not initializing
**Check**:
1. Called `getRouter().initialize()` in Component?
2. manifest.json routing config correct?
3. Console errors?
### Issue: Route not matching
**Check**:
1. Pattern syntax correct?
2. Parameters match?
3. Route name used in navTo?
**Debug**:
```javascript
// Log all routes
var aRoutes = this.getRouter().getRoutes();
aRoutes.forEach(function(oRoute) {
console.log(oRoute.getPattern());
});
```
### Issue: View not displaying
**Check**:
1. Target viewName correct?
2. controlId exists?
3. controlAggregation correct?
**Debug**:
```javascript
// Attach route matched
this.getRouter().attachRouteMatched(function(oEvent) {
console.log("Route matched:", oEvent.getParameter("name"));
console.log("Arguments:", oEvent.getParameter("arguments"));
});
```
---
## Official Documentation
- **Routing and Navigation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: routing)
- **API Reference - Router**: [https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Router](https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Router)
- **API Reference - Route**: [https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Route](https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Route)
- **Flexible Column Layout**: [https://sapui5.hana.ondemand.com/#/api/sap.f.FlexibleColumnLayout](https://sapui5.hana.ondemand.com/#/api/sap.f.FlexibleColumnLayout)
---
**Note**: This document covers routing and navigation in SAPUI5. For Fiori Elements routing, see the fiori-elements.md reference file.

681
references/security.md Normal file
View File

@@ -0,0 +1,681 @@
# SAPUI5 Security Guide
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps)
**Last Updated**: 2025-11-21
---
## Overview
Security is critical for enterprise applications. SAPUI5 provides built-in security features, but developers must use them correctly.
**Security Principles**:
1. **Never trust user input**
2. **Use framework security features**
3. **Keep framework updated**
4. **Follow secure coding practices**
5. **Test for vulnerabilities**
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: security, secure-programming)
---
## Cross-Site Scripting (XSS) Prevention
### Overview
XSS occurs when attackers inject malicious scripts into web pages viewed by other users.
**SAPUI5 Protection**: Automatic output encoding in data binding and controls.
### Automatic Protection
**Data Binding** (Safe):
```xml
<!-- Automatically HTML-encoded -->
<Text text="{/userName}"/>
<Input value="{/userInput}"/>
```
**Control Properties** (Safe):
```javascript
// Automatically encoded
oText.setText(sUserInput);
oButton.setText(sUserInput);
```
### Dangerous Patterns
**innerHTML** (NEVER USE):
```javascript
// DANGEROUS - DO NOT USE
this.byId("myDiv").getDomRef().innerHTML = sUserInput;
// SAFE - Use data binding or setText
this.byId("myText").setText(sUserInput);
```
**jQuery HTML manipulation** (Avoid):
```javascript
// DANGEROUS
this.$("#myDiv").html(sUserInput);
// SAFE
this.byId("myText").setText(sUserInput);
```
### HTML Content
If HTML content is necessary, use `sap.ui.core.HTML` with sanitization:
```javascript
sap.ui.require([
"sap/ui/core/HTML",
"sap/base/security/sanitizeHTML"
], function(HTML, sanitizeHTML) {
// Sanitize HTML
var sSafeHTML = sanitizeHTML(sUnsafeHTML, {
uriRewriter: function(sUrl) {
// Whitelist URLs
if (sUrl.startsWith("[https://trusted.com/](https://trusted.com/)")) {
return sUrl;
}
return "";
}
});
// Create HTML control
var oHTML = new HTML({
content: sSafeHTML,
sanitizeContent: true
});
});
```
### URL Validation
**Validate URLs** before using:
```javascript
sap.ui.require([
"sap/base/security/URLWhitelist"
], function(URLWhitelist) {
// Add trusted domains
URLWhitelist.add("https", "trusted.com");
URLWhitelist.add("https", "*.mycompany.com");
// Validate URL
if (URLWhitelist.validate(sUrl)) {
// URL is safe
window.open(sUrl);
} else {
// URL not trusted
MessageBox.error("Invalid URL");
}
});
```
**Never use** `javascript:` URLs:
```javascript
// DANGEROUS
<Link href="javascript:alert('XSS')"/>
// SAFE
<Link press=".onLinkPress"/>
```
---
## Content Security Policy (CSP)
### Overview
CSP prevents XSS by controlling which resources can be loaded and executed.
**SAPUI5 Support**: Framework is CSP-compliant.
### Configuration
**Server-Side** (Recommended):
```
Content-Security-Policy: default-src 'self';
script-src 'self' [https://sapui5.hana.ondemand.com;](https://sapui5.hana.ondemand.com;)
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self' data:;
connect-src 'self' [https://api.mybackend.com;](https://api.mybackend.com;)
```
**Meta Tag** (Alternative):
```html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' [https://sapui5.hana.ondemand.com;](https://sapui5.hana.ondemand.com;)
style-src 'self' 'unsafe-inline';">
```
### SAPUI5 CSP Requirements
**Required Directives**:
- `script-src`: Allow SAPUI5 CDN or local resources
- `style-src 'unsafe-inline'`: Required for dynamic styles
- `font-src`: Allow icon fonts
- `img-src data:`: Allow data URIs for images
**Nonce Support** (Better than 'unsafe-inline'):
```html
<meta http-equiv="Content-Security-Policy"
content="style-src 'self' 'nonce-{{random}}'; script-src 'self' 'nonce-{{random}}'">
<script nonce="{{random}}" src="resources/sap-ui-core.js"></script>
```
### Testing CSP
1. Enable CSP in development
2. Check browser console for violations
3. Adjust policy as needed
4. Test all application features
**Common Violations**:
- Inline scripts (use external files)
- `eval()` calls (refactor code)
- Inline event handlers (use addEventListener)
---
## Clickjacking Prevention
### Overview
Clickjacking tricks users into clicking on hidden elements by overlaying malicious content on legitimate pages.
### Frame Options
**X-Frame-Options Header** (Server-Side):
```
X-Frame-Options: DENY
```
Or allow same origin:
```
X-Frame-Options: SAMEORIGIN
```
**CSP Frame Ancestors**:
```
Content-Security-Policy: frame-ancestors 'none';
```
Or specify allowed origins:
```
Content-Security-Policy: frame-ancestors 'self' [https://trusted.com;](https://trusted.com;)
```
### Framebuster Script
For legacy browsers:
```html
<script>
if (top !== self) {
top.location = self.location;
}
</script>
```
---
## Authentication & Authorization
### Authentication
**Never store credentials** in client-side code:
```javascript
// DANGEROUS - DO NOT DO THIS
var sPassword = "hardcoded123";
// SAFE - Use server-side authentication
fetch("/api/login", {
method: "POST",
credentials: "include",
body: JSON.stringify({ username, password })
});
```
**Use Secure Tokens**:
```javascript
// Store token securely (httpOnly cookie preferred)
// If using localStorage, encrypt sensitive data
```
### Authorization
**Check permissions** server-side:
```javascript
// Client-side (UI only, not security)
if (this.hasPermission("delete")) {
this.byId("deleteButton").setVisible(true);
}
// Server-side (actual security check)
DELETE /api/products/123
Authorization: Bearer <token>
```
**Never rely** on client-side checks for security:
```javascript
// This is UI convenience, NOT security
if (oUser.role === "admin") {
this.showAdminPanel();
}
// Real security happens on server
```
---
## Secure Data Transmission
### HTTPS
**Always use HTTPS** in production:
```javascript
// manifest.json
{
"sap.app": {
"dataSources": {
"mainService": {
"uri": "[https://api.mycompany.com/odata/",](https://api.mycompany.com/odata/",) // HTTPS
"type": "OData"
}
}
}
}
```
**Redirect HTTP to HTTPS** (Server-Side):
```
HTTP/1.1 301 Moved Permanently
Location: [https://myapp.com/](https://myapp.com/)
```
### CORS
**Configure CORS** properly on backend:
```
Access-Control-Allow-Origin: [https://myapp.com](https://myapp.com)
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
```
**Never use** `*` with credentials:
```
# DANGEROUS
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
```
---
## CSRF Protection
### Overview
Cross-Site Request Forgery tricks authenticated users into performing unwanted actions.
### CSRF Tokens
**OData V2**:
SAPUI5 automatically handles CSRF tokens for OData V2 models:
```javascript
// Automatic CSRF token handling
oModel.create("/Products", oData, {
success: function() {
// Token automatically included
}
});
```
**Custom AJAX**:
```javascript
fetch("/api/action", {
method: "POST",
headers: {
"X-CSRF-Token": await this.fetchCSRFToken()
},
credentials: "include",
body: JSON.stringify(data)
});
fetchCSRFToken: async function() {
const response = await fetch("/api/csrf-token", {
method: "HEAD",
credentials: "include"
});
return response.headers.get("X-CSRF-Token");
}
```
---
## Input Validation
### Client-Side Validation
**Validate format**, not security:
```javascript
onEmailChange: function(oEvent) {
var sEmail = oEvent.getParameter("value");
var oInput = oEvent.getSource();
// Basic format validation (UI convenience)
var bValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(sEmail);
oInput.setValueState(bValid ? "None" : "Error");
oInput.setValueStateText(bValid ? "" : "Invalid email format");
// Real validation happens on server
}
```
**Use Data Types**:
```xml
<Input
value="{
path: '/email',
type: 'sap.ui.model.type.String',
constraints: {
maxLength: 100,
search: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
}
}"/>
```
### Server-Side Validation
**Always validate** on server:
```javascript
// Client sends data
oModel.create("/Products", {
name: sName,
price: fPrice
}, {
success: function() {
// Server has validated input
},
error: function(oError) {
// Server rejected invalid input
MessageBox.error("Validation failed");
}
});
```
---
## Secure Storage
### Sensitive Data
**Never store** sensitive data in localStorage or sessionStorage:
```javascript
// DANGEROUS
localStorage.setItem("password", sPassword);
localStorage.setItem("apiKey", sApiKey);
// SAFE - Use secure httpOnly cookies (server-side)
// Or short-lived memory-only storage
```
### Session Management
**Use server-side sessions**:
```javascript
// Login
fetch("/api/login", {
method: "POST",
credentials: "include", // Send cookies
body: JSON.stringify({ username, password })
});
// Requests automatically include session cookie
fetch("/api/data", {
credentials: "include"
});
// Logout
fetch("/api/logout", {
method: "POST",
credentials: "include"
});
```
---
## SQL Injection Prevention
### OData Services
**Use parameterized queries** in backend:
```javascript
// Client-side (safe - OData handles escaping)
oModel.read("/Products", {
filters: [
new Filter("Name", FilterOperator.EQ, sUserInput)
]
});
// Backend must use parameterized queries
// DO NOT concatenate SQL strings
```
---
## Secure Configuration
### Manifest.json
**Don't expose** sensitive information:
```json
// DANGEROUS
{
"sap.app": {
"dataSources": {
"mainService": {
"uri": "[https://api.mycompany.com/odata/?apikey=secret123"](https://api.mycompany.com/odata/?apikey=secret123")
}
}
}
}
// SAFE
{
"sap.app": {
"dataSources": {
"mainService": {
"uri": "[https://api.mycompany.com/odata/"](https://api.mycompany.com/odata/")
// API key sent via server-side proxy
}
}
}
}
```
### Environment Variables
**Use environment-specific** configuration:
```javascript
// Build-time replacement
var API_URL = "#{API_URL}#"; // Replaced during build
// Or runtime configuration
fetch("/config.json").then(config => {
// Use config.apiUrl
});
```
---
## Security Testing
### Manual Testing
1. **XSS Testing**:
- Input `<script>alert('XSS')</script>`
- Input `<img src=x onerror=alert('XSS')>`
- Verify automatic encoding
2. **CSRF Testing**:
- Attempt cross-origin requests
- Verify token validation
3. **Authentication**:
- Test without credentials
- Test with expired tokens
- Test privilege escalation
### Automated Testing
**OWASP ZAP**:
```bash
# Scan application
zap-cli quick-scan -s xss,sqli [http://localhost:8080](http://localhost:8080)
```
**Retire.js** (Check for vulnerable libraries):
```bash
npm install -g retire
retire --js --path webapp/
```
---
## Security Checklist
### XSS Prevention
- [ ] Use data binding for user input
- [ ] Never use innerHTML
- [ ] Sanitize HTML content if needed
- [ ] Validate URLs before use
- [ ] No javascript: URLs
### CSP
- [ ] CSP header configured
- [ ] Application works with CSP enabled
- [ ] No CSP violations in console
- [ ] script-src and style-src configured
### Clickjacking
- [ ] X-Frame-Options header set
- [ ] frame-ancestors CSP directive set
- [ ] Framebuster script if needed
### Authentication
- [ ] No credentials in code
- [ ] Secure token storage
- [ ] HTTPS only in production
- [ ] Session timeout implemented
### Authorization
- [ ] Server-side permission checks
- [ ] Client-side checks for UI only
- [ ] Role-based access control
- [ ] Least privilege principle
### Data Transmission
- [ ] HTTPS everywhere
- [ ] CORS properly configured
- [ ] No sensitive data in URLs
- [ ] Secure cookie flags (HttpOnly, Secure)
### Input Validation
- [ ] Client-side format validation
- [ ] Server-side security validation
- [ ] Data types with constraints
- [ ] Whitelist, not blacklist
### Secure Storage
- [ ] No sensitive data in localStorage
- [ ] Session management server-side
- [ ] httpOnly cookies for tokens
---
## Common Vulnerabilities
### 1. Reflected XSS
**Vulnerable**:
```javascript
var sSearch = new URLSearchParams(window.location.search).get("q");
this.byId("searchField").getDomRef().innerHTML = sSearch;
```
**Fixed**:
```javascript
var sSearch = new URLSearchParams(window.location.search).get("q");
this.byId("searchField").setValue(sSearch);
```
### 2. DOM-based XSS
**Vulnerable**:
```javascript
var sUrl = location.hash.substring(1);
window.location = sUrl;
```
**Fixed**:
```javascript
sap.ui.require(["sap/base/security/URLWhitelist"], function(URLWhitelist) {
var sUrl = location.hash.substring(1);
if (URLWhitelist.validate(sUrl)) {
window.location = sUrl;
}
});
```
### 3. Insecure Direct Object Reference
**Vulnerable**:
```javascript
// User can modify ID in URL
var sId = oRouter.getParameter("id");
oModel.read("/Orders(" + sId + ")"); // Access to any order
```
**Fixed**:
```javascript
// Server checks if user has access to this order
var sId = oRouter.getParameter("id");
oModel.read("/Orders(" + sId + ")", {
// Server validates: user can only access their own orders
});
```
---
## Official Documentation
- **Security**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: security, secure-programming)
- **XSS Prevention**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: cross-site-scripting)
- **CSP**: [https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps](https://github.com/SAP-docs/sapui5/tree/main/docs/05_Developing_Apps) (search: content-security-policy)
- **URL Whitelist**: [https://sapui5.hana.ondemand.com/#/api/sap.base.security.URLWhitelist](https://sapui5.hana.ondemand.com/#/api/sap.base.security.URLWhitelist)
- **OWASP Top 10**: [https://owasp.org/www-project-top-ten/](https://owasp.org/www-project-top-ten/)
---
**Note**: This document covers security best practices for SAPUI5. Security is not optional - it must be built into every application from the start. Always follow the principle of defense in depth.

781
references/testing.md Normal file
View File

@@ -0,0 +1,781 @@
# SAPUI5 Testing Guide
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials)
**Last Updated**: 2025-11-21
---
## Testing Strategy
SAPUI5 applications should include:
1. **Unit Tests** (QUnit): Test individual functions and modules
2. **Integration Tests** (OPA5): Test user interactions and flows
3. **Mock Server**: Simulate backend for testing without real services
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: testing, qunit, opa)
---
## QUnit Testing
Unit testing framework for JavaScript code.
### Setup
**test/unit/unitTests.qunit.html**:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Unit Tests</title>
<script
id="sap-ui-bootstrap"
src="../../resources/sap-ui-core.js"
data-sap-ui-theme="sap_horizon"
data-sap-ui-resourceroots='{
"com.mycompany.myapp": "../../"
}'
data-sap-ui-async="true">
</script>
<link rel="stylesheet" type="text/css" href="../../resources/sap/ui/thirdparty/qunit-2.css">
<script src="../../resources/sap/ui/thirdparty/qunit-2.js"></script>
<script src="../../resources/sap/ui/qunit/qunit-junit.js"></script>
<script src="../../resources/sap/ui/qunit/qunit-coverage.js"></script>
<script src="unitTests.qunit.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
```
**test/unit/unitTests.qunit.js**:
```javascript
QUnit.config.autostart = false;
sap.ui.require([
"com/mycompany/myapp/test/unit/model/formatter"
], function() {
"use strict";
QUnit.start();
});
```
### Writing Unit Tests
**test/unit/model/formatter.js**:
```javascript
sap.ui.define([
"com/mycompany/myapp/model/formatter",
"sap/ui/model/resource/ResourceModel"
], function(formatter, ResourceModel) {
"use strict";
QUnit.module("Formatter Tests");
QUnit.test("Should format price correctly", function(assert) {
// Arrange
var fPrice = 123.456;
// Act
var sResult = formatter.formatPrice(fPrice);
// Assert
assert.strictEqual(sResult, "123.46 EUR", "Price formatted correctly");
});
QUnit.test("Should return empty string for null price", function(assert) {
// Arrange & Act
var sResult = formatter.formatPrice(null);
// Assert
assert.strictEqual(sResult, "", "Empty string for null");
});
QUnit.test("Should format status text", function(assert) {
// Arrange
var sStatus = "A";
// Act
var sResult = formatter.formatStatus(sStatus);
// Assert
assert.strictEqual(sResult, "Approved", "Status formatted correctly");
});
QUnit.test("Should calculate total", function(assert) {
// Arrange
var iQuantity = 5;
var fPrice = 10.5;
// Act
var fTotal = formatter.calculateTotal(iQuantity, fPrice);
// Assert
assert.strictEqual(fTotal, 52.5, "Total calculated correctly");
});
});
```
### Async Tests
```javascript
QUnit.test("Should load data asynchronously", function(assert) {
// Indicate async test
var done = assert.async();
// Arrange
var oModel = new JSONModel();
// Act
oModel.loadData("test/data/products.json");
// Assert after data loaded
oModel.attachRequestCompleted(function() {
var aProducts = oModel.getProperty("/products");
assert.ok(aProducts.length > 0, "Data loaded successfully");
done(); // Mark test complete
});
});
```
### Testing Controllers
```javascript
sap.ui.define([
"com/mycompany/myapp/controller/Main.controller",
"sap/ui/thirdparty/sinon",
"sap/ui/thirdparty/sinon-qunit"
], function(MainController) {
"use strict";
QUnit.module("Main Controller", {
beforeEach: function() {
this.oController = new MainController();
},
afterEach: function() {
this.oController.destroy();
}
});
QUnit.test("Should initialize correctly", function(assert) {
// Arrange - spy on method
var oSpy = sinon.spy(this.oController, "_loadData");
// Act
this.oController.onInit();
// Assert
assert.ok(oSpy.calledOnce, "Load data called on init");
});
});
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: qunit)
---
## OPA5 Testing
One Page Acceptance testing for integration tests.
### Setup
**test/integration/opaTests.qunit.html**:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Integration Tests</title>
<script
id="sap-ui-bootstrap"
src="../../resources/sap-ui-core.js"
data-sap-ui-theme="sap_horizon"
data-sap-ui-resourceroots='{
"com.mycompany.myapp": "../../"
}'
data-sap-ui-async="true">
</script>
<link rel="stylesheet" type="text/css" href="../../resources/sap/ui/thirdparty/qunit-2.css">
<script src="../../resources/sap/ui/thirdparty/qunit-2.js"></script>
<script src="../../resources/sap/ui/qunit/qunit-junit.js"></script>
<script src="opaTests.qunit.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
```
**test/integration/opaTests.qunit.js**:
```javascript
QUnit.config.autostart = false;
sap.ui.require([
"sap/ui/test/Opa5",
"com/mycompany/myapp/test/integration/arrangements/Startup",
"com/mycompany/myapp/test/integration/WorklistJourney"
], function(Opa5, Startup) {
"use strict";
Opa5.extendConfig({
arrangements: new Startup(),
viewNamespace: "com.mycompany.myapp.view.",
autoWait: true
});
QUnit.start();
});
```
### Page Objects
**test/integration/pages/Worklist.js**:
```javascript
sap.ui.define([
"sap/ui/test/Opa5",
"sap/ui/test/actions/Press",
"sap/ui/test/actions/EnterText",
"sap/ui/test/matchers/AggregationLengthEquals",
"sap/ui/test/matchers/PropertyStrictEquals"
], function(Opa5, Press, EnterText, AggregationLengthEquals, PropertyStrictEquals) {
"use strict";
var sViewName = "Worklist";
Opa5.createPageObjects({
onTheWorklistPage: {
actions: {
iPressOnTheFirstListItem: function() {
return this.waitFor({
id: "table",
viewName: sViewName,
actions: new Press(),
errorMessage: "The table is not visible"
});
},
iEnterSearchTerm: function(sSearchTerm) {
return this.waitFor({
id: "searchField",
viewName: sViewName,
actions: new EnterText({ text: sSearchTerm }),
errorMessage: "Search field not found"
});
},
iPressTheAddButton: function() {
return this.waitFor({
id: "addButton",
viewName: sViewName,
actions: new Press(),
errorMessage: "Add button not found"
});
}
},
assertions: {
theTableShouldHaveAllEntries: function() {
return this.waitFor({
id: "table",
viewName: sViewName,
matchers: new AggregationLengthEquals({
name: "items",
length: 10
}),
success: function() {
Opa5.assert.ok(true, "Table has 10 entries");
},
errorMessage: "Table does not have expected entries"
});
},
theTableShouldBeVisible: function() {
return this.waitFor({
id: "table",
viewName: sViewName,
success: function(oTable) {
Opa5.assert.ok(oTable.getVisible(), "Table is visible");
},
errorMessage: "Table is not visible"
});
},
iShouldSeeTheCorrectTitle: function() {
return this.waitFor({
id: "page",
viewName: sViewName,
matchers: new PropertyStrictEquals({
name: "title",
value: "Worklist"
}),
success: function() {
Opa5.assert.ok(true, "Page title is correct");
},
errorMessage: "Page title is incorrect"
});
}
}
}
});
});
```
### Journeys (Test Scenarios)
**test/integration/WorklistJourney.js**:
```javascript
sap.ui.define([
"sap/ui/test/opaQunit",
"./pages/Worklist",
"./pages/Object"
], function(opaTest) {
"use strict";
QUnit.module("Worklist Journey");
opaTest("Should see the table with all entries", function(Given, When, Then) {
// Arrangements
Given.iStartMyApp();
// Actions
// (none - just checking initial state)
// Assertions
Then.onTheWorklistPage.theTableShouldBeVisible();
Then.onTheWorklistPage.theTableShouldHaveAllEntries();
// Cleanup
Then.iTeardownMyApp();
});
opaTest("Should be able to search for items", function(Given, When, Then) {
// Arrangements
Given.iStartMyApp();
// Actions
When.onTheWorklistPage.iEnterSearchTerm("Product");
// Assertions
Then.onTheWorklistPage.theTableShouldHaveAllEntries();
// Cleanup
Then.iTeardownMyApp();
});
opaTest("Should navigate to object page when item is pressed", function(Given, When, Then) {
// Arrangements
Given.iStartMyApp();
// Actions
When.onTheWorklistPage.iPressOnTheFirstListItem();
// Assertions
Then.onTheObjectPage.iShouldSeeTheObjectPage();
// Cleanup
Then.iTeardownMyApp();
});
});
```
### Startup Arrangements
**test/integration/arrangements/Startup.js**:
```javascript
sap.ui.define([
"sap/ui/test/Opa5"
], function(Opa5) {
"use strict";
return Opa5.extend("com.mycompany.myapp.test.integration.arrangements.Startup", {
iStartMyApp: function() {
this.iStartMyUIComponent({
componentConfig: {
name: "com.mycompany.myapp",
async: true,
manifest: true
},
hash: "",
autoWait: true
});
}
});
});
```
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: opa5, integration-testing)
---
## Mock Server
Simulate OData backend for testing.
### Setup
**localService/metadata.xml**:
Copy metadata from real service or create manually.
**localService/mockdata/Products.json**:
```json
[
{
"ProductID": 1,
"Name": "Product 1",
"Price": "100.00",
"CategoryID": 1
},
{
"ProductID": 2,
"Name": "Product 2",
"Price": "200.00",
"CategoryID": 2
}
]
```
**localService/mockserver.js**:
```javascript
sap.ui.define([
"sap/ui/core/util/MockServer",
"sap/base/util/UriParameters"
], function(MockServer, UriParameters) {
"use strict";
return {
init: function() {
// Create mock server
var oMockServer = new MockServer({
rootUri: "/sap/opu/odata/sap/SERVICE_SRV/"
});
// Configure mock server
var sPath = sap.ui.require.toUrl("com/mycompany/myapp/localService");
MockServer.config({
autoRespond: true,
autoRespondAfter: 1000 // 1 second delay
});
// Simulate backend
oMockServer.simulate(sPath + "/metadata.xml", {
sMockdataBaseUrl: sPath + "/mockdata",
bGenerateMissingMockData: true
});
// Custom request handling
var aRequests = oMockServer.getRequests();
// Add custom response for specific request
aRequests.push({
method: "GET",
path: new RegExp("Products\\?.*"),
response: function(oXhr) {
oXhr.respondJSON(200, {}, JSON.stringify({
d: {
results: [
// Custom data
]
}
}));
return true;
}
});
oMockServer.setRequests(aRequests);
// Start mock server
oMockServer.start();
return oMockServer;
}
};
});
```
**test/initMockServer.js**:
```javascript
sap.ui.define([
"com/mycompany/myapp/localService/mockserver"
], function(mockserver) {
"use strict";
// Initialize mock server
mockserver.init();
// Initialize application
sap.ui.require([
"sap/ui/core/ComponentSupport"
]);
});
```
**Start with Mock Server**:
```html
<!-- In test HTML file -->
<script src="test/initMockServer.js"></script>
```
**Conditional Mock Server** (development only):
```javascript
sap.ui.define([
"sap/base/util/UriParameters"
], function(UriParameters) {
"use strict";
var oUriParameters = new UriParameters(window.location.href);
var bUseMockServer = oUriParameters.get("useMockServer") === "true";
if (bUseMockServer) {
sap.ui.require([
"com/mycompany/myapp/localService/mockserver"
], function(mockserver) {
mockserver.init();
initApp();
});
} else {
initApp();
}
function initApp() {
sap.ui.require(["sap/ui/core/ComponentSupport"]);
}
});
```
**Usage**: `index.html?useMockServer=true`
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: mock-server)
---
## Test Automation Best Practices
### 1. Test Structure (AAA Pattern)
```javascript
QUnit.test("Test description", function(assert) {
// Arrange - setup test data and conditions
var oModel = new JSONModel({ value: 10 });
// Act - execute the function being tested
var iResult = myFunction(oModel.getProperty("/value"));
// Assert - verify the result
assert.strictEqual(iResult, 20, "Result is correct");
});
```
### 2. Use Descriptive Test Names
```javascript
// Good
QUnit.test("Should format currency with two decimal places", function(assert) {});
QUnit.test("Should return empty string when price is null", function(assert) {});
// Bad
QUnit.test("Test 1", function(assert) {});
QUnit.test("Format test", function(assert) {});
```
### 3. Test One Thing Per Test
```javascript
// Good - separate tests
QUnit.test("Should add item to cart", function(assert) {});
QUnit.test("Should calculate correct total", function(assert) {});
// Bad - testing multiple things
QUnit.test("Should add item and calculate total", function(assert) {});
```
### 4. Use Spies and Stubs (Sinon.js)
```javascript
QUnit.test("Should call service method", function(assert) {
// Arrange
var oStub = sinon.stub(oService, "getData").returns([]);
// Act
oController.loadData();
// Assert
assert.ok(oStub.calledOnce, "Service called once");
// Cleanup
oStub.restore();
});
```
### 5. Clean Up After Tests
```javascript
QUnit.module("Test Module", {
beforeEach: function() {
// Setup before each test
this.oModel = new JSONModel();
},
afterEach: function() {
// Cleanup after each test
this.oModel.destroy();
}
});
```
### 6. Use autoWait in OPA5
```javascript
Opa5.extendConfig({
autoWait: true // Automatically waits for UI to be ready
});
```
### 7. Organize Tests by Feature
```
test/
├── unit/
│ ├── model/
│ │ └── formatter.js
│ ├── controller/
│ │ └── Main.controller.js
│ └── unitTests.qunit.html
├── integration/
│ ├── pages/
│ │ ├── Worklist.js
│ │ └── Object.js
│ ├── WorklistJourney.js
│ ├── NavigationJourney.js
│ └── opaTests.qunit.html
└── localService/
├── mockserver.js
├── metadata.xml
└── mockdata/
```
---
## Code Coverage
Enable code coverage in QUnit:
**test/unit/unitTests.qunit.html**:
```html
<script src="../../resources/sap/ui/qunit/qunit-coverage.js"></script>
```
**View Coverage Report**:
1. Run tests in browser
2. Click "Enable coverage" in QUnit toolbar
3. View coverage report after tests complete
**Coverage Goals**:
- Unit tests: 80%+ coverage
- Integration tests: Cover all critical user flows
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: code-coverage)
---
## Continuous Integration
### Karma Runner
**karma.conf.js**:
```javascript
module.exports = function(config) {
config.set({
frameworks: ["ui5"],
ui5: {
type: "application",
configPath: "ui5.yaml",
paths: {
webapp: "webapp"
}
},
browsers: ["ChromeHeadless"],
reporters: ["progress", "coverage"],
coverageReporter: {
type: "html",
dir: "coverage/"
},
singleRun: true
});
};
```
**package.json**:
```json
{
"scripts": {
"test": "karma start"
},
"devDependencies": {
"karma": "^6.0.0",
"karma-ui5": "^2.0.0",
"karma-chrome-launcher": "^3.0.0",
"karma-coverage": "^2.0.0"
}
}
```
**Run Tests**:
```bash
npm test
```
---
## Testing Tools
### UI5 Inspector
Browser extension for debugging SAPUI5 apps:
- View control tree
- Inspect control properties
- View bindings
- Check performance
**Installation**: Available for Chrome and Firefox
### Support Assistant
Built-in tool for quality checks:
```javascript
// Enable in code
sap.ui.require(["sap/ui/support/RuleAnalyzer"], function(RuleAnalyzer) {
RuleAnalyzer.analyze();
});
```
**Or use keyboard**: `Ctrl+Alt+Shift+S`
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: support-assistant)
---
## Links to Official Documentation
- **Testing Overview**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: testing)
- **QUnit**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: qunit)
- **OPA5**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: opa5)
- **Mock Server**: [https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials](https://github.com/SAP-docs/sapui5/tree/main/docs/04_Essentials) (search: mock-server)
- **Get Started Testing**: [https://github.com/SAP-docs/sapui5/tree/main/docs/03_Get-Started](https://github.com/SAP-docs/sapui5/tree/main/docs/03_Get-Started) (search: testing)
---
**Note**: This document covers testing strategies and best practices for SAPUI5 applications. For specific testing scenarios and advanced techniques, refer to the official documentation links provided.

View File

@@ -0,0 +1,622 @@
# TypeScript Support in SAPUI5
**Source**: Official SAP SAPUI5 Documentation
**Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First](https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First)
**Last Updated**: 2025-11-21
---
## Overview
TypeScript enhances JavaScript development by adding type information, enabling early error detection and improved code assistance. SAPUI5 provides dedicated type definitions for fully-typed application development.
**Benefits**:
- Early error detection during development
- Better code completion and IntelliSense
- Improved refactoring support
- Enhanced documentation through types
- Safer code through compile-time checks
**Production Status**: TypeScript support is production-ready and actively maintained by the SAPUI5 team.
---
## Type Definition Packages
### @sapui5/types (Recommended)
Officially maintained by the SAPUI5 development team:
```bash
npm install --save-dev @sapui5/types
```
**Features**:
- Official type definitions
- Regular updates with SAPUI5 releases
- Full framework coverage
- Maintained by SAP
**Configuration** (tsconfig.json):
```json
{
"compilerOptions": {
"module": "es2022",
"moduleResolution": "node",
"target": "es2022",
"lib": ["es2022", "dom"],
"types": ["@sapui5/types"],
"skipLibCheck": true,
"strict": true
}
}
```
### @types/openui5 (Community)
Community-maintained via DefinitelyTyped:
```bash
npm install --save-dev @types/openui5
```
**Use Case**: Alternative for OpenUI5 projects or when specific versions needed.
---
## Project Setup
### Basic TypeScript SAPUI5 Project
**package.json**:
```json
{
"name": "my-sapui5-ts-app",
"version": "1.0.0",
"scripts": {
"build": "tsc && ui5 build",
"start": "ui5 serve",
"watch": "tsc --watch"
},
"devDependencies": {
"@sapui5/types": "^1.120.0",
"@ui5/cli": "^3.0.0",
"typescript": "^5.0.0"
}
}
```
**tsconfig.json**:
```json
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "node",
"lib": ["es2022", "dom"],
"types": ["@sapui5/types"],
"outDir": "./webapp",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
**ui5.yaml**:
```yaml
specVersion: "3.0"
metadata:
name: my.sapui5.ts.app
type: application
framework:
name: SAPUI5
version: "1.120.0"
libraries:
- name: sap.m
- name: sap.ui.core
- name: sap.f
```
---
## TypeScript Component
**src/Component.ts**:
```typescript
import UIComponent from "sap/ui/core/UIComponent";
import models from "./model/models";
import Device from "sap/ui/Device";
import JSONModel from "sap/ui/model/json/JSONModel";
/**
* @namespace my.sapui5.ts.app
*/
export default class Component extends UIComponent {
public static metadata = {
manifest: "json"
};
private contentDensityClass: string;
public init(): void {
// Call parent init
super.init();
// Set device model
this.setModel(models.createDeviceModel(), "device");
// Create router
this.getRouter().initialize();
}
/**
* Get content density CSS class
*/
public getContentDensityClass(): string {
if (this.contentDensityClass === undefined) {
// Check if touch device
if (!Device.support.touch) {
this.contentDensityClass = "sapUiSizeCompact";
} else {
this.contentDensityClass = "sapUiSizeCozy";
}
}
return this.contentDensityClass;
}
}
```
---
## TypeScript Controller
**src/controller/Main.controller.ts**:
```typescript
import Controller from "sap/ui/core/mvc/Controller";
import MessageToast from "sap/m/MessageToast";
import MessageBox from "sap/m/MessageBox";
import JSONModel from "sap/ui/model/json/JSONModel";
import Filter from "sap/ui/model/Filter";
import FilterOperator from "sap/ui/model/FilterOperator";
import Event from "sap/ui/base/Event";
import ResourceBundle from "sap/base/i18n/ResourceBundle";
import ResourceModel from "sap/ui/model/resource/ResourceModel";
import Table from "sap/m/Table";
import SearchField from "sap/m/SearchField";
/**
* @namespace my.sapui5.ts.app.controller
*/
export default class Main extends Controller {
private viewModel: JSONModel;
/*eslint-disable @typescript-eslint/no-empty-function*/
public onInit(): void {
// Create view model
this.viewModel = new JSONModel({
busy: false,
selectedItemsCount: 0
});
this.getView()?.setModel(this.viewModel, "view");
}
/**
* Search handler
*/
public onSearch(event: Event): void {
const searchField = event.getSource() as SearchField;
const query = searchField.getValue();
const filters: Filter[] = [];
if (query && query.length > 0) {
filters.push(new Filter("Name", FilterOperator.Contains, query));
}
const table = this.byId("productsTable") as Table;
const binding = table.getBinding("items");
binding?.filter(filters);
}
/**
* Button press handler
*/
public onPress(): void {
const resourceBundle = this.getResourceBundle();
MessageToast.show(resourceBundle.getText("buttonPressed") as string);
}
/**
* Delete confirmation
*/
public onDelete(): void {
const resourceBundle = this.getResourceBundle();
MessageBox.confirm(
resourceBundle.getText("deleteConfirm") as string,
{
onClose: (action: string) => {
if (action === MessageBox.Action.OK) {
this.performDelete();
}
}
}
);
}
/**
* Get resource bundle
*/
private getResourceBundle(): ResourceBundle {
const model = this.getView()?.getModel("i18n") as ResourceModel;
return model.getResourceBundle() as ResourceBundle;
}
/**
* Perform delete operation
*/
private performDelete(): void {
// Delete logic here
MessageToast.show("Item deleted");
}
}
```
---
## TypeScript Model/Formatter
**src/model/formatter.ts**:
```typescript
import DateFormat from "sap/ui/core/format/DateFormat";
import NumberFormat from "sap/ui/core/format/NumberFormat";
export default {
/**
* Format date to localized string
*/
formatDate(date: Date | string | null): string {
if (!date) {
return "";
}
const dateFormat = DateFormat.getDateInstance({
pattern: "dd.MM.yyyy"
});
return dateFormat.format(new Date(date));
},
/**
* Format currency
*/
formatCurrency(amount: number | null, currency: string): string {
if (amount === null || amount === undefined) {
return "";
}
const currencyFormat = NumberFormat.getCurrencyInstance();
return currencyFormat.format(amount, currency);
},
/**
* Format status
*/
formatStatus(status: string): string {
const statusMap: Record<string, string> = {
"A": "Approved",
"R": "Rejected",
"P": "Pending"
};
return statusMap[status] || status;
},
/**
* Format status state
*/
formatStatusState(status: string): string {
const stateMap: Record<string, string> = {
"A": "Success",
"R": "Error",
"P": "Warning"
};
return stateMap[status] || "None";
}
};
```
---
## Custom Control in TypeScript
**src/control/CustomButton.ts**:
```typescript
import Button from "sap/m/Button";
import RenderManager from "sap/ui/core/RenderManager";
/**
* @namespace my.sapui5.ts.app.control
*/
export default class CustomButton extends Button {
public static metadata = {
properties: {
customProperty: {
type: "string",
defaultValue: ""
}
},
events: {
customPress: {
parameters: {
value: { type: "string" }
}
}
}
};
// Property getter/setter
public getCustomProperty(): string {
return this.getProperty("customProperty") as string;
}
public setCustomProperty(value: string): this {
this.setProperty("customProperty", value);
return this;
}
// Custom renderer
public static renderer = {
...Button.renderer,
render: function(rm: RenderManager, control: CustomButton): void {
// Custom rendering logic
Button.renderer.render(rm, control);
}
};
/**
* Custom method
*/
public customMethod(): void {
this.fireEvent("customPress", {
value: this.getCustomProperty()
});
}
}
```
---
## Type Definitions for Custom Types
**src/types/index.d.ts**:
```typescript
declare namespace my.sapui5.ts.app {
// Custom types
interface Product {
id: string;
name: string;
price: number;
currency: string;
category: string;
status: "A" | "R" | "P";
}
interface AppConfiguration {
apiUrl: string;
timeout: number;
debug: boolean;
}
type StatusType = "A" | "R" | "P";
type ViewMode = "display" | "edit" | "create";
}
```
---
## Best Practices
### 1. Use Strict Mode
Enable strict TypeScript checking:
```json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
```
### 2. Type Assertions
Use type assertions when type cannot be inferred:
```typescript
const table = this.byId("productsTable") as Table;
const model = this.getView()?.getModel() as JSONModel;
```
### 3. Optional Chaining
Use optional chaining for nullable values:
```typescript
const view = this.getView();
view?.setModel(model);
```
### 4. Generics with Models
Type your model data:
```typescript
interface Product {
id: string;
name: string;
price: number;
}
const model = new JSONModel<Product[]>([]);
const data = model.getData(); // Type: Product[]
```
### 5. Event Typing
Type event handlers properly:
```typescript
import Event from "sap/ui/base/Event";
import Input from "sap/m/Input";
public onChange(event: Event): void {
const input = event.getSource() as Input;
const value = input.getValue();
}
```
---
## Common Issues
### Issue: Cannot find module 'sap/...'
**Solution**: Install type definitions:
```bash
npm install --save-dev @sapui5/types
```
Add to tsconfig.json:
```json
{
"compilerOptions": {
"types": ["@sapui5/types"]
}
}
```
### Issue: Type errors with metadata
**Solution**: Use static metadata property:
```typescript
export default class MyController extends Controller {
public static metadata = {
// metadata here
};
}
```
### Issue: 'this' implicitly has type 'any'
**Solution**: Use arrow functions or bind 'this':
```typescript
// Arrow function
.then((data) => {
this.processData(data);
});
// Bind
.then(function(data) {
this.processData(data);
}.bind(this));
```
---
## Compatibility
**TypeScript Version**: 4.0+ (5.0+ recommended)
**SAPUI5 Version**: 1.60+ (1.120+ recommended)
**Node.js Version**: 16+ (18+ recommended)
---
## Migration from JavaScript
### Step 1: Rename Files
Rename `.js` files to `.ts`:
```bash
mv Component.js Component.ts
mv controller/Main.controller.js controller/Main.controller.ts
```
### Step 2: Add Type Annotations
Add types gradually:
```typescript
// Before (JS)
onPress: function(oEvent) {
var sValue = oEvent.getSource().getValue();
}
// After (TS)
public onPress(event: Event): void {
const source = event.getSource() as Input;
const value: string = source.getValue();
}
```
### Step 3: Fix Type Errors
Address compilation errors one by one:
- Add missing imports
- Type function parameters
- Use type assertions where needed
- Enable strict mode gradually
---
## Testing with TypeScript
**QUnit Test** (test/unit/model/formatter.ts):
```typescript
import formatter from "my/sapui5/ts/app/model/formatter";
QUnit.module("Formatter Tests");
QUnit.test("Should format date correctly", (assert) => {
const date = new Date(2025, 0, 21);
const result = formatter.formatDate(date);
assert.ok(result.includes("21"), "Date formatted correctly");
});
QUnit.test("Should format currency", (assert) => {
const result = formatter.formatCurrency(100, "EUR");
assert.ok(result.includes("100"), "Currency formatted");
});
```
---
## Official Resources
- **TypeScript Documentation**: [https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First](https://github.com/SAP-docs/sapui5/tree/main/docs/02_Read-Me-First) (search: typescript)
- **UI5 TypeScript Tutorial**: [https://github.com/SAP-samples/ui5-typescript-tutorial](https://github.com/SAP-samples/ui5-typescript-tutorial)
- **TypeScript Guidelines**: Official SAPUI5 TypeScript guidelines
- **Sample Applications**: TypeScript To-Do List demo
---
---
## Advanced TypeScript Topics
For advanced TypeScript patterns including:
- **Metadata-Driven Controls (MDC)** with TypeScript
- **Control library development** in TypeScript
- Using `@ui5/ts-interface-generator` for control APIs
- Enum registration and library.ts patterns
See `references/mdc-typescript-advanced.md` for detailed implementation guides.
---
**Note**: This document covers TypeScript support in SAPUI5. The framework actively maintains type definitions, making TypeScript a viable choice for new projects and migrations.