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
28 changes: 28 additions & 0 deletions docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export default defineConfig({
// The section is called Getting Started in the sidebar this opens;
// "Introduction" was the first PAGE in it, one level down.
{ text: "Getting Started", link: "/get_started/about" }, // nav
{ text: "Tutorials", link: "/tutorials/overview" }, // nav
{ text: "Cookbook", link: "/cookbook/overview" },
{ text: "Configuration", link: "/configuration/setup" },
// Technical Insight is not a line of its own here any more: it is a
Expand Down Expand Up @@ -192,6 +193,33 @@ export default defineConfig({
{ text: `What's Next?`, link: "/get_started/next" },
],
},
{
// Between Getting Started and the Cookbook on purpose: a tutorial is
// read after the framework is installed and before the reference
// chapters, and that is the order the three sections stand in.
text: "Tutorials",
link: "/tutorials/overview",
collapsed: true,
items: [
{ text: "Overview", link: "/tutorials/overview" }, // sidebar
{
text: "Walkthrough",
link: "/tutorials/walkthrough/overview",
collapsed: true,
items: [
{ text: "Introduction", link: "/tutorials/walkthrough/overview" }, // sidebar
{ text: "1 — Hello World", link: "/tutorials/walkthrough/step-1" },
{ text: "2 — A First View", link: "/tutorials/walkthrough/step-2" },
{ text: "3 — Events", link: "/tutorials/walkthrough/step-3" },
{ text: "4 — Data Binding", link: "/tutorials/walkthrough/step-4" },
{ text: "5 — List Binding", link: "/tutorials/walkthrough/step-5" },
{ text: "6 — Row Events", link: "/tutorials/walkthrough/step-6" },
{ text: "7 — Popups", link: "/tutorials/walkthrough/step-7" },
{ text: "8 — App Structure", link: "/tutorials/walkthrough/step-8" },
],
},
],
},
{
text: "Cookbook",
link: "/cookbook/overview",
Expand Down
Binary file added docs/public/tutorials/walkthrough-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions docs/tutorials/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
outline: [2, 4]
description: Learn abap2UI5 by building — step-by-step tutorials where every step is a complete class you can run in the browser.
---
# Tutorials

Learn abap2UI5 by building something. A tutorial is a sequence of steps, and
every step is one **complete class**: run it directly under its code with the
Run button, or copy it into your system and start it like any abap2UI5 app
(see the [Quickstart](/get_started/quickstart)).

## Learning Path

| Tutorial | Type | Content |
|---|---|---|
| [Walkthrough](/tutorials/walkthrough/overview) | Foundation | Builds a small invoice app from scratch, one step at a time — the app class and its lifecycle, views built in ABAP, events, data binding, lists, popups, and the structure real apps use. |

More tutorials — deep dives into single topics — will follow. Until then, the
[Cookbook](/cookbook/overview) covers each topic as a reference chapter.

## After the Tutorials

Three catalogues of complete, tested apps continue where the tutorials stop:

- [Samples](https://abap2ui5.github.io/samples/) — one app per pattern, along a guided learning path
- [Controls](https://abap2ui5.github.io/samples-controls/) — UI5 demo kit samples rebuilt as abap2UI5 apps, searchable by control
- [Stack](https://abap2ui5.github.io/samples-stack/) — integration samples per technology, from RAP to WebSocket

And two pages of this site compress the walkthrough for readers in a hurry:
[Hello World](/get_started/hello_world) covers the first steps on a single
page, the [Full Example](/get_started/full_example) builds a realistic
selection-screen app on top of them.
42 changes: 42 additions & 0 deletions docs/tutorials/walkthrough/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
outline: [2, 4]
description: Build a small invoice app from scratch in eight steps — every step a complete, runnable abap2UI5 class.
---
# Walkthrough

In this tutorial we build a small invoice app from scratch and meet every
paradigm an abap2UI5 app is made of along the way: the app class and its
lifecycle, views written in ABAP, events, data binding, lists and popups.

## Preview

![The finished walkthrough app: a list of invoices with supplier and quantity, and a dialog editing the quantity of one row](/tutorials/walkthrough-preview.png)

This is the app after the last step — about a hundred lines of ABAP, no
frontend project, no OData service.

Each step is a **complete, runnable class** — the whole app as it stands at
that point, not a fragment. Press the Run button under the code to start it in
the browser, or copy the class into your system and launch it like any
abap2UI5 app (see the [Quickstart](/get_started/quickstart)). No step depends
on anything outside its own code, so you can also jump straight to the step
you are interested in.

## Steps

- **[Step 1: Hello World](/tutorials/walkthrough/step-1)** — the smallest possible app: one class, one method, one message.
- **[Step 2: A First View](/tutorials/walkthrough/step-2)** — render a UI5 view built entirely in ABAP.
- **[Step 3: Events](/tutorials/walkthrough/step-3)** — a button, a press event, and the lifecycle behind them.
- **[Step 4: Data Binding](/tutorials/walkthrough/step-4)** — an input field whose value reaches the server by itself.
- **[Step 5: List Binding](/tutorials/walkthrough/step-5)** — show an internal table as a UI5 list.
- **[Step 6: Row Events](/tutorials/walkthrough/step-6)** — react to a click on a row, and know which row it was.
- **[Step 7: Popups](/tutorials/walkthrough/step-7)** — edit a row in a dialog.
- **[Step 8: App Structure](/tutorials/walkthrough/step-8)** — refactor into the structure real apps use.

## What You Should Know

No prior abap2UI5 or UI5 experience is needed — the tutorial introduces every
concept as it appears. Basic ABAP (classes, methods, internal tables) is
assumed. If you want the framework installed in your own system first, do the
[Quickstart](/get_started/quickstart) — but the Run button works without any
installation at all.
43 changes: 43 additions & 0 deletions docs/tutorials/walkthrough/step-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
outline: [2, 4]
description: The smallest possible abap2UI5 app — one class, one method, one message.
---
# Step 1: Hello World

Every abap2UI5 app is one ABAP class implementing the interface
`z2ui5_if_app`. That interface has a single method, `main`, and the framework
calls it with one parameter: `client`, your only API. This is the smallest app
that can exist:

```abap
CLASS zcl_app_walkthrough DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES z2ui5_if_app.
ENDCLASS.

CLASS zcl_app_walkthrough IMPLEMENTATION.
METHOD z2ui5_if_app~main.
client->message_box_display( `Hello World` ).
ENDMETHOD.
ENDCLASS.
```

Press **Run this example** under the code — the class starts right here in
the browser. To run it in your own system instead, copy it in, open the
abap2UI5 startup page (see the [Quickstart](/get_started/quickstart)), and
enter the class name.

## What Just Happened

- **No app project, no OData service, no frontend artifact.** The class *is*
the app. abap2UI5 follows a thin-frontend model: the browser only renders,
while all logic, state and data stay in ABAP on the server.
- **`main` runs on every roundtrip.** The framework calls it when the app
starts and again after every user interaction. Right now every call shows
the same message box; from [Step 3](/tutorials/walkthrough/step-3) on we
will tell the calls apart.
- **`client` is the whole API.** Displaying views and messages, reacting to
events, binding data — everything in this tutorial goes through this one
object.

Next, we replace the message box with a real UI5 view.
62 changes: 62 additions & 0 deletions docs/tutorials/walkthrough/step-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
outline: [2, 4]
description: Render a UI5 XML view built entirely in ABAP with the view builder.
---
# Step 2: A First View

A message box is not much of a UI. UI5 apps describe their screens as XML
views — and in abap2UI5 you write that view in ABAP, with
`z2ui5_cl_ui5_view_builder`:

```abap
CLASS zcl_app_walkthrough DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES z2ui5_if_app.
ENDCLASS.

CLASS zcl_app_walkthrough IMPLEMENTATION.
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( `Shell`
)->ele( `Page`
)->a( n = `title` v = `Walkthrough - Step 2`

)->tag( `Text`
)->a( n = `text` v = `Hello World` ).

client->view_display( view->stringify( ) ).

ENDMETHOD.
ENDCLASS.
```

## The Four Verbs

You are writing a UI5 XML view, one control per call. The builder has four
verbs and no list of controls to look up — every UI5 control, property and
aggregation is available, because the builder never knew any of them by name:

| | |
| --- | --- |
| `ele( )` | add a control and **descend** into it — for a container |
| `tag( )` | add a control and **stay** — for a leaf |
| `a( )` | set **one** attribute on the control the chain points at |
| `end( )` | ascend to the parent |

The single rule: `a( )` applies to the control the chain currently points at,
so attributes follow their control — and a control gets them *before* its
first child. The root `mvc:View` and its `xmlns` declarations are written by
hand, exactly as in a real UI5 view. `stringify( )` renders the XML from the
root, and `view_display( )` sends it to the browser.

The indentation mirrors the XML tree — `Text` sits inside `Page` inside
`Shell` — which is what makes the chain readable as the view it builds. The
full layout rules are on the [View → Definition](/cookbook/view/definition)
page.

Next, the app gets its first button — and with it, the app lifecycle.
77 changes: 77 additions & 0 deletions docs/tutorials/walkthrough/step-3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
outline: [2, 4]
description: A button, a press event, and the abap2UI5 lifecycle behind them.
---
# Step 3: Events

The framework calls `main` on **every** roundtrip — on the initial start and
again after each user interaction. As soon as an app reacts to input, `main`
has to tell those calls apart. That job falls to the lifecycle checks, and
`main` becomes a dispatcher:

```abap
CLASS zcl_app_walkthrough DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES z2ui5_if_app.
ENDCLASS.

CLASS zcl_app_walkthrough IMPLEMENTATION.
METHOD z2ui5_if_app~main.

IF client->check_on_navigated( ).

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( `Shell`
)->ele( `Page`
)->a( n = `title` v = `Walkthrough - Step 3`

)->tag( `Text`
)->a( n = `text` v = `Hello World`
)->tag( `Button`
)->a( n = `text` v = `Say Hello`
)->a( n = `press` v = client->_event( `SAY_HELLO` ) ).

client->view_display( view->stringify( ) ).

ELSEIF client->check_on_event( `SAY_HELLO` ).

client->message_toast_display( `Hello from abap2UI5!` ).

ENDIF.

ENDMETHOD.
ENDCLASS.
```

## The Lifecycle

```text
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Browser │──────>│ main() │──────>│ Browser │
│ (Start) │ HTTP │ view │ HTTP │ (View) │
└──────────┘ └──────────┘ └────┬─────┘
│ user clicks
┌──────────┐ ┌──────────┐ ┌────┴─────┐
│ Browser │<──────│ main() │<──────│ Browser │
│ (Toast) │ HTTP │ event │ HTTP │ (Event) │
└──────────┘ └──────────┘ └──────────┘
```

- **`check_on_navigated( )`** is true when the app has to draw its screen —
on the first start, and again whenever the user navigates back to it later.
This branch displays the view.
- **`check_on_event( )`** is true when the user triggered an event. The
argument names which one.
- ``client->_event( `SAY_HELLO` )`` wires the button: it returns the press
handler that sends the event — with the name you chose — back to `main`.

Each `check_*` method is true only for its own phase, so the `IF`/`ELSEIF`
chain cleanly dispatches every roundtrip. The full picture is on the
[Life Cycle](/cookbook/event_navigation/life_cycle) page.

Next: data leaves the browser and reaches your class — without a single line
of transfer code.
67 changes: 67 additions & 0 deletions docs/tutorials/walkthrough/step-4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
outline: [2, 4]
description: Bind an input field to a class attribute — data reaches the server by itself.
---
# Step 4: Data Binding

So far the app only talks. Now the user answers: an input field replaces the
text, and whatever is typed into it arrives in your class — without a single
line of transfer code:

```abap
CLASS zcl_app_walkthrough DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES z2ui5_if_app.
DATA recipient TYPE string.
ENDCLASS.

CLASS zcl_app_walkthrough IMPLEMENTATION.
METHOD z2ui5_if_app~main.

IF client->check_on_navigated( ).

recipient = `World`.

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( `Shell`
)->ele( `Page`
)->a( n = `title` v = `Walkthrough - Step 4`

)->tag( `Input`
)->a( n = `value` v = client->_bind( recipient )
)->tag( `Button`
)->a( n = `text` v = `Say Hello`
)->a( n = `press` v = client->_event( `SAY_HELLO` ) ).

client->view_display( view->stringify( ) ).

ELSEIF client->check_on_event( `SAY_HELLO` ).

client->message_toast_display( |Hello { recipient }!| ).

ENDIF.

ENDMETHOD.
ENDCLASS.
```

## How the Value Travels

- **`DATA recipient TYPE string`** — a public attribute is the model. The
framework serializes public attributes after every roundtrip and restores
them before the next one, so your class keeps its state without any session
handling. The attribute must be in the `PUBLIC SECTION`: the framework
reads it dynamically and silently ignores private or protected ones (full
rules on the [Binding](/cookbook/model/binding) page).
- **`client->_bind( recipient )`** connects the attribute to the `value`
property of the input. When the button fires, the browser sends the current
screen state along with the event — by the time your `ELSEIF` branch runs,
`recipient` already holds what the user typed.
- **Type it, press the button** — the toast greets whatever name is in the
field. Two-way, and you wrote no transfer code.

Next, the same binding moves a whole internal table into a list.
Loading