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
17 changes: 17 additions & 0 deletions docs/cookbook/browser_interaction/timer.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ client->follow_up_action(

After 2 seconds the browser triggers a backend roundtrip with the event name `REFRESH`, which you handle via `check_on_event` like any other event.

An optional third argument keeps the busy indicator down for the tick — see [Without the Busy Indicator](#without-the-busy-indicator).

## Periodic Refresh

To get a repeating timer, simply re-arm it at the end of each handler:
Expand Down Expand Up @@ -86,6 +88,21 @@ WHEN client->check_on_event( `FIRE_OPEN_TAB` ).

There is one timer at a time. Calling `start_timer` again before the previous one fires replaces it — useful for a debounce, e.g. auto-saving an input field 500 ms after the last keystroke.

## Without the Busy Indicator

Each tick is a backend roundtrip like any other, so when the backend takes longer than a second, the full-screen busy indicator comes up. For a tick nobody is waiting on — a background poll, or a carousel or ticker that moves on by itself — the overlay then flashes over a screen the user is only reading. Pass `` `X` `` as a third argument to keep it down for that tick:

```abap
WHEN client->check_on_event( `TICK` ).
" read the new status ...
client->follow_up_action( val = client->cs_event-start_timer
t_arg = VALUE #( ( `TICK` ) ( `5000` ) ( `X` ) ) ).
```

The flag belongs to the tick that call arms, so a repeating timer passes it again every time it re-arms. Write it as the string `` `X` ``, not `abap_true`: `t_arg` is a string table, and abaplint's syntax check rejects a `c` value such as `abap_true` as one of its rows.

Only the overlay goes — the flag does for the tick what [`check_no_busy`](/resources/api#event) does for an `_event( )` wire. The tick is still the one roundtrip in flight: a click that lands while it runs is dropped, as during any roundtrip, and brings the overlay up at once. Leave the flag off for a tick the user is actually waiting for; there the overlay is what tells them the app is working.

::: warning
Each timer tick causes a full backend roundtrip. Use sensible intervals (e.g. 2000 ms or more) to avoid heavy server load.
:::
Expand Down
1 change: 1 addition & 0 deletions docs/public/api/client-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@
"**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.",
"**cs_event-filter_bar_variant_init** - wire a classic sap.ui.comp.filterbar.FilterBar to a SmartVariantManagement, t_arg = SmartVariantManagement id, FilterBar id: ``client->follow_up_action( val = client->cs_event-filter_bar_variant_init t_arg = VALUE #( ( `variant` ) ( `filterbar` ) ) )``. A SmartFilterBar knows its own fields and registers itself (see smart_variant_init above); a classic FilterBar does not, so a list report normally hand-writes the same controller boilerplate - registerFetchData / registerApplyData / registerGetFiltersWithValues, addPersonalizableControl( ) with a PersonalizableInfo, and a change handler per filter field that marks the variant as modified. This action does all of it, so saving, selecting and restoring a variant works without a single line of JavaScript. The restored values reach the backend through the binding of the filter fields, no extra roundtrip needed.",
"**cs_event-keyboard_shortcut** - bind a key combination to a named backend event, the declarative equivalent of a sap.ui.core.CommandExecution shortcut, t_arg = combination, event name: ``client->follow_up_action( val = client->cs_event-keyboard_shortcut t_arg = VALUE #( ( `Ctrl+S` ) ( `SAVE` ) ) )``. The combination is spelled like the UI5 one (`Ctrl+S`, `Ctrl+Shift+D`, `F2`; ctrl/shift/alt/meta in any order, cmd/command/option/control accepted as aliases). Pressing it fires the event exactly like a button press and suppresses the browser's own default for the combination. Registering the same combination again rebinds it; an empty event name removes it. The registrations belong to the running app and are dropped when another app takes over. An optional THIRD t_arg SCOPES the shortcut: the scoped registration wins while its scope is OPEN and the unscoped one applies otherwise, which is how a UI5 CommandExecution in a Popover's dependents shadows the page-level one for the same command. A scope is either a view slot (cs_view-popover/popup/nested/nested2/main) or the ID OF A CONTROL that can be open or closed - a Popover/Dialog declared in the view and opened with control_by_id openBy, which never enters a framework slot. A control scope beats a slot scope (it is the more specific statement), then the innermost open slot wins. An empty event name removes the registration of THAT scope only.",
"**cs_event-start_timer** - raise a BACKEND event once after a delay, t_arg = event name, delay in milliseconds: ``client->follow_up_action( val = client->cs_event-start_timer t_arg = VALUE #( ( `TICK` ) ( `5000` ) ) )``. There is one timer per app: arming it again replaces the pending one, and every roundtrip cancels it, so a repeating timer - a poll, a carousel - is re-armed by every response that keeps it going. A tick that meets a roundtrip in flight waits for it instead of being dropped. The tick is a roundtrip like any other and raises the global busy indicator after the usual one-second delay; an optional THIRD t_arg `X` (abap_bool as `X`/``, like every flag in t_arg) keeps it down - the check_no_busy of _event( ) for the tick, so a poll whose backend call takes longer than that no longer flashes the full-screen overlay: ``client->follow_up_action( val = client->cs_event-start_timer t_arg = VALUE #( ( `TICK` ) ( `5000` ) ( `X` ) ) )``. As with check_no_busy only the overlay goes: the tick is still the one roundtrip in flight, and a click that lands meanwhile is dropped by the busy guard and raises the overlay at once, like every dropped click.",
"**cs_event-hash_attach_changed** - APP-OWNED hash routing (HashChanger#attachHashChanged), the 1:1 counterpart of a UI5 router's own hash (`#/Page2`) for an app that does NOT use hash_routing, t_arg = a backend event name: ``client->follow_up_action( val = client->cs_event-hash_attach_changed t_arg = VALUE #( ( `HASH_CHANGED` ) ) )``. From then on hash_set( `/Page2` ) writes that value as the whole app hash (a pushed history entry), hash_replace( ) the same without a new entry, and a hash change the app did not write itself - browser Back/Forward, a manual URL edit - fires the registered event; the hash the browser now stands on arrives with that request (and with every other one, a fresh deep-link start included) in get( )-s_config-hash, so the app decides what to show. While registered, the framework leaves the hash entirely alone. Calling it without t_arg unregisters. The registration dies with an app switch - register it in view_display( ), so every render (a draft restore included) re-asserts it. Mutually exclusive with hash_routing (a routed app's hash belongs to the router) and with app_state_set_active (both claim the whole hash).",
"**cs_event-hash_back** - the UI5 onNavBack pattern: without t_arg one real step back in the browser history (`window.history.go(-1)` - the step is CONSUMED, and the resulting hash change fires the registered event). With t_arg = a fallback hash it guards the cold deep link the way UI5's recommended onNavBack does: when this page load never pushed an app hash, there is no in-app step to take, so the fallback is written as a REPLACE instead of falling out of the app - and the change fires the registered event, which shows the fallback route: ``client->follow_up_action( val = client->cs_event-hash_back t_arg = VALUE #( ( `/` ) ) )``.",
"**cs_event-store_data** - write a structure into the browser's local or session storage, the write half of the invisible z2ui5:Storage control that reads it back. t_arg = the structure holding TYPE (`local`/`session`), PREFIX, KEY and VALUE - handed over as the BINDING of that structure, so the frontend takes the current value: ``client->follow_up_action( val = client->cs_event-store_data t_arg = VALUE #( ( |${ client->_bind( ms_storage ) }| ) ) )``. It works from a view wire and from a handler alike: on a wire UI5 resolves the binding when the view is built, and a follow-up action queued in a handler carries the model PATH, which the frontend resolves when it runs. The ``$`` is what makes the wire form work: a bare ``{/MS_STORAGE}`` travels raw into the handler expression, where UI5 reads it as an object literal and the wire never fires - only ``${ }`` is a binding there. A handler resolves both spellings. An empty VALUE removes the key.",
Expand Down
2 changes: 2 additions & 0 deletions docs/resources/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ Every cs_event-* action also works roundtrip-free when WIRED IN THE VIEW: write

**cs_event-keyboard_shortcut** - bind a key combination to a named backend event, the declarative equivalent of a sap.ui.core.CommandExecution shortcut, t_arg = combination, event name: ``client->follow_up_action( val = client->cs_event-keyboard_shortcut t_arg = VALUE #( ( `Ctrl+S` ) ( `SAVE` ) ) )``. The combination is spelled like the UI5 one (`Ctrl+S`, `Ctrl+Shift+D`, `F2`; ctrl/shift/alt/meta in any order, cmd/command/option/control accepted as aliases). Pressing it fires the event exactly like a button press and suppresses the browser's own default for the combination. Registering the same combination again rebinds it; an empty event name removes it. The registrations belong to the running app and are dropped when another app takes over. An optional THIRD t_arg SCOPES the shortcut: the scoped registration wins while its scope is OPEN and the unscoped one applies otherwise, which is how a UI5 CommandExecution in a Popover's dependents shadows the page-level one for the same command. A scope is either a view slot (cs_view-popover/popup/nested/nested2/main) or the ID OF A CONTROL that can be open or closed - a Popover/Dialog declared in the view and opened with control_by_id openBy, which never enters a framework slot. A control scope beats a slot scope (it is the more specific statement), then the innermost open slot wins. An empty event name removes the registration of THAT scope only.

**cs_event-start_timer** - raise a BACKEND event once after a delay, t_arg = event name, delay in milliseconds: ``client->follow_up_action( val = client->cs_event-start_timer t_arg = VALUE #( ( `TICK` ) ( `5000` ) ) )``. There is one timer per app: arming it again replaces the pending one, and every roundtrip cancels it, so a repeating timer - a poll, a carousel - is re-armed by every response that keeps it going. A tick that meets a roundtrip in flight waits for it instead of being dropped. The tick is a roundtrip like any other and raises the global busy indicator after the usual one-second delay; an optional THIRD t_arg `X` (abap_bool as `X`/``, like every flag in t_arg) keeps it down - the check_no_busy of _event( ) for the tick, so a poll whose backend call takes longer than that no longer flashes the full-screen overlay: ``client->follow_up_action( val = client->cs_event-start_timer t_arg = VALUE #( ( `TICK` ) ( `5000` ) ( `X` ) ) )``. As with check_no_busy only the overlay goes: the tick is still the one roundtrip in flight, and a click that lands meanwhile is dropped by the busy guard and raises the overlay at once, like every dropped click.

**cs_event-hash_attach_changed** - APP-OWNED hash routing (HashChanger#attachHashChanged), the 1:1 counterpart of a UI5 router's own hash (`#/Page2`) for an app that does NOT use hash_routing, t_arg = a backend event name: ``client->follow_up_action( val = client->cs_event-hash_attach_changed t_arg = VALUE #( ( `HASH_CHANGED` ) ) )``. From then on hash_set( `/Page2` ) writes that value as the whole app hash (a pushed history entry), hash_replace( ) the same without a new entry, and a hash change the app did not write itself - browser Back/Forward, a manual URL edit - fires the registered event; the hash the browser now stands on arrives with that request (and with every other one, a fresh deep-link start included) in get( )-s_config-hash, so the app decides what to show. While registered, the framework leaves the hash entirely alone. Calling it without t_arg unregisters. The registration dies with an app switch - register it in view_display( ), so every render (a draft restore included) re-asserts it. Mutually exclusive with hash_routing (a routed app's hash belongs to the router) and with app_state_set_active (both claim the whole hash).

**cs_event-hash_back** - the UI5 onNavBack pattern: without t_arg one real step back in the browser history (`window.history.go(-1)` - the step is CONSUMED, and the resulting hash change fires the registered event). With t_arg = a fallback hash it guards the cold deep link the way UI5's recommended onNavBack does: when this page load never pushed an app hash, there is no in-app step to take, so the fallback is written as a REPLACE instead of falling out of the app - and the change fires the registered event, which shows the fallback route: ``client->follow_up_action( val = client->cs_event-hash_back t_arg = VALUE #( ( `/` ) ) )``.
Expand Down
5 changes: 5 additions & 0 deletions docs/resources/deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ The controls still ship and views that use them keep rendering. See
[Frontend](/cookbook/event_navigation/frontend) for the full argument
list of each event.

Migrating a `Timer` whose `finished` wire kept the busy indicator down with
`s_ctrl-check_no_busy`: `cs_event-start_timer` takes the same flag as its third
argument, `` `X` `` (*next release*) — see
[Timer](/cookbook/browser_interaction/timer#without-the-busy-indicator).

### `cs_config-title` → `cs_event-set_title`

The page title used to be set in the user exit and the tab title while the app
Expand Down
Loading