Skip to content

Commit 75bb758

Browse files
committed
docs(view): rewrite Nested Views around nest_view_display patterns
1 parent e2123e8 commit 75bb758

1 file changed

Lines changed: 164 additions & 62 deletions

File tree

docs/cookbook/view/nested_views.md

Lines changed: 164 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,98 +3,200 @@ outline: [2, 4]
33
---
44
# Nested Views
55

6-
A nested view is a view that is built from several independent parts. In abap2UI5 there are two main ways to achieve this: composing a single view tree from helper methods, and splitting the app into sub-apps that each own a slice of the view.
6+
A **nested view** in abap2UI5 is a separate XML view fragment that you inject into a *placeholder* inside another view. The main view stays on screen; only the nested fragment is rendered (and later re-rendered or refreshed) independently. This is the standard pattern for master-detail screens, side panels, tab content, and anywhere you want one part of the UI to update without rebuilding the whole page.
77

8-
#### Composing with Helper Methods
8+
If you know SAPUI5's [nested views](https://sapui5.hana.ondemand.com/sdk/#/topic/df8c9c3d6f2a4d728ba7d6f4cb6c6d35) (`<mvc:XMLView viewName="..."/>`), the goal is the same — split the UI into independently managed pieces. In abap2UI5 the wiring is done from ABAP at runtime: instead of referencing a static view file, you build the nested view's XML in ABAP and tell the client to plug it into a named slot.
99

10-
The fluent builder returns each node as an object reference, so you can pass a node into a helper method and let it add children there:
10+
#### The Basic Pattern
11+
12+
Two ingredients are needed:
13+
14+
1. **An anchor in the main view** — any control with an `id`. The nested view will be inserted *into* this control.
15+
2. **A nested view + a `nest_view_display` call** — builds the fragment and ships it to the named anchor.
1116

1217
```abap
13-
METHOD z2ui5_if_app~main.
14-
IF client->check_on_init( ).
15-
DATA(xml) = z2ui5_cl_xml_view=>factory( ).
16-
DATA(page) = xml->shell( )->page( 'My App' ).
18+
" 1) Main view with an anchor
19+
DATA(lo_view) = z2ui5_cl_xml_view=>factory( ).
20+
DATA(page) = lo_view->shell(
21+
)->page( title = `Main View`
22+
id = `test` ). " <-- the anchor id
23+
24+
page->content(
25+
)->button( text = `Re-render only the nested view`
26+
press = client->_event( `NEST` ) ).
27+
28+
" 2) Nested view, built like any other view
29+
DATA(lo_view_nested) = z2ui5_cl_xml_view=>factory(
30+
)->page( `Nested View`
31+
)->input( client->_bind_edit( mv_input_nest )
32+
)->button( text = `event`
33+
press = client->_event( `TEST` ) ).
34+
35+
IF client->check_on_init( ).
36+
client->view_display( lo_view->stringify( ) ).
37+
ENDIF.
38+
39+
CASE client->get( )-event.
40+
WHEN `NEST`.
41+
client->nest_view_display(
42+
val = lo_view_nested->stringify( )
43+
id = `test` " target the anchor
44+
method_insert = `addContent` ). " UI5 mutator on that control
45+
ENDCASE.
46+
```
1747

18-
build_header( page ).
19-
build_content( page ).
20-
build_footer( page ).
48+
What happens at runtime: `view_display` paints the main view; the page with `id="test"` sits on screen. When the user clicks the button, `nest_view_display` ships the nested XML to the client, which calls `addContent( ... )` on the control with that id. The nested fragment appears inside the page — without re-rendering the page itself.
2149

22-
client->view_display( xml->stringify( ) ).
23-
ENDIF.
24-
ENDMETHOD.
50+
The full pattern (re-render everything vs. main only vs. nested only) is in `Z2UI5_CL_DEMO_APP_065`.
2551

26-
METHOD build_header.
27-
io_page->toolbar( )->title( 'Header' ).
28-
ENDMETHOD.
52+
#### `nest_view_display` Parameters
2953

30-
METHOD build_content.
31-
io_page->vbox(
32-
)->text( 'Line 1'
33-
)->text( 'Line 2' ).
34-
ENDMETHOD.
54+
| Parameter | Meaning |
55+
| ---------------- | -------------------------------------------------------------------------------------------------- |
56+
| `val` | The nested view's XML, produced by `stringify( )`. |
57+
| `id` | The id of the anchor control in the main view. |
58+
| `method_insert` | UI5 mutator method called on the anchor to add the nested view (e.g. `addContent`). |
59+
| `method_destroy` | Optional. UI5 mutator method that removes the previous nested content before inserting the new one. |
60+
61+
`method_insert` and `method_destroy` are plain UI5 control methods — pick whichever the anchor exposes. The choice depends on the anchor's aggregation:
62+
63+
| Anchor control | Typical `method_insert` | Typical `method_destroy` |
64+
| ----------------------- | --------------------------- | ------------------------------- |
65+
| `Page`, `VBox`, generic | `addContent` | `removeAllContent` |
66+
| `FlexibleColumnLayout` | `addMidColumnPage` | `removeAllMidColumnPages` |
67+
| `FlexibleColumnLayout` | `addEndColumnPage` | `removeAllEndColumnPages` |
68+
| `FlexibleColumnLayout` | `addBeginColumnPage` | `removeAllBeginColumnPages` |
69+
70+
Always pass `method_destroy` when the nested view is going to be replaced over the lifetime of the app; otherwise consecutive calls stack new fragments on top of the old ones.
71+
72+
#### Independent Re-rendering
73+
74+
The whole point of nested views is to re-render only what changed. Three calls cover the common needs:
75+
76+
| Call | What it does |
77+
| --------------------------------- | --------------------------------------------------------------------------------------------- |
78+
| `client->view_display( ... )` | Replaces the main view's XML. The anchor is recreated, so any nested content is lost too. |
79+
| `client->nest_view_display( ... )`| Replaces only the nested view. The main view stays on screen. |
80+
| `client->nest_view_model_update( )` | Pushes current ABAP data values into the **already-rendered** nested view. No re-render. |
81+
82+
The matching call for the main view is `client->view_model_update( )` — push data changes into the rendered main view without re-rendering it.
83+
84+
A rule of thumb:
85+
86+
- **Layout changed** (different controls, new columns, new sections) → `view_display` / `nest_view_display`.
87+
- **Only the data changed** (a flag flipped, a row added to a bound table) → `view_model_update` / `nest_view_model_update`.
88+
89+
`Z2UI5_CL_DEMO_APP_065` shows the difference between the three options in a single screen with one button per call.
3590

36-
METHOD build_footer.
37-
io_page->footer( )->button(
38-
text = 'Save'
39-
press = client->_event( 'SAVE' ) ).
91+
#### Master-Detail with `FlexibleColumnLayout`
92+
93+
The most common real-world use: a master list on the left, detail content on the right. `sap.f.FlexibleColumnLayout` is the standard container; abap2UI5 nests the detail view into its middle column.
94+
95+
```abap
96+
" Master view — built once
97+
DATA(page) = z2ui5_cl_xml_view=>factory(
98+
)->page( title = `abap2UI5 - Master Detail` ).
99+
100+
DATA(lr_master) = page->flexible_column_layout(
101+
layout = client->_bind_edit( mv_layout )
102+
id = `test` " anchor
103+
)->begin_column_pages( ).
104+
105+
lr_master->list( items = client->_bind_edit( val = t_tab
106+
view = client->cs_view-main )
107+
selectionchange = client->_event( `SELCHANGE` )
108+
)->standard_list_item( title = `{TITLE}`
109+
selected = `{SELECTED}` ).
110+
111+
client->view_display( page->stringify( ) ).
112+
```
113+
114+
When the user picks a row, a detail view is rendered into the middle column:
115+
116+
```abap
117+
METHOD view_display_detail.
118+
DATA(lo_view_nested) = z2ui5_cl_xml_view=>factory( ).
119+
DATA(page) = lo_view_nested->page( `Nested View` ).
120+
121+
page->ui_table( rows = client->_bind_edit( val = t_tab2
122+
view = client->cs_view-nested ) ).
123+
" ...columns, toolbar, row actions...
124+
125+
client->nest_view_display(
126+
val = lo_view_nested->stringify( )
127+
id = `test`
128+
method_insert = `addMidColumnPage`
129+
method_destroy = `removeAllMidColumnPages` ).
40130
ENDMETHOD.
41131
```
42132

43-
Each helper receives the parent node by reference and attaches its controls to it. The final `stringify( )` call serialises the entire tree — including all nested children — into one XML string.
133+
The layout is bound editable (`mv_layout`), so events like *full-screen mode* or *close detail* simply update `mv_layout` and call `view_model_update` / `nest_view_model_update`. The FCL transitions itself; no view is rebuilt.
44134

45-
#### Sub-Apps
135+
End-to-end samples:
46136

47-
For larger apps it is useful to split responsibility across separate ABAP classes. Each sub-app implements `z2ui5_if_app` and manages its own piece of the view. The parent app instantiates the sub-app, passes the client, and merges the returned view fragment into its own view tree.
137+
- `Z2UI5_CL_DEMO_APP_069` — tree master, two interchangeable detail apps (`addMidColumnPage`).
138+
- `Z2UI5_CL_DEMO_APP_097` — list master, `sap.ui.table.Table` in the detail with sort/filter/row actions.
139+
- `Z2UI5_CL_DEMO_APP_085` — full master-detail with an `ObjectPageLayout` as the nested detail, including search, sort, and the FCL fullscreen toggle.
48140

49-
**Parent app:**
50-
```abap
51-
METHOD z2ui5_if_app~main.
52-
IF client->check_on_init( ).
53-
sub_app = NEW z2ui5_cl_my_sub_app( ).
54-
ENDIF.
141+
#### Routing Bindings to the Right View
55142

56-
DATA(xml) = z2ui5_cl_xml_view=>factory( ).
57-
DATA(page) = xml->shell( )->page( 'Parent' ).
143+
Each view (main, nested) has its own client-side model. When you build a binding with `client->_bind( ... )` you can declare which view's model the binding belongs to via the `view` parameter:
58144

59-
" Let the sub-app render into a container on the page
60-
DATA(container) = page->vbox( ).
61-
sub_app->render( client = client io_node = container ).
145+
```abap
146+
" In the main view
147+
items = client->_bind_edit( val = t_tab view = client->cs_view-main )
62148
63-
client->view_display( xml->stringify( ) ).
64-
ENDMETHOD.
149+
" In the nested view
150+
rows = client->_bind_edit( val = t_tab2 view = client->cs_view-nested )
65151
```
66152

67-
**Sub-app:**
153+
The constants `client->cs_view-main` and `client->cs_view-nested` are abap2UI5's view identifiers. Declaring them lets `nest_view_model_update( )` know which model to refresh:
154+
68155
```abap
69-
METHOD render.
70-
io_node->text( 'I am the sub-app' ).
71-
io_node->button(
72-
text = 'Sub Action'
73-
press = io_client->_event( 'SUB_ACTION' ) ).
74-
ENDMETHOD.
156+
DELETE t_tab2 WHERE title = ls_arg-title.
157+
client->nest_view_model_update( ). " only the nested view's data is pushed
75158
```
76159

77-
The sub-app appends its controls to whatever node the parent provides. Events fired inside the sub-app are handled by the sub-app's own `main` method on the next roundtrip.
160+
If you omit `view`, the binding defaults to the main view, which still works for many cases but is less explicit. Get into the habit of passing the right `cs_view-...` value whenever a fragment is built — it makes the data flow obvious to anyone reading the code.
78161

79-
#### Navigation Between Views
162+
#### Two Levels of Nesting
80163

81-
When the nested content changes completely — for example, a detail page replacing a list — use `view_display` to swap the entire view rather than nesting:
164+
The middle column can itself host another nested view in the end column — useful for master / detail / detail-of-detail flows. abap2UI5 exposes a second method for this level:
82165

83166
```abap
84-
CASE client->get_event( ).
85-
WHEN 'OPEN_DETAIL'.
86-
client->view_display( detail_view->stringify( ) ).
87-
WHEN 'BACK'.
88-
client->view_display( list_view->stringify( ) ).
89-
ENDCASE.
167+
METHOD view_display_detail_detail.
168+
DATA(lo_view_nested) = z2ui5_cl_xml_view=>factory( ).
169+
lo_view_nested->page( `Nested View`
170+
)->text( client->_bind( mv_title ) ).
171+
172+
client->nest2_view_display(
173+
val = lo_view_nested->stringify( )
174+
id = `test`
175+
method_insert = `addEndColumnPage`
176+
method_destroy = `removeAllEndColumnPages` ).
177+
ENDMETHOD.
90178
```
91179

92-
Reserve true nesting for content that appears side-by-side or in a stable layout shell. Use `view_display` for sequential navigation where one screen fully replaces another.
180+
`nest2_view_display` works exactly like `nest_view_display` but targets the second level — typically the FCL's *end* column. `Z2UI5_CL_DEMO_APP_098` walks through all three columns: a list selects a row, a row-action navigates to the end column, the layout switches to `ThreeColumnsEndExpanded`.
181+
182+
#### When to Use Nested Views (and When Not To)
183+
184+
| Situation | Approach |
185+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
186+
| Different visual sections that update at different rates | Nested views — re-render each piece on its own |
187+
| Master-detail, FCL columns, drill-down navigation | Nested views — the canonical use case |
188+
| A side panel that toggles open/closed but keeps the page intact | Nested views |
189+
| Building one view from helper methods (still rendered as one) | Plain ABAP composition — pass nodes between methods, no `nest_view_display` needed |
190+
| One full screen replacing another | `view_display` with the new view (or `nav_app_call` for a separate app) |
191+
192+
Plain composition is the right starting point: keep helper methods that take a parent node and add children to it. Reach for nested views once the UI has clear sub-areas that need to update independently — otherwise you pay for ceremony you don't use.
93193

94194
#### Tips
95195

96-
- Keep each helper or sub-app focused on one logical section. A helper that grows beyond ~30 lines is a candidate for its own sub-app class.
97-
- Sub-apps can hold their own instance attributes for local state. Because the whole app tree is reconstructed on every roundtrip, each sub-app re-renders from its current state automatically.
98-
- Pass only the node the sub-app needs, not the whole view root. This limits the sub-app's scope and makes the boundary explicit.
196+
- The anchor id must be unique in the main view. The framework calls `byId` on the rendered view to find it; duplicate ids break the lookup.
197+
- Always provide `method_destroy` when a nested slot will be replaced more than once. Forgetting it causes nested fragments to accumulate.
198+
- Build the nested view in its own method (e.g. `view_display_detail`) and call it both from the initial render and from event handlers. Two call sites, one definition.
199+
- If a nested view does not pick up a data change, you probably need `nest_view_model_update( )`; if a control simply isn't there, you need `nest_view_display( )` again.
200+
- For very large apps, look at `Z2UI5_CL_DEMO_APP_104`, which loads each detail screen from a separate `z2ui5_if_app` class and renders it into the nested slot. It is an advanced pattern — start with the simpler form first.
99201

100-
See `Z2UI5_CL_DEMO_APP_160` for an end-to-end example of parent and sub-app composition.
202+
See `Z2UI5_CL_DEMO_APP_065`, `Z2UI5_CL_DEMO_APP_069`, `Z2UI5_CL_DEMO_APP_085`, `Z2UI5_CL_DEMO_APP_097`, `Z2UI5_CL_DEMO_APP_098`, and `Z2UI5_CL_DEMO_APP_104` for runnable examples covering every variation above.

0 commit comments

Comments
 (0)