Skip to content

Commit 82ed62b

Browse files
committed
Add UI Patterns cookbook section
New section under Cookbook with four high-frequency composition patterns that every business app reaches for — written step by step, each closing with a copy-paste-ready full example: - Form: SimpleForm with mixed field types, required marker and save - Master-Detail: list + detail panel in an hbox with selection wiring - Filter, Search, Table: search field and select in the table toolbar - IconTabBar: tabbed page with counters and per-tab content Sidebar slot sits between Popup/Popover and Browser Feature.
1 parent d02ebfb commit 82ed62b

5 files changed

Lines changed: 595 additions & 0 deletions

File tree

docs/.vitepress/config.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,17 @@ export default defineConfig({
194194
{ text: "Built-In", link: "/development/popups/built_in" },
195195
],
196196
},
197+
{
198+
text: "UI Patterns",
199+
link: "/development/patterns/form",
200+
collapsed: true,
201+
items: [
202+
{ text: "Form", link: "/development/patterns/form" },
203+
{ text: "Master-Detail", link: "/development/patterns/master_detail" },
204+
{ text: "Filter, Search, Table", link: "/development/patterns/filter_table" },
205+
{ text: "IconTabBar", link: "/development/patterns/icon_tab_bar" },
206+
],
207+
},
197208
{
198209
text: "Browser Feature",
199210
link: "/development/specific/barcodes",
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
---
2+
outline: [2, 4]
3+
---
4+
# Filter, Search and Table
5+
6+
The closest UI5 cousin of an ALV report: a table with a search field and one or two filter dropdowns above it. The user types, picks filters, the list narrows.
7+
8+
The split of work is simple — UI5 takes the input, ABAP does the filtering. Each filter change fires a backend event, the handler re-runs the `SELECT` (or filters the in-memory table), and the table re-renders.
9+
10+
#### Step 1 — The bare table
11+
12+
Start with the result table. No filters yet — just bind the data and show columns. This is the [tables](../model/tables.md) pattern unchanged:
13+
14+
```abap
15+
DATA(view) = z2ui5_cl_xml_view=>factory( )->page( `Sales Orders` ).
16+
DATA(tab) = view->table( client->_bind( mt_orders ) growing = abap_true ).
17+
tab->columns( )->column( )->text( `Order` )->get_parent(
18+
)->column( )->text( `Customer` )->get_parent(
19+
)->column( )->text( `Value` ).
20+
tab->items( )->column_list_item( )->cells(
21+
)->text( `{VBELN}` )->text( `{KUNNR}` )->text( `{NETWR}` ).
22+
```
23+
24+
#### Step 2 — A search field in the toolbar
25+
26+
Add a header toolbar to the table with a `search_field`. Bind its `value` two-way to a filter string and fire `SEARCH` on enter or button click. The `liveChange` event would fire on every keystroke — usually too chatty for a backend roundtrip:
27+
28+
```abap
29+
DATA(tab) = view->table( client->_bind( mt_orders ) growing = abap_true ).
30+
tab->header_toolbar( )->overflow_toolbar(
31+
)->title( `Sales Orders`
32+
)->toolbar_spacer( )
33+
)->search_field(
34+
value = client->_bind_edit( mv_search )
35+
width = `20rem`
36+
search = client->_event( `SEARCH` ) ).
37+
```
38+
39+
#### Step 3 — A filter dropdown
40+
41+
Add a `select` next to the search field for typed filtering — sales organisation, status, type — anything with a small fixed set of values. A `change` event makes it re-filter immediately, without a separate apply button:
42+
43+
```abap
44+
tab->header_toolbar( )->overflow_toolbar(
45+
)->title( `Sales Orders`
46+
)->toolbar_spacer( )
47+
)->label( `Org:` )
48+
)->select(
49+
selectedkey = client->_bind_edit( mv_org )
50+
change = client->_event( `APPLY_FILTER` )
51+
)->items( )->core_item( key = `` text = `All`
52+
)->core_item( key = `1000` text = `Germany`
53+
)->core_item( key = `2000` text = `France` )->get_parent(
54+
)->search_field(
55+
value = client->_bind_edit( mv_search )
56+
width = `20rem`
57+
search = client->_event( `APPLY_FILTER` ) ).
58+
```
59+
60+
#### Step 4 — Apply the filter in ABAP
61+
62+
One event handler covers both filter sources. Use a clean re-`SELECT` for small datasets, or — when the source is already in memory — filter with `FILTER` / `DELETE … WHERE`:
63+
64+
```abap
65+
WHEN client->check_on_event( `APPLY_FILTER` ).
66+
SELECT FROM vbak
67+
FIELDS vbeln, kunnr, netwr, vkorg
68+
WHERE ( @mv_org = '' OR vkorg = @mv_org )
69+
AND ( @mv_search = '' OR vbeln LIKE @( `%` && mv_search && `%` )
70+
OR kunnr LIKE @( `%` && mv_search && `%` ) )
71+
INTO TABLE @mt_orders
72+
UP TO 100 ROWS.
73+
client->view_model_update( ).
74+
```
75+
76+
#### Full Example
77+
78+
A complete order list with search and a sales-org filter, both wired to the same event:
79+
80+
```abap
81+
CLASS z2ui5_cl_sample_filter_table DEFINITION PUBLIC.
82+
83+
PUBLIC SECTION.
84+
INTERFACES z2ui5_if_app.
85+
TYPES:
86+
BEGIN OF ty_order,
87+
vbeln TYPE vbak-vbeln,
88+
kunnr TYPE vbak-kunnr,
89+
netwr TYPE vbak-netwr,
90+
vkorg TYPE vbak-vkorg,
91+
END OF ty_order.
92+
DATA mt_orders TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY.
93+
DATA mv_search TYPE string.
94+
DATA mv_org TYPE vbak-vkorg.
95+
96+
ENDCLASS.
97+
98+
CLASS z2ui5_cl_sample_filter_table IMPLEMENTATION.
99+
METHOD z2ui5_if_app~main.
100+
101+
CASE abap_true.
102+
103+
WHEN client->check_on_init( ).
104+
load_data( ).
105+
render( ).
106+
107+
WHEN client->check_on_event( `APPLY_FILTER` ).
108+
load_data( ).
109+
client->view_model_update( ).
110+
111+
ENDCASE.
112+
113+
ENDMETHOD.
114+
115+
METHOD load_data.
116+
117+
SELECT FROM vbak
118+
FIELDS vbeln, kunnr, netwr, vkorg
119+
WHERE ( @mv_org = '' OR vkorg = @mv_org )
120+
AND ( @mv_search = '' OR vbeln LIKE @( `%` && mv_search && `%` )
121+
OR kunnr LIKE @( `%` && mv_search && `%` ) )
122+
INTO TABLE @mt_orders
123+
UP TO 100 ROWS.
124+
125+
ENDMETHOD.
126+
127+
METHOD render.
128+
129+
DATA(view) = z2ui5_cl_xml_view=>factory( )->shell( )->page( `Sales Orders` ).
130+
DATA(tab) = view->table( client->_bind( mt_orders ) growing = abap_true ).
131+
132+
tab->header_toolbar( )->overflow_toolbar(
133+
)->title( `Sales Orders`
134+
)->toolbar_spacer( )
135+
)->label( `Org:` )
136+
)->select(
137+
selectedkey = client->_bind_edit( mv_org )
138+
change = client->_event( `APPLY_FILTER` )
139+
)->items( )->core_item( key = `` text = `All`
140+
)->core_item( key = `1000` text = `Germany`
141+
)->core_item( key = `2000` text = `France` )->get_parent(
142+
)->search_field(
143+
value = client->_bind_edit( mv_search )
144+
width = `20rem`
145+
search = client->_event( `APPLY_FILTER` ) ).
146+
147+
tab->columns( )->column( )->text( `Order` )->get_parent(
148+
)->column( )->text( `Customer` )->get_parent(
149+
)->column( )->text( `Value` )->get_parent(
150+
)->column( )->text( `Org` ).
151+
tab->items( )->column_list_item( )->cells(
152+
)->text( `{VBELN}` )->text( `{KUNNR}` )->text( `{NETWR}` )->text( `{VKORG}` ).
153+
154+
client->view_display( view->stringify( ) ).
155+
156+
ENDMETHOD.
157+
158+
ENDCLASS.
159+
```
160+
161+
::: tip
162+
For more advanced patterns — saved filter variants, multi-range selection, fuzzy search — the [layout-variant add-on](https://github.com/abap2UI5-addons) and `Z2UI5_CL_POP_GET_RANGE` (see [Built-In Popups](../popups/built_in.md)) are worth a look.
163+
:::

docs/development/patterns/form.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
outline: [2, 4]
3+
---
4+
# Form
5+
6+
An input mask with labels, fields and a save button — the bread-and-butter screen of every business app. UI5 builds it with `sap.ui.layout.form.SimpleForm`: a responsive grid that arranges label/field pairs and wraps automatically on small screens.
7+
8+
The pattern is always the same — a `simple_form` with `editable = abap_true`, then `content( 'form' )` for the form body, and finally a sequence of `label( … )` + an input control. What follows builds that up step by step.
9+
10+
#### Step 1 — Label and input
11+
12+
The minimum: one label, one input, bound to a string field. Set `editable` so the user can type into it:
13+
14+
```abap
15+
client->view_display( z2ui5_cl_xml_view=>factory(
16+
)->page( `Customer`
17+
)->simple_form( editable = abap_true
18+
)->content( `form`
19+
)->label( `Name` )->input( client->_bind_edit( ms_customer-name )
20+
)->stringify( ) ).
21+
```
22+
23+
#### Step 2 — More field types
24+
25+
Real forms use more than text inputs. Mix in `select` for dropdowns, `check_box` for booleans, `date_picker` for dates and `text_area` for longer text. Bind each with `_bind_edit` against the matching ABAP field:
26+
27+
```abap
28+
DATA(form) = z2ui5_cl_xml_view=>factory(
29+
)->page( `Customer`
30+
)->simple_form( editable = abap_true
31+
)->content( `form` ).
32+
33+
form->label( `Name` )->input( client->_bind_edit( ms_customer-name ) ).
34+
form->label( `Country` )->select( selectedkey = client->_bind_edit( ms_customer-country )
35+
)->items( )->core_item( key = `DE` text = `Germany`
36+
)->core_item( key = `FR` text = `France`
37+
)->core_item( key = `IT` text = `Italy` ).
38+
form->label( `Active` )->check_box( selected = client->_bind_edit( ms_customer-active ) ).
39+
form->label( `Birthday` )->date_picker( value = client->_bind_edit( ms_customer-birthday ) ).
40+
form->label( `Notes` )->text_area( value = client->_bind_edit( ms_customer-notes ) rows = `4` ).
41+
```
42+
43+
Storing the result as `DATA(form)` and adding rows in separate statements is easier to read than one long chain — pick whichever style fits your code.
44+
45+
#### Step 3 — Required and validation
46+
47+
Mark a field required by setting `required = abap_true` on the `label`. UI5 then renders the red asterisk. The actual validation still runs in ABAP — check the values on save and report problems via the message API:
48+
49+
```abap
50+
form->label( `Name` required = abap_true )->input( client->_bind_edit( ms_customer-name ) ).
51+
52+
" ...later in the save handler:
53+
WHEN client->check_on_event( `SAVE` ).
54+
IF ms_customer-name IS INITIAL.
55+
client->message_box_display( `Name is required.` ).
56+
RETURN.
57+
ENDIF.
58+
" ...persist ms_customer here...
59+
client->message_toast_display( `saved` ).
60+
```
61+
62+
For richer feedback — error states on individual inputs, multi-message popups — see [Messages, Errors](../messages/messages.md).
63+
64+
#### Full Example
65+
66+
A complete editable customer form with a save handler and basic validation:
67+
68+
```abap
69+
CLASS z2ui5_cl_sample_form DEFINITION PUBLIC.
70+
71+
PUBLIC SECTION.
72+
INTERFACES z2ui5_if_app.
73+
TYPES:
74+
BEGIN OF ty_customer,
75+
name TYPE string,
76+
country TYPE string,
77+
active TYPE abap_bool,
78+
birthday TYPE d,
79+
notes TYPE string,
80+
END OF ty_customer.
81+
DATA ms_customer TYPE ty_customer.
82+
83+
ENDCLASS.
84+
85+
CLASS z2ui5_cl_sample_form IMPLEMENTATION.
86+
METHOD z2ui5_if_app~main.
87+
88+
CASE abap_true.
89+
90+
WHEN client->check_on_init( ).
91+
ms_customer = VALUE #( country = `DE` active = abap_true ).
92+
93+
DATA(form) = z2ui5_cl_xml_view=>factory(
94+
)->page( `Customer`
95+
)->simple_form( editable = abap_true
96+
)->content( `form` ).
97+
98+
form->title( `General` ).
99+
form->label( `Name` required = abap_true )->input( client->_bind_edit( ms_customer-name ) ).
100+
form->label( `Country` )->select( selectedkey = client->_bind_edit( ms_customer-country )
101+
)->items( )->core_item( key = `DE` text = `Germany`
102+
)->core_item( key = `FR` text = `France`
103+
)->core_item( key = `IT` text = `Italy` ).
104+
form->label( `Active` )->check_box( selected = client->_bind_edit( ms_customer-active ) ).
105+
form->label( `Birthday` )->date_picker( value = client->_bind_edit( ms_customer-birthday ) ).
106+
107+
form->title( `Notes` ).
108+
form->label( `Comment` )->text_area( value = client->_bind_edit( ms_customer-notes ) rows = `4` ).
109+
110+
form->toolbar( )->toolbar_spacer(
111+
)->button( text = `Save` type = `Emphasized` press = client->_event( `SAVE` )
112+
)->button( text = `Cancel` press = client->_event( `CANCEL` ) ).
113+
114+
client->view_display( form->stringify( ) ).
115+
116+
WHEN client->check_on_event( `SAVE` ).
117+
IF ms_customer-name IS INITIAL.
118+
client->message_box_display( `Name is required.` ).
119+
RETURN.
120+
ENDIF.
121+
" ...persist ms_customer here...
122+
client->message_toast_display( `saved` ).
123+
124+
WHEN client->check_on_event( `CANCEL` ).
125+
CLEAR ms_customer.
126+
client->view_model_update( ).
127+
128+
ENDCASE.
129+
130+
ENDMETHOD.
131+
ENDCLASS.
132+
```
133+
134+
::: tip
135+
For a responsive layout that places labels next to fields on desktop and stacks them on mobile, pass `layout = 'ResponsiveGridLayout'` to `simple_form` and tune `labelspanxl`, `columnsxl` etc. — see the `Z2UI5_CL_APP_STARTUP` source for a fine-tuned reference.
136+
:::

0 commit comments

Comments
 (0)