Frontend ​
If you don't want to handle the event in the backend, fire actions directly on the frontend. The difference between the two event styles:
client->_event( )— causes a backend roundtrip; the event runs in themainmethodclient->follow_up_action( )— runs an action in the browser; no backend call
The two positions ​
follow_up_action( ) is one method in two positions, and the position is
what decides when the browser runs the action. The call is the same either
way — same val, same t_arg, same view — so there is one method to learn
and not two.
In a view attribute, its result is a string that is consumed as the attribute's value, and the action is wired to the control. The browser runs it when the user presses, with no roundtrip at all: the backend never hears about the click.
)->tag( `Button`
)->a( n = `text` v = `reload`
)->a( n = `press` v = client->follow_up_action( client->cs_event-location_reload ) )
(An event that takes no arguments needs no t_arg at all, and val can be
passed positionally, as here.)
As a statement in main, the same call is scheduled instead of wired, and
the browser runs it once the response arrives — i.e. after your backend work
is done. This is the follow-up in the name: finish the event handler, then have
the frontend do one more thing.
CASE client->get( )-event.
WHEN `SAVE`.
save( ).
client->message_toast_display( `saved` ).
client->follow_up_action( " runs in the browser after this response
val = client->cs_event-set_title
t_arg = VALUE #( ( |Invoice { mv_id }| ) ) ).
ENDCASE.
Neither form is a special case of the other, and the same events are available in both: everything in the table below can be wired to a control or scheduled after a roundtrip. Pick the position by when the action has to happen, not by what it does.
The frontend events ​
The frontend events, as z2ui5_if_client=>cs_event carries them:
CONSTANTS:
BEGIN OF cs_event,
"Framework
popup_close TYPE string VALUE `POPUP_CLOSE`,
popover_close TYPE string VALUE `POPOVER_CLOSE`,
set_size_limit TYPE string VALUE `SET_SIZE_LIMIT`,
set_odata_model TYPE string VALUE `SET_ODATA_MODEL`,
cross_app_nav_to_ext TYPE string VALUE `CROSS_APP_NAV_TO_EXT`,
cross_app_nav_to_prev_app TYPE string VALUE `CROSS_APP_NAV_TO_PREV_APP`,
"Actions
clipboard_copy TYPE string VALUE `CLIPBOARD_COPY`,
set_title TYPE string VALUE `SET_TITLE`,
set_title_launchpad TYPE string VALUE `SET_TITLE_LAUNCHPAD`,
set_favicon TYPE string VALUE `SET_FAVICON`,
set_focus TYPE string VALUE `SET_FOCUS`,
scroll_to TYPE string VALUE `SCROLL_TO`,
scroll_into_view TYPE string VALUE `SCROLL_INTO_VIEW`,
start_timer TYPE string VALUE `START_TIMER`,
keyboard_shortcut TYPE string VALUE `KEYBOARD_SHORTCUT`,
open_new_tab TYPE string VALUE `OPEN_NEW_TAB`,
location_reload TYPE string VALUE `LOCATION_RELOAD`,
system_logout TYPE string VALUE `SYSTEM_LOGOUT`,
download_b64_file TYPE string VALUE `DOWNLOAD_B64_FILE`,
urlhelper TYPE string VALUE `URLHELPER`,
store_data TYPE string VALUE `STORE_DATA`,
play_audio TYPE string VALUE `PLAY_AUDIO`,
"Control calls (positional t_arg)
control_by_id TYPE string VALUE `CONTROL_BY_ID`,
control_global TYPE string VALUE `CONTROL_GLOBAL`,
binding_call TYPE string VALUE `BINDING_CALL`,
bind_element TYPE string VALUE `BIND_ELEMENT`,
"Smart controls (sap.ui.comp)
smart_variant_init TYPE string VALUE `SMART_VARIANT_INIT`,
filter_bar_variant_init TYPE string VALUE `FILTER_BAR_VARIANT_INIT`,
"URL and app state - the hash_* family is named after UI5's
"sap/ui/core/routing/HashChanger
hash_set TYPE string VALUE `SET_PUSH_STATE`,
hash_replace TYPE string VALUE `HASH_REPLACE`,
hash_back TYPE string VALUE `HASH_BACK`,
hash_attach_changed TYPE string VALUE `HASH_ATTACH_CHANGED`,
hash_routing TYPE string VALUE `SET_NAV_ROUTING`,
app_state_set_active TYPE string VALUE `SET_APP_STATE_ACTIVE`,
END OF cs_event.
Some of these events have their own pages: keyboard_shortcut binds key combinations to backend events, the hash_* family puts the URL fragment under the router's control or the app's own, and smart_variant_init / filter_bar_variant_init wire variant management for smart controls. The dedicated cookbook pages under Browser Interaction and Device Capabilities carry the argument list of each event.
For example, to open a new tab directly from a button press (no backend involved):
METHOD z2ui5_if_app~main.
DATA(view) = z2ui5_cl_ui5_view_builder=>factory(
)->ele( n = `View` ns = `mvc`
)->a( n = `xmlns` v = `sap.m`
)->a( n = `xmlns:mvc` v = `sap.ui.core.mvc`
)->ele( `Page`
)->tag( `Button`
)->a( n = `text` v = `open new tab`
)->a( n = `press` v = client->follow_up_action(
val = client->cs_event-open_new_tab
t_arg = VALUE #( ( `https://github.com/abap2UI5` ) ) ) ).
client->view_display( view->stringify( ) ).
ENDMETHOD.
Calling control methods on the frontend ​
The control-call constants — control_by_id, control_global, binding_call and bind_element — are frontend events too, but instead of a fixed built-in action they operate on a control, a global object, a binding or a whole view slot. Their arguments are positional: an empty argument between two filled ones keeps its slot as ``.
| Event | t_arg (positional) |
|---|---|
control_by_id |
id, method, params… — call a method on a control resolved by id |
control_global |
object, method, params… — MESSAGE_TOAST, MESSAGE_BOX, BUSY_INDICATOR, THEMING, POPUP, INVISIBLE_MESSAGE, FORMATTING, ICON_POOL |
binding_call |
id, aggregation, method, params… — e.g. filter (path, operator, value1, value2) or sort (path, descending, group) on the aggregation's binding |
bind_element |
index, _bind( table ) — element-bind a whole view slot to a table row, see below |
For control_by_id, any public control method is callable as long as it is not on the framework's denylist: methods that would break abap2UI5's own invariants (destroying views, re-rendering, detaching the framework's handlers, …) are blocked, ordinary setters and toggles (setVisible, toggleBy, enablePostButton, …) simply work. A small set of methods is additionally special-cased for typed arguments. control_global and binding_call remain strict whitelists — only the listed global objects and the binding methods filter / sort are callable. Three of those objects are less obvious than the rest: POPUP-setWithinArea confines every popup to one control instead of to the window (UI5 ≥ 1.89; an empty argument releases it again), INVISIBLE_MESSAGE-announce reads a text out to a screen reader without rendering it (UI5 ≥ 1.78; t_arg = text, mode), and FORMATTING-setCustomCurrencies / -addCustomCurrencies register currency codes the standard sap.ui.model.type.Currency does not know (UI5 ≥ 1.120) — set… REPLACES the whole registration, add… MERGES codes into it.
" toggle a MessagePopover open, anchored to the pressing button, no roundtrip
press = client->follow_up_action(
val = client->cs_event-control_by_id
t_arg = VALUE #( ( `msgPopover` ) ( `toggleBy` ) ( `${$source>/id}` ) ) )
A t_arg argument that starts with a brace travels as real JSON rather than
as a string. That is how MESSAGE_TOAST and MESSAGE_BOX take their UI5
options — the object is the last argument of the call and is the option object
of sap.m.MessageToast.show( ) / sap.m.MessageBox.<type>( ) 1:1, with the box
type as the method:
client->follow_up_action(
val = client->cs_event-control_global
t_arg = VALUE #( ( `MESSAGE_BOX` ) ( `error` ) ( `Not saved.` )
( `{"contentWidth":"30rem","icon":"WARNING"}` ) ) ).
Which options belong here and which stay on client->message_box_display( ) is
the subject of Message.
The same events also work as a statement in your main method, with the identical t_arg — then the browser runs them after the response arrives, once your backend work is done:
" after backend processing, let the wizard advance: setNextStep is the
" STEP's method, so the current step is the control addressed
client->follow_up_action(
val = client->cs_event-control_by_id
t_arg = VALUE #( ( `STEP1` ) ( `setNextStep` ) ( `STEP2` ) ) ).
Element-binding a view slot: bind_element ​
bind_element binds a whole view slot (popup, popover, main, …) to one row of a bound table — the abap2UI5 equivalent of oControl.bindElement( ). All relative bindings in that slot ({NAME}, {CATEGORY}, nested aggregations) then resolve against the selected row, so a detail popup needs no data copied into event arguments:
" element-bind the popup slot to row <index> of t_product
client->follow_up_action(
val = client->cs_event-bind_element
view = client->cs_view-popup
t_arg = VALUE #( ( index ) ( client->_bind( t_product ) ) ) ).
The view parameter selects the slot to bind; t_arg carries the row index and the table's binding path. See Z2UI5_CL_SMP_APP_470 in the samples repository for a complete example.
The view parameter ​
For control_by_id, the control is looked up by id. follow_up_action( ) takes a separate view parameter (default cs_view-main) that scopes this lookup:
- omit it (or pass
cs_view-main) — the id is resolved across all open views; - pass
cs_view-popup/cs_view-popover/cs_view-nested/ … — the lookup is scoped to a control hosted in that view (e.g. a control living inside a popup).
" call a method on a control that lives inside the popup view
press = client->follow_up_action(
val = client->cs_event-control_by_id
view = client->cs_view-popup
t_arg = VALUE #( ( `NavCon` ) ( `to` ) ( `${$parameters>/selectedKey}` ) ) )
Migrated from a positional view slot
The view used to be the second entry of t_arg (id, view, method, …). It is now the dedicated view importing parameter, and the framework injects it into the argument list itself. Older examples that still pass `MAIN` as the second t_arg element no longer work — the extra entry shifts every argument by one and the call fails on the frontend. Drop the positional view entry and use the view parameter instead.
control_global ignores view (it is not resolved by id), and binding_call always resolves its id across all open views. For bind_element, view selects the slot to element-bind (see above).
Raw JavaScript ​
There is no third way. follow_up_action( ) used to take a raw JavaScript
expression as val and run it in the browser; that form is gone. A val the
frontend does not know as an event name is not run - it arrives as an unknown
event and nothing happens. What the raw form was reached for has a
cs_event-* equivalent: control_global for the UI5 globals (MessageToast,
MessageBox, BusyIndicator, …), control_by_id for a control method,
hash_back for history.back( ). Frontend code of the app's own ships as a
Custom Control in the customer
frontend BSP, with a defined interface and reviewable code - not as a string
assembled in ABAP, which is what the raw form was: a <script> from the
backend, executed with the user's privileges, outside UI5's output encoding and
against any strict Content Security Policy.
Working Samples ​
Complete apps from the sample catalog
that use what this page describes. Each is a single class in abap2UI5/samples
unless its row names another of the three sample repositories — pull that repository with
abapGit and start the class with ?app_start=<class>.
| Sample | Class |
|---|---|
| Link with preventDefault (A) | Z2UI5_CL_SMP_APP_472 |
| Element Binding to the Selected Row (A) | Z2UI5_CL_SMP_APP_470 |
| Expand a Panel by ID (setExpanded) (A) | Z2UI5_CL_SMP_APP_448 |
| Switch NavContainer Page by ID (A) | Z2UI5_CL_SMP_APP_088 |
| Wizard with Steps (A) | Z2UI5_CL_SMP_APP_202 |
| Open the PDF Viewer by ID (A) | Z2UI5_CL_SMP_APP_449 |
| Toggle by ID (toggleBy) (A) | Z2UI5_CL_SMP_APP_465 |
| Inline CSS on a Control (css) (A) | Z2UI5_CL_SMP_APP_513 |
| Aggregation Item by Index (A) | Z2UI5_CL_SMP_APP_514 |
| The Global Busy Indicator (A) | Z2UI5_CL_SMP_APP_515 |
| Register an Icon Font (A) | Z2UI5_CL_SMP_APP_518 |