Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export default defineConfig({
{
text: "EML, CDS, SQL",
link: "/cookbook/eml_cds_sql/rap",
collapsed: true,
items: [
{ text: "RAP", link: "/cookbook/eml_cds_sql/rap" },
{ text: "EML", link: "/cookbook/eml_cds_sql/eml" },
Expand All @@ -248,14 +249,34 @@ export default defineConfig({
],
},
{
text: "Utilities",
text: "Patterns, Helpers",
collapsed: true,
items: [
{ text: "Snippets", link: "/cookbook/expert_more/snippets" },
{ text: "Value Help", link: "/cookbook/expert_more/value_help" },
{ text: "Demo Output", link: "/cookbook/expert_more/demo_output" },
{ text: "E-Mail", link: "/cookbook/expert_more/email" },
{ text: "Fuzzy Search", link: "/cookbook/eml_cds_sql/fuzzy_search" },
],
},
{
text: "Experimental",
items: [
{ text: "Drag & Drop", link: "/advanced/experimental/drag_drop" },
{
text: "Smart Control",
link: "/advanced/experimental/smart_control",
},
],
},
{
text: "Obsolete",
items: [
{ text: "Custom Controls", link: "/cookbook/expert_more/custom_controls" },
{ text: "Custom JS", link: "/cookbook/expert_more/custom_js" },
{ text: "follow_up_action", link: "/cookbook/expert_more/follow_up_action" },
],
},
],
},
],
Expand Down Expand Up @@ -310,25 +331,13 @@ export default defineConfig({
text: "User Exit",
link: "/advanced/extensibility/user_exits",
},
{ text: "Custom JavaScript", link: "/advanced/extensibility/custom_js" },
{ text: "Frontend", link: "/advanced/extensibility/frontend" },
{
text: "Custom Control",
link: "/advanced/extensibility/custom_control",
},
],
},
{
text: "Experimental",
collapsed: true,
items: [
{ text: "Drag & Drop", link: "/advanced/experimental/drag_drop" },
{
text: "Smart Control",
link: "/advanced/experimental/smart_control",
},
],
},
],
},
{
Expand Down
32 changes: 0 additions & 32 deletions docs/advanced/extensibility/custom_js.md

This file was deleted.

43 changes: 43 additions & 0 deletions docs/cookbook/eml_cds_sql/eml.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,46 @@ ENDMETHOD.
Key Points:
- EML calls in abap2UI5 apps run outside the RAP framework, so explicit transaction commits (COMMIT ENTITIES) are needed.
- Restrictions inside the RAP framework, like disallowing direct calls to posting function modules or explicit commits, don't apply to abap2UI5 EML operations. You get more flexibility when handling commits and other actions.

### Failure Handling

EML statements (`MODIFY ENTITIES`, `READ ENTITIES`, `COMMIT ENTITIES`) report problems through the `FAILED` and `REPORTED` structures rather than by raising exceptions. They can also raise classic ABAP exceptions for infrastructure-level failures.

What to handle:
- **Business / validation failures** — inspect `FAILED` (which entities failed) and `REPORTED` (the messages explaining why) after each EML call. These are *not* exceptions; an unchecked `FAILED` will look like success in your code while the data was never written.
- **Transactional behavior** — EML modifications stay in the transactional buffer until `COMMIT ENTITIES`. If you skip the commit, nothing is persisted. If `COMMIT ENTITIES` itself reports failures, you must decide whether to retry, roll back, or surface the error to the user.
- **Infrastructure exceptions** — wrap EML calls in `TRY … CATCH` for the cases that *do* raise:
- `cx_root` / `cx_dynamic_check` — catch-all safety net.
- `cx_abap_invalid_value`, `cx_sy_conversion_no_number` — data conversion problems before the EML statement runs.
- `cx_abap_behv` and its subclasses — RAP behavior framework errors (e.g. unknown action, locking issues).
- `cx_abap_lock_failure` — when `COMMIT ENTITIES` cannot acquire locks.

A typical defensive pattern:

```abap
TRY.
MODIFY ENTITIES OF z_i_invoice
ENTITY invoice
UPDATE FIELDS ( amount ) WITH VALUE #( ( %tky = ls_key amount = lv_amount ) )
FAILED DATA(failed)
REPORTED DATA(reported).

IF failed IS NOT INITIAL.
" surface reported messages, do NOT commit
RAISE EXCEPTION NEW cx_abap_behv( ).
ENDIF.

COMMIT ENTITIES RESPONSE OF z_i_invoice
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).

IF commit_failed IS NOT INITIAL.
RAISE EXCEPTION NEW cx_abap_behv( ).
ENDIF.

CATCH cx_root INTO DATA(lx).
client->nav_app_call( z2ui5_cl_pop_error=>factory( lx ) ).
ENDTRY.
```

For general exception handling and the framework's error popup, see the [Exception](/cookbook/event_navigation/exception) page.
29 changes: 29 additions & 0 deletions docs/cookbook/event_navigation/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,32 @@ METHOD z2ui5_if_app~main.

ENDMETHOD.
```

## Common Failure Modes

Not every problem raises an ABAP exception. Many failures surface only in the browser, or fail silently. The sections below describe what to look for in three frequent cases.

#### Binding-Path Mismatch

When a `_bind` / `_bind_edit` path does not resolve against the JSON model on the frontend — typically because the public attribute was renamed, the data was never sent, or the path is mistyped — UI5 does **not** raise an ABAP exception. The control simply renders empty or with a default value.

Where to look:
- **Browser console.** UI5 logs a warning like `Binding "/path/to/field" was not found in model` from `sap.ui.model.json.JSONModel`. Open the browser DevTools console and filter by `sap.ui.model`.
- **Network tab.** Inspect the abap2UI5 response payload — the JSON model is included verbatim. If the field is absent or named differently than your binding path, you have your answer.
- **ABAP side.** Nothing. The backend never learns the binding failed. Asserting "no console warnings" is the only way to catch this in tests.

Two-way binding (`_bind_edit`) silently drops the write-back when the path is invalid — your ABAP attribute keeps its old value after the next event. Cross-check the attribute value in the debugger if data is mysteriously not updating.

#### Malformed XML

`Z2UI5_CL_XML_VIEW` produces XML; UI5 parses it on the frontend. A typo in a control name, an unclosed tag, or an aggregation that contains an invalid child can break parsing entirely.

Where the error surfaces depends on what went wrong:
- **Pure XML syntax errors** (unclosed tag, bad escape) — the XML parser fails and UI5 logs a `Parse error` in the browser console. The page renders blank or up to the broken element.
- **Unknown UI5 controls / namespaces** — UI5 logs `failed to load 'sap.m.NotAControl'` (or similar) in the console; the surrounding view may render partially.
- **Wrong aggregation / wrong child type** — see the warning on the [View Definition](/cookbook/view/definition) page. UI5 may log an `aggregation … does not contain` warning or silently drop the child. Layouts can render in unexpected ways without any error.
- **ABAP side** — none of these surface as ABAP exceptions. `view_display( )` accepts any string. The response goes out, and only the browser notices.

When something looks wrong on screen, **always check the browser console first** before re-reading the ABAP code.

For EML-specific failure handling (`FAILED` / `REPORTED`, transactional behavior, `cx_abap_behv`, `cx_abap_lock_failure`, defensive `TRY/CATCH` patterns), see the [EML](/cookbook/eml_cds_sql/eml) page.
55 changes: 55 additions & 0 deletions docs/cookbook/expert_more/custom_controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
outline: [2, 4]
---
# Custom Controls (Obsolete)

::: warning Mostly Not Needed Anymore
Earlier versions of abap2UI5 required custom UI5 controls to cover common browser interactions — setting the page title, moving focus, scrolling, copying to the clipboard, opening new tabs, and so on. These are now built into the framework as **frontend events**, callable from ABAP via `client->action( val = client->cs_event-... )`. The custom controls that used to wrap these behaviors are obsolete.

You can still build your own [Custom Controls](/advanced/extensibility/custom_control) when you need something the framework does not provide, but for everything listed below, use the dedicated built-in action instead.
:::

## Replaced Custom Controls

The table below lists custom controls that are no longer necessary and the built-in action that replaces each one.

| Old Custom Control Purpose | New Built-in Action | Documentation |
| -------------------------- | ---------------------------- | -------------------------------------------------------------------------- |
| Set the browser tab title | `cs_event-set_title` | [Title](/cookbook/browser_interaction/title) |
| Move input focus | `cs_event-set_focus` | [Focus](/cookbook/browser_interaction/focus) |
| Scroll to a position | `cs_event-scroll_to` | [Scrolling](/cookbook/browser_interaction/scrolling) |
| Scroll an element into view| `cs_event-scroll_into_view` | [Scrolling](/cookbook/browser_interaction/scrolling) |
| Copy to clipboard | `cs_event-clipboard_copy` | [Clipboard](/cookbook/browser_interaction/clipboard) |
| Open a URL / new tab | `cs_event-open_new_tab` | [URL Handling](/cookbook/browser_interaction/url_handling) |
| Start a timer | `cs_event-start_timer` | [Timer](/cookbook/browser_interaction/timer) |
| Toggle the soft keyboard | `cs_event-keyboard_set_mode` | [Soft Keyboard](/cookbook/device_capabilities/soft_keyboard) |
| Trigger a file download | `cs_event-download_b` | [Upload, Download](/cookbook/device_capabilities/upload_download) |
| Play an audio file | `cs_event-play_audio` | [Barcode Scanning](/cookbook/device_capabilities/barcode_scanning) |

## Why Prefer the Built-in Actions

- **No frontend code to maintain.** The action runs framework-side JavaScript that ships with abap2UI5 — you do not write or deploy your own JS.
- **Stays in pure ABAP.** Calling `client->action( )` keeps your app logic in one place and one language.
- **Reviewed and tested.** The built-in actions are part of the framework, covered by its tests, and updated alongside it.
- **Safer.** Custom controls usually involve embedding JavaScript via [Custom JS](/cookbook/expert_more/custom_js), which carries the security risks documented on that page.

## Typical Pattern

Instead of writing a custom control, call the matching action after an event:

```abap
client->action( val = client->cs_event-set_title
t_arg = VALUE #( ( `Invoice 4711` ) ) ).
```

See the linked cookbook pages above for the full argument list of each action.

## When Custom Controls Still Make Sense

Building a real [Custom Control](/advanced/extensibility/custom_control) is still appropriate when:

- You need a UI element that UI5 does not ship and no abap2UI5 action covers.
- You are integrating a third-party UI5 library.
- You need behavior beyond simple frontend method calls — e.g. a stateful widget with its own rendering.

In all other cases, prefer the built-in action.
85 changes: 85 additions & 0 deletions docs/cookbook/expert_more/custom_js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
outline: [2, 4]
---
# Custom JS

::: warning Not Recommended
This functionality is still available, but its use is **strongly discouraged**. Injecting arbitrary JavaScript from the backend into the frontend introduces serious security risks. Only use it if you fully understand the consequences and have no alternative.
:::

## Why It Is a Security Risk

Custom JS works by sending a JavaScript string from the ABAP backend to the frontend, where it is injected into the DOM as an HTML `<script>` tag and executed in the user's browser. This pattern is essentially a **self-inflicted Cross-Site Scripting (XSS) vector** and breaks several security assumptions UI5 normally protects you from:

- **Bypasses output encoding.** UI5 escapes model data by default to prevent XSS. Raw `<script>` injection sidesteps that protection entirely.
- **Executes with full user privileges.** The injected code runs in the same origin as your app and can read cookies, session tokens, the UI5 model, and any data the user has access to — and send it anywhere.
- **Dynamic content is dangerous.** If any part of the injected JavaScript is built from user input, database values, translations, or other non-static sources, an attacker who controls that source can execute arbitrary code in every user's browser.
- **Breaks Content Security Policy (CSP).** A strict CSP — one of the most effective defenses against XSS — typically forbids inline scripts. Custom JS forces you to weaken or disable CSP, removing that protection for the whole app.
- **Hard to audit.** JavaScript assembled in ABAP strings is not covered by frontend linters, static analysis, or code review tools that normally catch dangerous patterns.
- **No sandboxing.** The script has the same DOM and network access as the rest of the app. There is no isolation boundary.

## Safer Alternatives

Before reaching for Custom JS, consider:

- Use the **standard UI5 controls and APIs** — most browser interactions are already covered.
- Build a proper **[Custom Control](/advanced/extensibility/custom_control)** with a defined interface and reviewable frontend code.
- Use the dedicated cookbook pages for [Clipboard](/cookbook/browser_interaction/clipboard), [Focus](/cookbook/browser_interaction/focus), [Scrolling](/cookbook/browser_interaction/scrolling), [Timer](/cookbook/browser_interaction/timer), [URL Handling](/cookbook/browser_interaction/url_handling), and similar.

## How It Works (If You Still Need It)

If you accept the risks and decide to use it anyway, the idea is: send the JavaScript function with the view to the frontend, then call it later when an event fires.

The `_generic` method creates a custom XML/HTML element — here an HTML `<script>` tag (namespace `html`). The `_cc_plain_xml` method inserts raw content into that element — in this case, the JavaScript function definition. On the backend, `client->follow_up_action` then runs the function by name on the frontend:

```abap
METHOD z2ui5_if_app~main.

IF client->check_on_init( ).
DATA(view) = z2ui5_cl_xml_view=>factory( ).
view->_generic( name = `script` ns = `html`
)->_cc_plain_xml(
|function myFunction() \{ console.log( `Hello World` ); \}|
).
view->page(
)->button( text = `call custom JS`
press = client->_event( `CUSTOM_JS` ) ).
client->view_display( view->stringify( ) ).
ENDIF.

IF client->get( )-event = `CUSTOM_JS`.
client->follow_up_action( `myFunction()` ).
ENDIF.

ENDMETHOD.
```

::: danger Never Inject Untrusted Input
If you must use this, ensure the JavaScript content is **entirely static and hardcoded**. Never concatenate user input, database values, translatable texts, or any other dynamic data into the script string — doing so turns the feature into a direct XSS vulnerability.
:::

## Embedding JavaScript Directly in an XML View

::: warning Also Not Recommended
The same security considerations apply: any `<script>` element embedded in an XML view runs with full app privileges and bypasses UI5's output encoding. Prefer a [Custom Control](/advanced/extensibility/custom_control) or one of the built-in actions instead.
:::

If you want to look at — or hand-craft — the raw XML view that abap2UI5 produces, a `<script>` tag is placed in the `html` namespace alongside the regular UI5 controls. The view stringified by `z2ui5_cl_xml_view=>factory( )` ends up looking like this:

```xml
<mvc:View
xmlns:mvc="sap.ui.core.mvc"
xmlns="sap.m"
xmlns:html="http://www.w3.org/1999/xhtml">
<html:script>
function myFunction() { console.log("Hello World"); }
</html:script>
<Page>
<Button text="call custom JS" press="..." />
</Page>
</mvc:View>
```

The browser parses the `html:script` element and executes its content as JavaScript at view render time. The function becomes available globally and can then be triggered from the backend via the obsolete [`follow_up_action`](/cookbook/expert_more/follow_up_action) — or, ideally, replaced entirely with a built-in `client->action( )` call.

This is exactly what the `_generic` + `_cc_plain_xml` helpers shown above produce; the two approaches are equivalent. Both ship raw JavaScript from the backend to the browser, and both carry the security risks described above. Use neither unless there is genuinely no alternative.
Loading