Skip to content

Commit e2123e8

Browse files
committed
docs(view): rewrite XML Templating page around UI5 template: instructions
1 parent 12c691c commit e2123e8

1 file changed

Lines changed: 107 additions & 61 deletions

File tree

docs/cookbook/view/xml_templating.md

Lines changed: 107 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,96 +3,142 @@ outline: [2, 4]
33
---
44
# XML Templating
55

6-
abap2UI5 sends views to the browser as XML strings. XML Templating lets you construct those strings dynamically in ABAP — branching on conditions, looping over data, and composing fragments — so that the view the browser receives is already fully resolved without any client-side template engine.
6+
XML Templating is a **UI5 preprocessor feature**, not an abap2UI5 invention. The UI5 runtime understands a small set of instructions in the `template` XML namespace — `template:repeat`, `template:if`, `template:then`, `template:else`, `template:with` — and expands them into plain XML *before* the control tree is created. abap2UI5 exposes these instructions through the fluent builder so you can drive the expansion from ABAP data.
7+
8+
See the official UI5 references for the underlying mechanics:
9+
[XML Templating](https://sapui5.hana.ondemand.com/sdk/#/topic/5ee619fc1370463ea674ee04b65ed83b),
10+
[`template:repeat`](https://sapui5.hana.ondemand.com/sdk/#/topic/512e545ba66f4214ba0de1eb56f319e1),
11+
[`template:if`](https://sapui5.hana.ondemand.com/sdk/#/topic/fc185952184c48618ef46306a1517f8c).
712

813
#### How It Works
914

10-
Every abap2UI5 view is ultimately an XML string. You can build that string however you like: literal assignment, string concatenation, helper method calls, or a dedicated XML builder. The browser never sees template syntax — it only receives the final, rendered XML.
15+
Templating happens **once**, at view instantiation, against a JSON model. The preprocessor walks the XML, evaluates each `template:` instruction against that model, and replaces the instruction with the resulting XML. After that the control tree is built from the expanded XML and normal data binding takes over.
1116

12-
```abap
13-
METHOD z2ui5_if_app~main.
14-
IF client->check_on_init( ).
15-
DATA(xml) = z2ui5_cl_xml_view=>factory( ).
16-
client->view_display( xml->stringify( ) ).
17-
ENDIF.
18-
ENDMETHOD.
19-
```
17+
This timing has two consequences:
2018

21-
#### Conditional Rendering
19+
- **Expansion is build-time.** A `template:repeat` over an internal table produces a fixed number of controls; the expanded XML is what UI5 renders.
20+
- **Data changes do not re-template.** If the data driving the template changes, the existing expansion stays as is. To pick up the change you must rebuild the view (`view_display`) or the templated fragment (`nest_view_display`) — see [Re-rendering](#re-rendering) below.
2221

23-
Use ABAP `IF` statements to include or exclude controls depending on runtime state:
22+
abap2UI5 wires up the templating model for you. Every variable you bind with `client->_bind( ... )` or `client->_bind_edit( ... )` is reachable inside templates via the `template>` model prefix:
2423

25-
```abap
26-
DATA(xml) = z2ui5_cl_xml_view=>factory( ).
27-
DATA(page) = xml->page( 'Conditional View' ).
24+
| ABAP binding | Path inside templates |
25+
| ---------------------------------- | --------------------------- |
26+
| `client->_bind( mt_layout )` | `{template>/MT_LAYOUT}` |
27+
| `client->_bind_edit( mv_flag )` | `{template>/XX/MV_FLAG}` |
28+
29+
The `template>` model is the templating engine's view of the data — distinct from the default model used by runtime bindings like `{MT_DATA}`.
2830

29-
IF is_admin = abap_true.
30-
page->button(
31-
text = 'Admin Action'
32-
press = client->_event( 'ADMIN_ACTION' ) ).
33-
ENDIF.
31+
#### `template:repeat` — Loops
3432

35-
page->text( 'Hello, ' && username ).
36-
client->view_display( xml->stringify( ) ).
33+
`template:repeat` clones its children once per row of the bound list. Use it when the **structure** of the view (e.g. which columns a table has) depends on data:
34+
35+
```abap
36+
mt_layout = VALUE #( ( fname = `NAME` merge = `false` visible = `true` )
37+
( fname = `DATE` merge = `false` visible = `true` )
38+
( fname = `AGE` merge = `false` visible = `false` ) ).
39+
client->_bind( mt_layout ).
40+
41+
view->table( client->_bind( mt_data )
42+
)->columns(
43+
)->template_repeat( list = `{template>/MT_LAYOUT}`
44+
var = `L0`
45+
)->column( mergeduplicates = `{L0>MERGE}`
46+
visible = `{L0>VISIBLE}`
47+
)->text( `{L0>FNAME}` )->get_parent(
48+
)->get_parent( )->get_parent(
49+
)->items(
50+
)->column_list_item(
51+
)->cells(
52+
)->template_repeat( list = `{template>/MT_LAYOUT}`
53+
var = `L1`
54+
)->object_identifier( text = `{= '{' + ${L1>FNAME} + '}' }` ).
3755
```
3856

39-
Only the controls added to the builder are serialized — nothing from the skipped branch appears in the XML.
57+
Notes on the snippet:
4058

41-
#### Repeating Controls
59+
- `list` is the binding path that drives the loop; `var` is the alias used inside the loop body (here `L0` for the column headers, `L1` for the cells).
60+
- Inside the loop, `{L0>FNAME}` is a templating-time read — it ends up as the literal string `NAME`/`DATE`/`AGE` in the expanded XML.
61+
- `{= '{' + ${L1>FNAME} + '}' }` is an [expression binding](https://sapui5.hana.ondemand.com/sdk/#/topic/daf6852a04b44d118963968a1239d2c0) that **constructs another binding string at templating time**. With `L1>FNAME = NAME` it expands to `text="{NAME}"`, which becomes a normal runtime binding against the row of `mt_data`. This is the standard pattern for templated tables: outer loop builds the columns, inner loop builds the cells, expression binding wires each cell to the right field of the row.
62+
- `template_repeat` accepts the same optional attributes as the UI5 instruction (`startIndex`, `length`) plus list-binding extras like sorters and filters.
4263

43-
Loop over an internal table to generate repeated controls:
64+
The full sample is `Z2UI5_CL_DEMO_APP_173`.
4465

45-
```abap
46-
DATA(list) = page->list( ).
66+
#### `template:if` / `template:then` / `template:else` — Conditionals
67+
68+
`template:if` evaluates an expression against the templating model and keeps or drops its children accordingly. With a `template:then` / `template:else` pair you get a two-branch switch:
4769

48-
LOOP AT items INTO DATA(item).
49-
list->item(
50-
title = item-title
51-
description = item-description ).
52-
ENDLOOP.
70+
```abap
71+
client->_bind_edit( mv_flag ).
72+
73+
view->template_if( `{template>/XX/MV_FLAG}`
74+
)->template_then(
75+
)->icon( src = `sap-icon://accept`
76+
color = `green` )->get_parent(
77+
)->template_else(
78+
)->icon( src = `sap-icon://decline`
79+
color = `red` ).
5380
```
5481

55-
Each iteration appends a child node. The resulting XML contains exactly as many `<Item>` elements as there were rows in `items`.
82+
The test argument follows the same rules as in UI5: any binding expression is fine, and the string `"false"` is treated as boolean `false` (a UI5 convenience). For richer conditions use expression binding, e.g. `` `{= ${template>/XX/MV_COUNT} > 0 }` ``.
83+
84+
`template:elseif` is also supported by UI5; check `Z2UI5_CL_XML_VIEW` for the corresponding fluent method or fall back to the generic builder (see [Definition](/cookbook/view/definition#the-fully-generic-builder)).
5685

57-
#### Composing Fragments
86+
#### Re-rendering
5887

59-
Break complex views into reusable helper methods that each return a subtree:
88+
Because templating runs once, the view must be rebuilt for changes in the templating model to take effect. There are two strategies:
89+
90+
**Rebuild the whole view** — simplest, works for most apps. After the user toggles a flag, render the view again:
6091

6192
```abap
62-
METHOD build_header.
63-
rv_header = io_page->toolbar( )->title( title ).
64-
ENDMETHOD.
65-
66-
METHOD build_table.
67-
DATA(tbl) = io_page->table( ).
68-
" ... add columns and rows
69-
rv_table = tbl.
70-
ENDMETHOD.
93+
CASE client->get( )-event.
94+
WHEN `CHANGE_FLAG`.
95+
view_display( ). " builds and ships the view again
96+
ENDCASE.
7197
```
7298

73-
Call the helpers in sequence inside `main` to assemble the final view. Because the builder accumulates children as regular ABAP object references, standard ABAP patterns (loops, conditions, method calls, function modules) all apply without restriction.
74-
75-
#### Dynamic Column Sets
99+
This is the pattern used in `Z2UI5_CL_DEMO_APP_173`: the switch fires `CHANGE_FLAG`, `view_display` runs, the new value of `mv_flag` flows through `template:if`, and the icon swaps.
76100

77-
A common pattern is selecting which columns to show based on user settings or authorization:
101+
**Rebuild only the templated fragment** — keeps a stable shell on screen and replaces a nested piece. `Z2UI5_CL_DEMO_APP_176` separates the main view from the templated table:
78102

79103
```abap
80-
DATA(cols) = xml->table( )->columns( ).
81-
82-
IF show_price = abap_true.
83-
cols->column( )->text( 'Price' ).
84-
ENDIF.
85-
IF show_stock = abap_true.
86-
cols->column( )->text( 'Stock' ).
87-
ENDIF.
104+
" Main view: built once, stays on screen
105+
DATA(lo_view) = z2ui5_cl_xml_view=>factory( ).
106+
lo_view->shell( )->page( title = `Main View` id = `test` ... ).
107+
client->view_display( lo_view->stringify( ) ).
108+
109+
" Nested templated view: inserted into the main view by id
110+
DATA(lo_view_nested) = z2ui5_cl_xml_view=>factory( ).
111+
lo_view_nested->shell( )->page( `Nested View`
112+
)->table( client->_bind( mt_data )
113+
)->columns(
114+
)->template_repeat( list = `{template>/MT_LAYOUT}` var = `LO`
115+
)->column( ... ) " ...
116+
117+
client->nest_view_display( val = lo_view_nested->stringify( )
118+
id = `test`
119+
method_insert = `addContent` ).
88120
```
89121

90-
The same loop that drives the column headers can drive the cell binding paths, keeping them in sync.
122+
`nest_view_display` targets a control in the existing view by id (`test`) and appends/replaces the nested view there. To refresh the templated piece on a data change, call `nest_view_display` again from the event handler — the main view is left untouched.
123+
124+
#### Templating vs ABAP-side Composition
125+
126+
The two demo apps both build *dynamic* views, but with different mechanics. Pick based on what the dynamic part actually is:
127+
128+
| Situation | Approach |
129+
| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
130+
| Whether a control exists at all, decided once per render | ABAP `IF` around the builder call — simpler, no `template:` namespace involved |
131+
| Number of controls comes from an internal table, computed once | ABAP `LOOP` over the table, calling the builder for each row |
132+
| Reusable XML fragment that UI5 itself should expand against metadata | `template:repeat` / `template:if` — keeps the templating logic in the view layer |
133+
| Cells of a table where the column set itself is data-driven | `template:repeat` — UI5 expands columns and cell bindings in one pass, as in app 173 |
134+
135+
Plain ABAP control flow covers most cases and is easier to debug. Reach for `template:` when you want the expansion to live in the view (closer to standard UI5 patterns) or when you are mapping a metadata-style structure onto controls.
91136

92137
#### Tips
93138

94-
- Build the full view on every roundtrip. abap2UI5 is stateless by default, so the view is always reconstructed from the current model state — there is no diff or patch step.
95-
- Keep view-construction logic out of business-logic methods. A dedicated `build_view` method or a set of small fragment builders keeps `main` readable.
96-
- Prefer the fluent builder API (`z2ui5_cl_xml_view`) over raw string concatenation. The builder escapes special characters automatically and produces well-formed XML.
139+
- Always bind the data that drives a template (`client->_bind` / `client->_bind_edit`) **before** the builder call that references it. The binding registers the path that `{template>/...}` resolves against.
140+
- Inside `template:repeat`, prefer `{var>FIELD}` over deeper paths — it keeps the body readable and lets you nest repeats with distinct `var` names (`L0`, `L1`, ...) without collisions.
141+
- Use expression binding (`{= ... }`) when the value you need is a binding string itself. Templating-time expressions can read `${var>...}` and concatenate strings, which is how dynamic cell bindings are assembled.
142+
- If a templated control does not update after a data change, you forgot to rebuild — call `view_display` or `nest_view_display` again.
97143

98-
See `Z2UI5_CL_DEMO_APP_032` and `Z2UI5_CL_DEMO_APP_033` for complete examples of conditional and dynamic views.
144+
See `Z2UI5_CL_DEMO_APP_173` for `template:repeat` + `template:if` in a single view and `Z2UI5_CL_DEMO_APP_176` for the stable-shell / templated-nested-view pattern.

0 commit comments

Comments
 (0)