@linkup-ai/abap-ai 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +384 -0
  2. package/dist/adt-client.js +364 -0
  3. package/dist/cli/activate.js +113 -0
  4. package/dist/cli/init.js +333 -0
  5. package/dist/cli/remove.js +80 -0
  6. package/dist/cli/status.js +229 -0
  7. package/dist/cli/systems.js +68 -0
  8. package/dist/cli.js +81 -0
  9. package/dist/index.js +1318 -0
  10. package/dist/knowledge/abap/abap-dictionary.md +199 -0
  11. package/dist/knowledge/abap/abap-sql.md +296 -0
  12. package/dist/knowledge/abap/amdp.md +273 -0
  13. package/dist/knowledge/abap/clean-code.md +293 -0
  14. package/dist/knowledge/abap/cloud-background-processing.md +250 -0
  15. package/dist/knowledge/abap/cloud-communication.md +265 -0
  16. package/dist/knowledge/abap/cloud-development.md +176 -0
  17. package/dist/knowledge/abap/cloud-extensibility.md +252 -0
  18. package/dist/knowledge/abap/cloud-released-apis.md +261 -0
  19. package/dist/knowledge/abap/constructor-expressions.md +289 -0
  20. package/dist/knowledge/abap/enhancements.md +232 -0
  21. package/dist/knowledge/abap/exceptions.md +271 -0
  22. package/dist/knowledge/abap/internal-tables.md +205 -0
  23. package/dist/knowledge/abap/object-orientation.md +298 -0
  24. package/dist/knowledge/abap/performance.md +216 -0
  25. package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
  26. package/dist/knowledge/abap/rap-business-events.md +216 -0
  27. package/dist/knowledge/abap/rap-draft.md +191 -0
  28. package/dist/knowledge/abap/rap-eml.md +453 -0
  29. package/dist/knowledge/abap/rap-end-to-end.md +486 -0
  30. package/dist/knowledge/abap/rap-feature-control.md +185 -0
  31. package/dist/knowledge/abap/rap-numbering.md +280 -0
  32. package/dist/knowledge/abap/rap-service-exposure.md +163 -0
  33. package/dist/knowledge/abap/rap-unmanaged.md +468 -0
  34. package/dist/knowledge/abap/string-processing.md +180 -0
  35. package/dist/knowledge/abap/unit-testing.md +303 -0
  36. package/dist/knowledge/abap-cds/access-control.md +241 -0
  37. package/dist/knowledge/abap-cds/annotations.md +331 -0
  38. package/dist/knowledge/abap-cds/associations.md +254 -0
  39. package/dist/knowledge/abap-cds/expressions.md +230 -0
  40. package/dist/knowledge/abap-cds/functions.md +245 -0
  41. package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
  42. package/dist/knowledge/cap/authentication.md +278 -0
  43. package/dist/knowledge/cap/cdl-syntax.md +247 -0
  44. package/dist/knowledge/cap/cql-queries.md +266 -0
  45. package/dist/knowledge/cap/deployment.md +343 -0
  46. package/dist/knowledge/cap/event-handlers.md +287 -0
  47. package/dist/knowledge/cap/fiori-integration.md +303 -0
  48. package/dist/knowledge/cap/service-definitions.md +287 -0
  49. package/dist/knowledge/fiori/annotations.md +347 -0
  50. package/dist/knowledge/fiori/deployment.md +340 -0
  51. package/dist/knowledge/fiori/fiori-elements.md +332 -0
  52. package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
  53. package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
  54. package/dist/knowledge/fiori/ui5-controllers.md +358 -0
  55. package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
  56. package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
  57. package/dist/knowledge/fiori/ui5-manifest.md +411 -0
  58. package/dist/knowledge/fiori/ui5-routing.md +303 -0
  59. package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
  60. package/dist/logger.js +114 -0
  61. package/dist/system-profile.js +207 -0
  62. package/dist/tools/abap-doc.js +72 -0
  63. package/dist/tools/abapgit.js +161 -0
  64. package/dist/tools/activate.js +68 -0
  65. package/dist/tools/atc-check.js +117 -0
  66. package/dist/tools/auth-object.js +56 -0
  67. package/dist/tools/breakpoints.js +76 -0
  68. package/dist/tools/call-hierarchy.js +84 -0
  69. package/dist/tools/cds-annotations.js +98 -0
  70. package/dist/tools/cds-dependencies.js +65 -0
  71. package/dist/tools/check.js +47 -0
  72. package/dist/tools/code-completion.js +70 -0
  73. package/dist/tools/code-coverage.js +111 -0
  74. package/dist/tools/create-amdp.js +111 -0
  75. package/dist/tools/create-dcl.js +81 -0
  76. package/dist/tools/create-transport.js +38 -0
  77. package/dist/tools/create.js +285 -0
  78. package/dist/tools/data-preview.js +37 -0
  79. package/dist/tools/delete.js +45 -0
  80. package/dist/tools/deploy-bsp.js +298 -0
  81. package/dist/tools/discovery.js +59 -0
  82. package/dist/tools/element-info.js +93 -0
  83. package/dist/tools/enhancements.js +186 -0
  84. package/dist/tools/extract-method.js +44 -0
  85. package/dist/tools/function-group.js +59 -0
  86. package/dist/tools/knowledge.js +275 -0
  87. package/dist/tools/lock-object.js +75 -0
  88. package/dist/tools/message-class.js +67 -0
  89. package/dist/tools/navigate.js +80 -0
  90. package/dist/tools/number-range.js +57 -0
  91. package/dist/tools/object-documentation.js +43 -0
  92. package/dist/tools/object-structure.js +78 -0
  93. package/dist/tools/object-versions.js +57 -0
  94. package/dist/tools/package-contents.js +60 -0
  95. package/dist/tools/pretty-printer.js +35 -0
  96. package/dist/tools/publish-binding.js +49 -0
  97. package/dist/tools/quick-fix.js +69 -0
  98. package/dist/tools/read.js +167 -0
  99. package/dist/tools/refactor-rename.js +60 -0
  100. package/dist/tools/release-transport.js +24 -0
  101. package/dist/tools/released-apis.js +51 -0
  102. package/dist/tools/repository-tree.js +90 -0
  103. package/dist/tools/scaffold-rap.js +642 -0
  104. package/dist/tools/search.js +73 -0
  105. package/dist/tools/shared/data-format.js +101 -0
  106. package/dist/tools/sql-console.js +17 -0
  107. package/dist/tools/system-info.js +270 -0
  108. package/dist/tools/traces.js +66 -0
  109. package/dist/tools/transport-contents.js +83 -0
  110. package/dist/tools/transports.js +67 -0
  111. package/dist/tools/unit-test.js +135 -0
  112. package/dist/tools/where-used.js +59 -0
  113. package/dist/tools/write.js +101 -0
  114. package/package.json +49 -0
@@ -0,0 +1,358 @@
1
+ # UI5 Controllers — lifecycle, events, model access, navigation
2
+
3
+ ## Controller Definition
4
+
5
+ ```js
6
+ sap.ui.define([
7
+ "sap/ui/core/mvc/Controller",
8
+ "sap/ui/model/json/JSONModel",
9
+ "sap/m/MessageBox",
10
+ "sap/m/MessageToast"
11
+ ], function(Controller, JSONModel, MessageBox, MessageToast) {
12
+ "use strict";
13
+
14
+ return Controller.extend("com.myapp.controller.Main", {
15
+
16
+ onInit: function() {
17
+ // Called once when view is instantiated
18
+ var oViewModel = new JSONModel({ busy: false, editMode: false });
19
+ this.getView().setModel(oViewModel, "view");
20
+ },
21
+
22
+ onBeforeRendering: function() {
23
+ // Called before every rendering (initial + re-render)
24
+ },
25
+
26
+ onAfterRendering: function() {
27
+ // Called after every rendering — DOM is available
28
+ },
29
+
30
+ onExit: function() {
31
+ // Called when view is destroyed — cleanup
32
+ }
33
+ });
34
+ });
35
+ ```
36
+
37
+ ## TypeScript Controller
38
+
39
+ ```ts
40
+ import Controller from "sap/ui/core/mvc/Controller";
41
+ import JSONModel from "sap/ui/model/json/JSONModel";
42
+ import MessageBox from "sap/m/MessageBox";
43
+ import MessageToast from "sap/m/MessageToast";
44
+ import Event from "sap/ui/base/Event";
45
+
46
+ /**
47
+ * @namespace com.myapp.controller
48
+ */
49
+ export default class Main extends Controller {
50
+ public onInit(): void {
51
+ const oViewModel = new JSONModel({ busy: false, editMode: false });
52
+ this.getView()?.setModel(oViewModel, "view");
53
+ }
54
+
55
+ public onItemPress(oEvent: Event): void {
56
+ const oItem = oEvent.getSource();
57
+ const sPath = oItem.getBindingContext().getPath();
58
+ // navigate...
59
+ }
60
+ }
61
+ ```
62
+
63
+ ## Accessing Controls
64
+
65
+ ```js
66
+ // By ID (from XML view)
67
+ var oTable = this.byId("orderTable");
68
+ var oInput = this.byId("nameInput");
69
+
70
+ // Get view
71
+ var oView = this.getView();
72
+
73
+ // Get control from event
74
+ onButtonPress: function(oEvent) {
75
+ var oButton = oEvent.getSource();
76
+ var oItem = oEvent.getParameter("listItem"); // for List/Table events
77
+ }
78
+ ```
79
+
80
+ ## Model Access
81
+
82
+ ```js
83
+ // Get named model
84
+ var oModel = this.getView().getModel(); // default model
85
+ var oModel = this.getView().getModel("view"); // named model
86
+
87
+ // Get OData model (from component)
88
+ var oModel = this.getOwnerComponent().getModel();
89
+
90
+ // Read property
91
+ var sName = oModel.getProperty("/Name");
92
+ var sValue = oModel.getProperty(sPath + "/Amount");
93
+
94
+ // Set property
95
+ oModel.setProperty("/editMode", true);
96
+ oModel.setProperty(sPath + "/Status", "A");
97
+
98
+ // Get data from binding context
99
+ onItemPress: function(oEvent) {
100
+ var oContext = oEvent.getSource().getBindingContext();
101
+ var sOrderID = oContext.getProperty("OrderID");
102
+ var oData = oContext.getObject(); // entire record
103
+ }
104
+ ```
105
+
106
+ ## OData V2 — CRUD Operations
107
+
108
+ ```js
109
+ var oModel = this.getView().getModel(); // sap.ui.model.odata.v2.ODataModel
110
+
111
+ // READ
112
+ oModel.read("/Orders('1000')", {
113
+ success: function(oData) { /* oData = record */ },
114
+ error: function(oError) { /* handle */ }
115
+ });
116
+
117
+ // READ with expand
118
+ oModel.read("/Orders('1000')", {
119
+ urlParameters: { "$expand": "to_Items" },
120
+ success: function(oData) { /* oData.to_Items = items array */ }
121
+ });
122
+
123
+ // CREATE
124
+ oModel.create("/Orders", oPayload, {
125
+ success: function(oData) {
126
+ MessageToast.show("Created: " + oData.OrderID);
127
+ },
128
+ error: function(oError) {
129
+ MessageBox.error("Creation failed");
130
+ }
131
+ });
132
+
133
+ // UPDATE
134
+ oModel.update("/Orders('1000')", oPayload, {
135
+ success: function() { MessageToast.show("Updated"); },
136
+ error: function(oError) { /* handle */ }
137
+ });
138
+
139
+ // DELETE
140
+ oModel.remove("/Orders('1000')", {
141
+ success: function() { MessageToast.show("Deleted"); },
142
+ error: function(oError) { /* handle */ }
143
+ });
144
+
145
+ // Batch — group multiple operations
146
+ oModel.setDeferredGroups(["batchGroup"]);
147
+ oModel.create("/Orders", oPayload1, { groupId: "batchGroup" });
148
+ oModel.create("/Orders", oPayload2, { groupId: "batchGroup" });
149
+ oModel.submitChanges({
150
+ groupId: "batchGroup",
151
+ success: function() { /* all succeeded */ },
152
+ error: function() { /* batch failed */ }
153
+ });
154
+
155
+ // Refresh
156
+ oModel.refresh(true); // force reload from server
157
+ oTable.getBinding("items").refresh(); // refresh specific binding
158
+ ```
159
+
160
+ ## OData V4 — CRUD Operations
161
+
162
+ ```js
163
+ var oModel = this.getView().getModel(); // sap.ui.model.odata.v4.ODataModel
164
+
165
+ // CREATE via list binding
166
+ var oList = this.byId("orderTable").getBinding("items");
167
+ var oContext = oList.create({
168
+ CustomerID: "CUST100",
169
+ Amount: 500
170
+ });
171
+ // oContext is immediately available (optimistic)
172
+
173
+ // Save pending changes
174
+ oModel.submitBatch(oModel.getUpdateGroupId());
175
+
176
+ // DELETE via context
177
+ var oContext = oEvent.getSource().getBindingContext();
178
+ oContext.delete().then(function() {
179
+ MessageToast.show("Deleted");
180
+ });
181
+
182
+ // UPDATE — modify bound property, then submitBatch
183
+ oModel.setProperty(sPath + "/Status", "A");
184
+ oModel.submitBatch(oModel.getUpdateGroupId());
185
+
186
+ // Refresh
187
+ oList.refresh();
188
+ oContext.refresh();
189
+ ```
190
+
191
+ ## Event Handling
192
+
193
+ ```js
194
+ // Button press
195
+ onSave: function() {
196
+ var oModel = this.getView().getModel();
197
+ if (oModel.hasPendingChanges()) {
198
+ oModel.submitChanges({
199
+ success: function() { MessageToast.show("Saved"); }
200
+ });
201
+ }
202
+ },
203
+
204
+ // List item press
205
+ onItemPress: function(oEvent) {
206
+ var oItem = oEvent.getSource();
207
+ var oContext = oItem.getBindingContext();
208
+ var sID = oContext.getProperty("OrderID");
209
+ this.getRouter().navTo("detail", { orderID: sID });
210
+ },
211
+
212
+ // Search
213
+ onSearch: function(oEvent) {
214
+ var sQuery = oEvent.getParameter("query");
215
+ var oFilter = sQuery
216
+ ? new sap.ui.model.Filter("Name", "Contains", sQuery)
217
+ : [];
218
+ this.byId("orderTable").getBinding("items").filter(oFilter);
219
+ },
220
+
221
+ // Input change
222
+ onNameChange: function(oEvent) {
223
+ var sValue = oEvent.getParameter("value");
224
+ // validate, update model, etc.
225
+ },
226
+
227
+ // Select change
228
+ onStatusChange: function(oEvent) {
229
+ var sKey = oEvent.getParameter("selectedItem").getKey();
230
+ }
231
+ ```
232
+
233
+ ## Formatters
234
+
235
+ ```js
236
+ // In controller or separate formatter.js
237
+ formatStatusState: function(sStatus) {
238
+ switch (sStatus) {
239
+ case "A": return "Success";
240
+ case "X": return "Error";
241
+ case "O": return "Warning";
242
+ default: return "None";
243
+ }
244
+ },
245
+
246
+ formatAmount: function(fAmount, sCurrency) {
247
+ if (!fAmount) return "";
248
+ return parseFloat(fAmount).toFixed(2) + " " + sCurrency;
249
+ },
250
+
251
+ formatDate: function(sDate) {
252
+ if (!sDate) return "";
253
+ var oFormat = sap.ui.core.format.DateFormat.getDateInstance({ style: "medium" });
254
+ return oFormat.format(new Date(sDate));
255
+ }
256
+ ```
257
+
258
+ ```xml
259
+ <!-- Using in XML view -->
260
+ <ObjectStatus text="{StatusText}"
261
+ state="{path: 'Status', formatter: '.formatStatusState'}"/>
262
+ <Text text="{parts: [{path: 'Amount'}, {path: 'Currency'}],
263
+ formatter: '.formatAmount'}"/>
264
+ ```
265
+
266
+ ## Router Access
267
+
268
+ ```js
269
+ // Get router from component
270
+ getRouter: function() {
271
+ return this.getOwnerComponent().getRouter();
272
+ },
273
+
274
+ // Navigate
275
+ onNavToDetail: function(sID) {
276
+ this.getRouter().navTo("detail", { orderID: sID });
277
+ },
278
+
279
+ // Navigate back
280
+ onNavBack: function() {
281
+ var oHistory = sap.ui.core.routing.History.getInstance();
282
+ var sPreviousHash = oHistory.getPreviousHash();
283
+ if (sPreviousHash !== undefined) {
284
+ window.history.go(-1);
285
+ } else {
286
+ this.getRouter().navTo("list", {}, true);
287
+ }
288
+ },
289
+
290
+ // Attach route matched (in onInit)
291
+ onInit: function() {
292
+ this.getRouter().getRoute("detail").attachPatternMatched(this._onRouteMatched, this);
293
+ },
294
+
295
+ _onRouteMatched: function(oEvent) {
296
+ var sOrderID = oEvent.getParameter("arguments").orderID;
297
+ // V2: bind element
298
+ this.getView().bindElement({
299
+ path: "/Orders('" + sOrderID + "')",
300
+ parameters: { expand: "to_Items" },
301
+ events: {
302
+ dataRequested: function() { /* show busy */ },
303
+ dataReceived: function() { /* hide busy */ }
304
+ }
305
+ });
306
+ // V4: bind element
307
+ this.getView().bindElement("/Orders('" + sOrderID + "')");
308
+ }
309
+ ```
310
+
311
+ ## i18n Access
312
+
313
+ ```js
314
+ // Get resource bundle
315
+ var oBundle = this.getView().getModel("i18n").getResourceBundle();
316
+ var sText = oBundle.getText("orderCreated", [sOrderID]); // with placeholder
317
+
318
+ // In XML view
319
+ // text="{i18n>orderCreated}"
320
+ ```
321
+
322
+ ## Busy Handling
323
+
324
+ ```js
325
+ onInit: function() {
326
+ this.getView().setModel(new JSONModel({ busy: false }), "view");
327
+ },
328
+
329
+ setBusy: function(bBusy) {
330
+ this.getView().getModel("view").setProperty("/busy", bBusy);
331
+ }
332
+ ```
333
+
334
+ ```xml
335
+ <Page busyIndicatorDelay="0" busy="{view>/busy}">
336
+ ```
337
+
338
+ ## Rules
339
+ - `onInit` for one-time setup (models, route attachment); never for DOM manipulation
340
+ - `onAfterRendering` for DOM access — but prefer data binding over DOM manipulation
341
+ - Always access controls via `this.byId()`, never `sap.ui.getCore().byId()`
342
+ - Get OData model from `getOwnerComponent().getModel()` (not view) for shared model
343
+ - Use `getBindingContext().getProperty()` to read data from events (not hardcoded paths)
344
+ - Separate formatters into `model/formatter.js` when reused across views
345
+ - Always handle `error` callback in OData operations
346
+ - V4: use `oContext.delete()` / `oList.create()` — not `oModel.remove/create`
347
+
348
+ ## Anti-Patterns
349
+ | Anti-Pattern | Correct |
350
+ |---|---|
351
+ | `sap.ui.getCore().byId("id")` | `this.byId("id")` |
352
+ | DOM manipulation in `onInit` | Use data binding or `onAfterRendering` |
353
+ | Hardcoded paths in controller | Use binding context: `oContext.getProperty("field")` |
354
+ | Missing error handler in OData calls | Always handle `error` callback |
355
+ | Global variables in controller | Use view model (`JSONModel`) for state |
356
+ | `window.location` for navigation | Use `this.getRouter().navTo()` |
357
+ | Sync operations (`oModel.read` without callbacks) | Always use async pattern with success/error |
358
+ | Formatter with side effects | Formatters must be pure functions (no model changes) |
@@ -0,0 +1,311 @@
1
+ # UI5 Data Binding — OData, JSON, property, aggregation, expression
2
+
3
+ ## Binding Modes
4
+
5
+ | Mode | Direction | Use |
6
+ |------|-----------|-----|
7
+ | `OneWay` | Model → View | Display data (default for OData) |
8
+ | `TwoWay` | Model ↔ View | Editable forms (default for JSON) |
9
+ | `OneTime` | Model → View (once) | Static data, config |
10
+
11
+ ```js
12
+ // Set default binding mode
13
+ oModel.setDefaultBindingMode(sap.ui.model.BindingMode.TwoWay);
14
+ ```
15
+
16
+ ## Model Types
17
+
18
+ ### JSON Model (client-side)
19
+
20
+ ```js
21
+ var oModel = new sap.ui.model.json.JSONModel({
22
+ orders: [
23
+ { id: "001", customer: "ACME", amount: 500 },
24
+ { id: "002", customer: "Beta", amount: 300 }
25
+ ],
26
+ selectedTab: "all",
27
+ editMode: false
28
+ });
29
+ this.getView().setModel(oModel, "local");
30
+ ```
31
+
32
+ ```xml
33
+ <Input value="{local>/selectedTab}"/>
34
+ <List items="{local>/orders}">
35
+ <StandardListItem title="{local>customer}" description="{local>amount}"/>
36
+ </List>
37
+ ```
38
+
39
+ ### OData V2 Model
40
+
41
+ ```json
42
+ // manifest.json
43
+ {
44
+ "sap.ui5": {
45
+ "models": {
46
+ "": {
47
+ "dataSource": "mainService",
48
+ "preload": true,
49
+ "settings": {
50
+ "defaultBindingMode": "TwoWay",
51
+ "defaultCountMode": "Inline",
52
+ "refreshAfterChange": false,
53
+ "useBatch": true
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### OData V4 Model
62
+
63
+ ```json
64
+ {
65
+ "sap.ui5": {
66
+ "models": {
67
+ "": {
68
+ "dataSource": "mainService",
69
+ "preload": true,
70
+ "settings": {
71
+ "synchronizationMode": "None",
72
+ "operationMode": "Server",
73
+ "autoExpandSelect": true,
74
+ "groupId": "$auto"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ ## Property Binding
83
+
84
+ ```xml
85
+ <!-- Simple -->
86
+ <Text text="{Name}"/>
87
+ <Input value="{Amount}"/>
88
+
89
+ <!-- Named model -->
90
+ <Text text="{view>/editMode}"/>
91
+ <Text text="{i18n>orderTitle}"/>
92
+
93
+ <!-- Absolute path -->
94
+ <Text text="{/Orders('001')/Name}"/>
95
+
96
+ <!-- With type (formatting + validation) -->
97
+ <Input value="{
98
+ path: 'Amount',
99
+ type: 'sap.ui.model.type.Float',
100
+ formatOptions: { minFractionDigits: 2, maxFractionDigits: 2 },
101
+ constraints: { minimum: 0 }
102
+ }"/>
103
+
104
+ <DatePicker value="{
105
+ path: 'OrderDate',
106
+ type: 'sap.ui.model.type.Date',
107
+ formatOptions: { style: 'medium', source: { pattern: 'yyyyMMdd' } }
108
+ }"/>
109
+
110
+ <Input value="{
111
+ path: 'Currency',
112
+ type: 'sap.ui.model.type.Currency',
113
+ formatOptions: { showMeasure: false }
114
+ }" description="{Currency}"/>
115
+ ```
116
+
117
+ ## Aggregation Binding (Lists/Tables)
118
+
119
+ ```xml
120
+ <!-- Template binding -->
121
+ <List items="{/Orders}">
122
+ <StandardListItem title="{OrderID}" description="{CustomerName}"/>
123
+ </List>
124
+
125
+ <!-- With sorter -->
126
+ <List items="{
127
+ path: '/Orders',
128
+ sorter: { path: 'OrderDate', descending: true }
129
+ }">
130
+ <StandardListItem title="{OrderID}"/>
131
+ </List>
132
+
133
+ <!-- With filter -->
134
+ <List items="{
135
+ path: '/Orders',
136
+ filters: [{ path: 'Status', operator: 'EQ', value1: 'O' }]
137
+ }">
138
+ <StandardListItem title="{OrderID}"/>
139
+ </List>
140
+
141
+ <!-- With parameters (OData) -->
142
+ <Table items="{
143
+ path: '/Orders',
144
+ parameters: {
145
+ expand: 'to_Items',
146
+ select: 'OrderID,CustomerName,Amount,Status'
147
+ }
148
+ }">
149
+ ```
150
+
151
+ ## Element Binding (Object/Detail Pages)
152
+
153
+ ```js
154
+ // V2: Bind view to single entity
155
+ this.getView().bindElement({
156
+ path: "/Orders('1000')",
157
+ parameters: { expand: "to_Items" },
158
+ events: {
159
+ change: this._onBindingChange.bind(this),
160
+ dataRequested: function() { oView.setBusy(true); },
161
+ dataReceived: function() { oView.setBusy(false); }
162
+ }
163
+ });
164
+
165
+ // V4: simpler
166
+ this.getView().bindElement("/Orders('1000')");
167
+
168
+ // After binding, all controls in the view resolve relative paths
169
+ // <Text text="{OrderID}"/> → resolves to /Orders('1000')/OrderID
170
+ ```
171
+
172
+ ## Expression Binding
173
+
174
+ ```xml
175
+ <!-- Ternary -->
176
+ <Text text="{= ${Amount} > 1000 ? 'High Value' : 'Standard'}"/>
177
+
178
+ <!-- Boolean -->
179
+ <Input editable="{= ${Status} === 'O'}"/>
180
+ <Button visible="{= ${Status} !== 'A'}"/>
181
+ <Panel visible="{= ${Amount} > 0}"/>
182
+
183
+ <!-- Arithmetic -->
184
+ <Text text="{= ${Quantity} * ${UnitPrice}}"/>
185
+
186
+ <!-- Negation -->
187
+ <Input editable="{= !${view>/readOnly}}"/>
188
+
189
+ <!-- String comparison -->
190
+ <ObjectStatus state="{= ${Status} === 'A' ? 'Success' : ${Status} === 'X' ? 'Error' : 'Warning'}"/>
191
+
192
+ <!-- With formatter -->
193
+ <Text text="{= ${Amount}.toFixed(2) + ' ' + ${Currency}}"/>
194
+ ```
195
+
196
+ ## Composite Binding (multiple parts)
197
+
198
+ ```xml
199
+ <!-- parts array -->
200
+ <Text text="{
201
+ parts: [
202
+ { path: 'FirstName' },
203
+ { path: 'LastName' }
204
+ ],
205
+ formatter: '.formatFullName'
206
+ }"/>
207
+
208
+ <!-- Simple concatenation with expression -->
209
+ <Text text="{= ${FirstName} + ' ' + ${LastName}}"/>
210
+ ```
211
+
212
+ ## Filters (in Controller)
213
+
214
+ ```js
215
+ var aFilters = [];
216
+
217
+ // Simple filter
218
+ aFilters.push(new sap.ui.model.Filter("Status", "EQ", "O"));
219
+
220
+ // Contains
221
+ aFilters.push(new sap.ui.model.Filter("Name", "Contains", sQuery));
222
+
223
+ // Between
224
+ aFilters.push(new sap.ui.model.Filter("Amount", "BT", 100, 1000));
225
+
226
+ // AND (multiple filters)
227
+ var oFilter = new sap.ui.model.Filter({
228
+ filters: [
229
+ new sap.ui.model.Filter("Status", "EQ", "O"),
230
+ new sap.ui.model.Filter("Amount", "GT", 100)
231
+ ],
232
+ and: true
233
+ });
234
+
235
+ // OR
236
+ var oFilter = new sap.ui.model.Filter({
237
+ filters: [
238
+ new sap.ui.model.Filter("Status", "EQ", "O"),
239
+ new sap.ui.model.Filter("Status", "EQ", "P")
240
+ ],
241
+ and: false
242
+ });
243
+
244
+ // Apply to binding
245
+ this.byId("orderTable").getBinding("items").filter(aFilters);
246
+
247
+ // Clear filters
248
+ this.byId("orderTable").getBinding("items").filter([]);
249
+ ```
250
+
251
+ ## Filter Operators
252
+
253
+ | Operator | Description | Example |
254
+ |----------|-------------|---------|
255
+ | `EQ` | Equal | `"EQ", "A"` |
256
+ | `NE` | Not equal | `"NE", "D"` |
257
+ | `GT` | Greater than | `"GT", 100` |
258
+ | `GE` | Greater or equal | `"GE", 100` |
259
+ | `LT` | Less than | `"LT", 1000` |
260
+ | `LE` | Less or equal | `"LE", 1000` |
261
+ | `BT` | Between | `"BT", 100, 1000` |
262
+ | `Contains` | Contains string | `"Contains", "SAP"` |
263
+ | `StartsWith` | Starts with | `"StartsWith", "Z"` |
264
+ | `EndsWith` | Ends with | `"EndsWith", "GmbH"` |
265
+
266
+ ## Sorters (in Controller)
267
+
268
+ ```js
269
+ var aSorters = [
270
+ new sap.ui.model.Sorter("OrderDate", true), // descending
271
+ new sap.ui.model.Sorter("Amount", false) // ascending
272
+ ];
273
+ this.byId("orderTable").getBinding("items").sort(aSorters);
274
+
275
+ // Grouped sorting
276
+ var oSorter = new sap.ui.model.Sorter("Category", false, function(oContext) {
277
+ var sCategory = oContext.getProperty("Category");
278
+ return { key: sCategory, text: "Category: " + sCategory };
279
+ });
280
+ ```
281
+
282
+ ## Common Data Types
283
+
284
+ | Type | Use | Import |
285
+ |------|-----|--------|
286
+ | `sap.ui.model.type.String` | Text with constraints | `constraints: { maxLength: 40 }` |
287
+ | `sap.ui.model.type.Integer` | Whole numbers | `constraints: { minimum: 0 }` |
288
+ | `sap.ui.model.type.Float` | Decimals | `formatOptions: { minFractionDigits: 2 }` |
289
+ | `sap.ui.model.type.Date` | Date formatting | `formatOptions: { style: 'medium' }` |
290
+ | `sap.ui.model.type.DateTime` | Date + time | `formatOptions: { style: 'medium' }` |
291
+ | `sap.ui.model.type.Currency` | Amount + currency | `formatOptions: { showMeasure: true }` |
292
+
293
+ ## Rules
294
+ - Default model (no name) accessed via `{PropertyName}`; named model via `{modelName>PropertyName}`
295
+ - JSON model: TwoWay by default; OData: OneWay by default (set TwoWay explicitly for forms)
296
+ - Use `type` in binding for automatic formatting + validation (dates, numbers, currency)
297
+ - Expression binding `{= ...}` for simple logic; formatter for complex logic
298
+ - V2: `expand` via parameters in binding; V4: `autoExpandSelect: true` handles it
299
+ - Always handle `change` event on element binding to detect missing records (404)
300
+ - `bindElement` for detail pages; aggregation binding for lists/tables
301
+
302
+ ## Anti-Patterns
303
+ | Anti-Pattern | Correct |
304
+ |---|---|
305
+ | Manual date formatting in JS | Use `type: 'sap.ui.model.type.Date'` in binding |
306
+ | `oModel.getData()` on OData model | Use `oModel.getProperty(path)` or binding context |
307
+ | Hardcoded paths: `"/Orders('1000')"` | Build paths from route parameters |
308
+ | Missing `defaultCountMode: "Inline"` in V2 | Results in extra $count requests |
309
+ | Expression binding with model writes | Expressions are read-only — use controller for writes |
310
+ | `oModel.refresh(true)` after every change | Use `refreshAfterChange: false` + selective refresh |
311
+ | Filter without clearing previous | Always pass full filter array (or empty to clear) |