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
2 changes: 1 addition & 1 deletion docs/.vitepress/theme/SiteMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const { isDark } = useData();
* four entries under it were already in this menu, so what was left of that
* dropdown was the NUMBER, and this is where it went. Moving it means moving
* the pattern in release.mjs with it. */
const VERSION = "1.144.0";
const VERSION = "1.144.1";

const extra = ref(null);

Expand Down
12 changes: 6 additions & 6 deletions docs/cookbook/browser_interaction/soft_keyboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ keyboard just comes back.
*is*. That is also why the mode survives a roundtrip without the app restoring
anything.

::: warning cs_event-keyboard_set_mode is removed
Before 2026-09 this page taught a frontend action,
`client->follow_up_action( val = client->cs_event-keyboard_set_mode … )`, which
set the attribute directly on the DOM. It carried exactly the defect above and
**was removed from the framework**; an app that still calls it fails at compile
time. The migration is the control on this page: declare
::: warning The keyboard-mode frontend action is removed
Before 2026-09 this page taught a frontend action that set the attribute
directly on the DOM. It carried exactly the defect above and **was removed from
the framework in 1.144.1** (see [Deprecations](/resources/deprecations)); an
app that still calls it fails at compile time. The migration is the control on
this page: declare
`xmlns:z2ui5="z2ui5.cc"`, build the field as `z2ui5:InputExt`, bind `inputMode`
to a string attribute, and delete the action.
:::
Expand Down
60 changes: 23 additions & 37 deletions docs/public/api/client-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@
"name": "t_arg",
"type": "string_table",
"optional": true,
"doc": "arguments sent with the event and read back with get_event_arg( n ) in the same order: a literal, a `${$source>/...}` or `${$parameters>/...}` client expression evaluated when the event fires, or `$event>...` for a field of the UI5 event itself."
"doc": "arguments sent with the event and read back with get_event_arg( n ) in the same order: a literal, a `${$source>/...}` or `${$parameters>/...}` client expression evaluated when the event fires, or `$event>...` for a field of the UI5 event itself. Two controller helpers reach what no binding path can, because a `${...}` addresses DATA and these address the live control tree: `$controller.textPath( ${$parameters>/item} )` - the ancestor-text breadcrumb of the control that fired; and `$controller.slotValue( 'POPUP', 'myId', 'getValue' )` - what a control in ANOTHER view slot currently holds. An id is local to the view or fragment it was written in, so a control in a dialog is not reachable otherwise; the slot keys are those of cs_view, and an empty one searches every open slot. The getter takes no arguments on purpose - to CALL a control use cs_event-control_by_id, which has a whitelist in front of it. Every miss (slot closed, id unknown, no such method, a getter that raises) is logged and sent as the empty string: an argument expression is evaluated while UI5 dispatches the handler, so one that throws loses the whole EVENT. `$controller.slotById( 'POPUP', 'myId' )` hands the control itself over for a null-tolerant reader such as textPath( ) - it answers null on a miss, which a method call on it would then throw over."
},
{
"name": "s_ctrl",
Expand Down Expand Up @@ -542,8 +542,9 @@
"name": "follow_up_action",
"group": "Events and frontend actions",
"doc": [
"Schedule a frontend action to run after the backend response has been processed. Two ways to call it: pass a frontend event as val (a cs_event-* constant, e.g. cs_event-set_title) with its arguments in t_arg and the framework builds the event call; or pass a raw JavaScript expression as val, without t_arg, to run it as it is. The families below take structured arguments; t_arg is POSITIONAL, and an empty argument between filled ones keeps its slot as ``.",
"Every one of them also works roundtrip-free when WIRED IN THE VIEW: write the same call where its result is consumed - `)->a( n = `press` v = client->follow_up_action( val = ... t_arg = ... ) )` - and the action runs in the browser without a server call.",
"Schedule a frontend action to run after the backend response has been processed: pass a frontend event as val (a cs_event-* constant, e.g. cs_event-set_title) with its arguments in t_arg, and the framework builds the event call as pure data. The families below take structured arguments; t_arg is POSITIONAL, and an empty argument between filled ones keeps its slot as ``.",
"A raw JavaScript expression as val (e.g. `sap.m.MessageToast.show('x')`) is not run - that form was removed. Its cs_event-* equivalents: control_global for the UI5 globals (MessageToast, MessageBox, BusyIndicator), control_by_id for a control method and hash_back for history.back( ). Frontend code of the app's own ships as a custom control in the customer frontend BSP (z2ui5_ccc).",
"Every cs_event-* action also works roundtrip-free when WIRED IN THE VIEW: write the same call where its result is consumed - `)->a( n = `press` v = client->follow_up_action( val = ... t_arg = ... ) )` - and the action runs in the browser without a server call.",
"**cs_event-control_by_id** - call a method on a control resolved by id, t_arg = id, method, params: ``client->follow_up_action( val = client->cs_event-control_by_id t_arg = VALUE #( ( `tab` ) ( `setSelectedIndex` ) ( `0` ) ) )``. Any public control method works unless it is on the frontend denylist (methods that would break framework invariants). The named per-aggregation mutators are on the allowed side of that line - addItem, removeItem, removeAllItems, destroyContent - and only the GENERIC reflection variants that take the member name as an argument are denied (addAggregation, removeAllAggregation, setAssociation, ...). The view is passed as the separate view parameter (default cs_view-main resolves the id across all open views; pass cs_view-popup/popover/... to scope the lookup to that view). Two entries are NOT UI5 methods but frontend capabilities in method form: `css` sets ONE whitelisted CSS declaration on the control's own DOM node (t_arg = id, `css`, property, value) - for a value the control has no property for at all, e.g. the width of a sap.m.Page; prefer a bound property wherever one exists. `toggleBy` opens/closes a popup anchored to a control (t_arg = id, `toggleBy`, anchor id). An association setter (setSelectedSection, setSelectedItem) clears the association when its argument is EMPTY. Wherever an argument takes a CONTROL ID, it also takes an aggregation ITEM, addressed positionally as `<id>/<aggregation>/<index>` (`carousel/pages/2`, 0-based). A control cloned from an aggregation template has no id the backend can spell - UI5 mints it from the template id, the parent id and the index, and the parent id carries the view prefix assigned at runtime - so this is the only way to reach one. It is the equivalent of the UI5 controller idiom `oCarousel.setActivePage( oCarousel.getPages()[ i ] )`. A plain id (no slashes) resolves exactly as before.",
"**cs_event-control_global** - call a whitelisted method on a global object (MESSAGE_TOAST, MESSAGE_BOX, BUSY_INDICATOR, THEMING, POPUP, INVISIBLE_MESSAGE, FORMATTING, ICON_POOL), t_arg = object, method, params: ``client->follow_up_action( val = client->cs_event-control_global t_arg = VALUE #( ( `BUSY_INDICATOR` ) ( `show` ) ( `0` ) ) )``. POPUP-setWithinArea confines every popup to the control whose id is passed (sap.ui.core.Popup.setWithinArea, needs UI5 >= 1.89) instead of to the window; an EMPTY argument releases the restriction again. INVISIBLE_MESSAGE-announce reads a text out to a screen reader without rendering it (sap.ui.core.InvisibleMessage, needs UI5 >= 1.78): t_arg = text, mode (Polite, default, or Assertive). It is a singleton, so there is no control id - this is the only way to announce a change the backend made. FORMATTING-setCustomCurrencies registers currency codes the standard sap.ui.model.type.Currency does not know, or overrides their digit count (sap/base/i18n/Formatting, needs UI5 >= 1.120): t_arg = JSON object, e.g. {\"BGN4\":{\"digits\":4}}. It REPLACES the whole registration - addCustomCurrencies MERGES codes into it instead (t_arg = the same map). Reaching for the wrong one is silent: an app that registers currencies as it loads more data and calls setCustomCurrencies drops what it registered before, and the symptom is a wrong digit count in a table, never an error. What this reaches is the FORMATTING configuration, not a control that has already formatted: a control caching its NumberFormat at init( ) - among them sap.ui.unified.Currency - keeps the digit count it was built with, because it implements no localization-change hook. A BOUND sap.ui.model.type.Currency does implement one and re-formats. ICON_POOL-registerFont makes an icon collection outside the default SAP-icons font resolvable - sap.tnt's SAP-icons-TNT is the common one: t_arg = fontFamily, fontURI, e.g. `SAP-icons-TNT` / `sap/tnt/themes/base/fonts/`. A normal UI5 app does this in its Component's init; an abap2UI5 app has no Component of its own, and IconPool is a module SINGLETON rather than a control, so no other wire reaches it. Without the registration a sap-icon://SAP-icons-TNT/... URI renders NO GLYPH and logs nothing. The fontURI is a module path in every real use and is resolved through sap.ui.require.toUrl, so the registration survives a different mount point; an absolute URL is passed through. Issue it from the init branch - the same collection is registered only once per session, so a repeat call costs nothing.",
"**cs_event-smart_variant_init** - run the initialise( ) handshake sap.ui.comp variant management needs (a controller would call oSmartVariantManagement.initialise( fnCallback, oPersonalizableControl )), t_arg = SmartVariantManagement id, personalizable control id (optional, default: the first control that registered itself): ``client->follow_up_action( val = client->cs_event-smart_variant_init t_arg = VALUE #( ( `pageVariant` ) ) )``. Without it the control keeps no personalizable control, saving a view fails inside sap.ui.fl and stored variants are never loaded. The action waits for that registration, which the smart controls do once their OData metadata has loaded.",
Expand All @@ -558,7 +559,7 @@
{
"name": "val",
"type": "string",
"doc": "the frontend event - a cs_event-* constant - or a raw JavaScript expression when t_arg is not supplied."
"doc": "the frontend event - a cs_event-* constant."
},
{
"name": "view",
Expand Down Expand Up @@ -635,9 +636,7 @@
"constants": [
{
"name": "cs_device",
"doc": [
"The values get( )-s_device carries, as constants to compare against: what system, browser, os and orientation say about the client, e.g. `IF client->get( )-s_device-system = client->cs_device-system-phone.`"
],
"doc": [],
"members": [
{
"name": "system",
Expand Down Expand Up @@ -742,9 +741,7 @@
},
{
"name": "cs_event",
"doc": [
"Every frontend event a wire or follow_up_action( ) can name: what the browser does when the response arrives (set_title, scroll_to, download_b64_file, clipboard_copy, ...) or when the wired control fires (the control_by_id / control_global / binding_call family), the smart-control handshakes, the hash family, and - at the end - obsolete spellings kept so old apps compile. follow_up_action( ) documents the families that take structured arguments; the rest take the argument their name suggests, one sample each in the cookbook."
],
"doc": [],
"members": [
{
"name": "popup_close",
Expand All @@ -757,24 +754,24 @@
"value": "POPOVER_CLOSE"
},
{
"name": "set_size_limit",
"name": "cross_app_nav_to_ext",
"type": "string",
"value": "SET_SIZE_LIMIT"
"value": "CROSS_APP_NAV_TO_EXT"
},
{
"name": "set_odata_model",
"name": "cross_app_nav_to_prev_app",
"type": "string",
"value": "SET_ODATA_MODEL"
"value": "CROSS_APP_NAV_TO_PREV_APP"
},
{
"name": "cross_app_nav_to_ext",
"name": "set_size_limit",
"type": "string",
"value": "CROSS_APP_NAV_TO_EXT"
"value": "SET_SIZE_LIMIT"
},
{
"name": "cross_app_nav_to_prev_app",
"name": "set_odata_model",
"type": "string",
"value": "CROSS_APP_NAV_TO_PREV_APP"
"value": "SET_ODATA_MODEL"
},
{
"name": "clipboard_copy",
Expand All @@ -786,6 +783,11 @@
"type": "string",
"value": "SET_TITLE"
},
{
"name": "set_title_launchpad",
"type": "string",
"value": "SET_TITLE_LAUNCHPAD"
},
{
"name": "set_favicon",
"type": "string",
Expand Down Expand Up @@ -816,11 +818,6 @@
"type": "string",
"value": "SYSTEM_LOGOUT"
},
{
"name": "keyboard_set_mode",
"type": "string",
"value": "KEYBOARD_SET_MODE"
},
{
"name": "keyboard_shortcut",
"type": "string",
Expand All @@ -836,11 +833,6 @@
"type": "string",
"value": "LOCATION_RELOAD"
},
{
"name": "set_title_launchpad",
"type": "string",
"value": "SET_TITLE_LAUNCHPAD"
},
{
"name": "download_b64_file",
"type": "string",
Expand Down Expand Up @@ -935,9 +927,7 @@
},
{
"name": "cs_view",
"doc": [
"The five slots the frontend renders into: the main view, the two nested views, the popup and the popover. The `view` parameter of follow_up_action( ) and _event_client( ) names the slot a control id is resolved in, and a keyboard shortcut can be scoped to one."
],
"doc": [],
"members": [
{
"name": "main",
Expand Down Expand Up @@ -993,9 +983,7 @@
"types": [
{
"name": "ty_s_name_value",
"doc": [
"A name-value pair, both strings - the shape of a launchpad startup parameter in get( )-t_comp_params (n = the parameter name the tile passed, v = its first value)."
],
"doc": [],
"members": [
{
"name": "n",
Expand All @@ -1009,9 +997,7 @@
},
{
"name": "ty_t_name_value",
"doc": [
"The table of name-value pairs get( )-t_comp_params carries."
],
"doc": [],
"definition": "STANDARD TABLE OF ty_s_name_value WITH EMPTY KEY"
},
{
Expand Down
Loading
Loading