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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Full author docs are in [CONTRIBUTING.md](CONTRIBUTING.md).
Other conventions:

- Callouts use `> :note:`, `> :tip:`, `> :info:` (custom syntax, not GitHub's `[!NOTE]` alerts).
- Step navigation footer: `***` separator, then `**Next:** [Step N+1: <title>](../<NN+1>/README.md)` and `**Previous:** [Step N-1: <title>](../<NN-1>/README.md)`.
- Step navigation footer: a single `***` separator, then `**Next:** [Step N+1: <title>](../<NN+1>/README.md)` and `**Previous:** [Step N-1: <title>](../<NN-1>/README.md)`. When a step also has a **Related Information** section, the order is Next/Previous first, then another `***`, then `**Related Information**` (matches all five tutorials). The first step has no `**Previous:**`; the last step has no `**Next:**`. The link display text must match the target step's actual title.
- Preview images: descriptive `alt` text (no bare `![](…)`), optional `"title"` for the hover tooltip.
- Do not add `<!-- loio… -->` markers or `<a name="loio…"/>` anchors — those are SAP-internal SDK artifacts.

Expand Down
32 changes: 32 additions & 0 deletions assets/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,36 @@ li > section.ts-only, li > section.js-only {
#__bs_notify__ {
background-color: rgba(27, 32, 50, 0.3) !important;
top: 40px !important;
}

/* Image captions rendered from each image's alt text (see renderImageCaptions
in assets/js/custom.js). Restores the SAP Demo Kit behavior of showing the
image description below the figure. */
figure.img-figure {
margin: 16px 0;
}

figure.img-figure > .img-caption {
margin-top: 6px;
font-size: 90%;
font-style: italic;
color: var(--inactive-color);
text-align: center;
}

/* Breadcrumb shown at the top of each step page: "UI5 Tutorials › <Tutorial>".
Injected server-side (tools/builder/prepare-gh-pages.js for GitHub Pages,
tools/dev-server/server.js for local preview) so the reader knows which
tutorial they are in and can jump back to its overview. */
.tutorial-breadcrumb {
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid var(--neutral-border);
font-size: 90%;
color: var(--inactive-color);
}

.tutorial-breadcrumb .tutorial-breadcrumb-sep {
margin: 0 6px;
color: var(--inactive-color);
}
55 changes: 55 additions & 0 deletions assets/js/custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,60 @@ function updateAllCodeCouples(globalLang) {

// initialization on startup

/**
* Render each content image's `alt` text as a visible caption beneath it,
* mirroring the SAP Demo Kit behavior that was lost when the tutorials moved
* to GitHub Pages. The caption text lives only in the image's `alt` attribute
* (also used by screen readers and as the broken-image fallback), so there is
* a single source of truth.
*
* Idempotent: an image already wrapped in <figure class="img-figure"> is
* skipped, so re-running (e.g. after a language switch) does not duplicate.
*/
function renderImageCaptions() {
const images = document.querySelectorAll("img");
images.forEach((img) => {
const caption = (img.getAttribute("alt") || "").trim();
if (!caption) {
return; // no alt text -> no caption
}
if (img.closest("figure.img-figure")) {
return; // already processed
}

// A link-wrapped image is captioned via its anchor; otherwise the image
// itself is the wrap target.
const wrapTarget =
img.parentElement && img.parentElement.tagName === "A"
? img.parentElement
: img;
const host = wrapTarget.parentElement;
if (!host) {
return;
}

// Only caption block-level "preview" images — those that are the sole
// meaningful content of their containing block. This skips inline
// images sitting inside a line of prose (badges, inline icons), which
// should not get a centered caption. `host.textContent` is empty for a
// standalone image because the <img> contributes no text.
if (host.textContent.trim() !== "") {
return;
}

const figure = document.createElement("figure");
figure.classList.add("img-figure");

const figcaption = document.createElement("figcaption");
figcaption.classList.add("img-caption");
figcaption.textContent = caption;

wrapTarget.parentNode.insertBefore(figure, wrapTarget);
figure.appendChild(wrapTarget);
figure.appendChild(figcaption);
});
}

document.addEventListener("DOMContentLoaded", (event) => {
setTimeout(() => {
const lang = initializeLanguage();
Expand All @@ -285,5 +339,6 @@ document.addEventListener("DOMContentLoaded", (event) => {
addLanguageSwitchButtons();
updateAllCodeCouples(lang);
updatePrevNextLinks(lang);
renderImageCaptions();
});
});
3 changes: 0 additions & 3 deletions packages/navigation/steps/01/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,6 @@ With the downloaded coding, you have an initial app with recommended settings th

So far we have a basic app that does not really have any navigation or routing implemented. This will change in the next steps when we implement our first navigation features.

***


***

**Next:** [Step 2: Enable Routing](../02/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/02/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,6 @@ Create a file `Home.controller.ts` in the `webapp/controller` folder. The contro

***

***

**Next:** [Step 3: Catch Invalid Hashes](../03/README.md)

**Previous:** [Step 1: Set Up the Initial App](../01/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/03/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,6 @@ Open the URL `index.html#/thisIsInvalid` in your browser. From now on the user w

***

***

**Next:** [Step 4: Add a *Back* Button to *Not Found* Page](../04/README.md)

**Previous:** [Step 2: Enable Routing](../02/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/04/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ The same applies to our `Home` controller, we now also inherit from the `BaseCon

***

***

**Next:** [Step 5: Display a Target Without Changing the Hash](../05/README.md)

**Previous:** [Step 3: Catch Invalid Hashes](../03/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/05/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,6 @@ When we now click the *Back* button, it works as expected and brings us back to

***

***

**Next:** [Step 6: Navigate to Routes with Hard-Coded Patterns](../06/README.md)

**Previous:** [Step 4: Add a *Back* Button to *Not Found* Page](../04/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/06/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,6 @@ Now you can open the app and press the *Show Employee List* button to navigate t

***

***

**Next:** [Step 7: Navigate to Routes with Mandatory Parameters](../07/README.md)

**Previous:** [Step 5: Display a Target Without Changing the Hash](../05/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/07/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,6 @@ That’s it. You can go to `webapp/index.html#/employees` and click on any list

***

***

**Next:** [Step 8: Navigate with Flip Transition](../08/README.md)

**Previous:** [Step 6: Navigate to Routes with Hard-Coded Patterns](../06/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/08/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,6 @@ You can go to `webapp/index.html#/employees/3` and click on the *Flip to Resume*

***

***

**Next:** [Step 9: Allow Bookmarkable Tabs with Optional Query Parameters](../09/README.md)

**Previous:** [Step 7: Navigate to Routes with Mandatory Parameters](../07/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/09/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,6 @@ When you click on any tab you will see that the hash in the URL changes immediat

***

***

**Next:** [Step 10: Implement “Lazy Loading”](../10/README.md)

**Previous:** [Step 8: Navigate with Flip Transition](../08/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/10/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,6 @@ Try it out yourself: Open the *Network* tab of your browser's developer tools an

***

***

**Next:** [Step 11: Assign Multiple Targets](../11/README.md)

**Previous:** [Step 9: Allow Bookmarkable Tabs with Optional Query Parameters](../09/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/11/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -567,8 +567,6 @@ Add the new texts to the `properties` file.

***

***

**Next:** [Step 12: Make a Search Bookmarkable](../12/README.md)

**Previous:** [Step 10: Implement “Lazy Loading”](../10/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/12/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,6 @@ When you change the value in the search field, you see that the hash updates acc

***

***

**Next:** [Step 13: Make Table Sorting Bookmarkable](../13/README.md)

**Previous:** [Step 11: Assign Multiple Targets](../11/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/13/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,6 @@ When changing the table’s sorting options, you will see that the hash updates

***

***

**Next:** [Step 14: Make Dialogs Bookmarkable](../14/README.md)

**Previous:** [Step 12: Make a Search Bookmarkable](../12/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/14/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,6 @@ As you can see, the dialog opens automatically if the parameter `showDialog=1` i

***

***

**Next:** [Step 15: Reuse an Existing Route](../15/README.md)

**Previous:** [Step 13: Make Table Sorting Bookmarkable](../13/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/15/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,6 @@ Next we add the `itemPress` handler `.onItemPressed` to the `EmployeeOverviewCon

***

***

**Next:** [Step 16: Handle Invalid Hashes by Listening to Bypassed Events](../16/README.md)

**Previous:** [Step 14: Make Dialogs Bookmarkable](../14/README.md)
2 changes: 0 additions & 2 deletions packages/navigation/steps/16/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ Now try to access `webapp/index.html#/thisIsInvalid` while you have your browser

***

***

**Next:** [Step 17: Listen to Matched Events of Any Route](../17/README.md)

**Previous:** [Step 15: Reuse an Existing Route](../15/README.md)
10 changes: 6 additions & 4 deletions packages/odatav4/steps/01/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,16 @@ To be able to add data to the emulated OData responses, we have to store the ent

In this tutorial, we only use the entity type `Person` of the *TripPin* service. The entities of type `Person` are collected in the entity set `People`. Each `Person` has a key property `UserName` and the properties `Age`, `FirstName`, and `LastName`.

***

**Next:** [Step 2: Data Access and Client-Server Communication](../02/README.md)

***

**Related Information**

[*OData* Reference Services including *TripPin*](http://www.odata.org/odata-services/)

[Bootstrapping: Loading and Initializing](https://sdk.openui5.org/topic/a04b0d10fb494d1cb722b9e341b584ba.html "To use OpenUI5 features in your HTML page, you have to load and initialize the OpenUI5 library.")

[Descriptor for Applications, Components, and Libraries \(manifest.json\)](https://sdk.openui5.org/topic/be0cf40f61184b358b5faedaec98b2da.html "The descriptor for applications, components, and libraries (in short: app descriptor) is inspired by the WebApplication Manifest concept introduced by the W3C. The descriptor provides a central, machine-readable, and easy-to-access location for storing metadata associated with an application, an application component, or a library.")

***

**Next:** [Step 2: Data Access and Client-Server Communication](../02/README.md)
18 changes: 9 additions & 9 deletions packages/odatav4/steps/02/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ In this step, we see how the `Table` that is bound to the `People` entity set in

**App with a toolbar that contains a Refresh button**

![App with a toolbar that contains a Refresh
button](assets/Tutorial_OData_V4_Step_2_0abcbb6.png "App with a toolbar that contains a Refresh
button")
![App with a toolbar that contains a Refresh button](assets/Tutorial_OData_V4_Step_2_0abcbb6.png "App with a toolbar that contains a Refresh button")

You can view this step live: [🔗 Live Preview of Step 2](https://ui5.github.io/tutorials/odatav4/build/02/index-cdn.html).

Expand Down Expand Up @@ -225,6 +223,14 @@ We search for the following mock server requests:

This request explicitly lists the fields that should be included in the response by using the `$select` query option. Although the *TripPin* service has more fields in its `People` entity set, only those four are included in the response. This is a feature of the OData V4 Model called "automatic determination of `$select`", or "auto-`$select`". It helps restricting the size of responses to what is really needed. The `ODataModel` computes the required fields from binding paths specified for controls. This feature is not active by default. In our case, this is activated by setting the `autoExpandSelect` property to `true` when instantiating the model in the `manifest.json` descriptor file .

***

**Next:** [Step 3: Automatic Data Type Detection](../03/README.md)

**Previous:** [Step 1: The Initial App](../01/README.md)

***

**Related Information**

[Bindings](https://sdk.openui5.org/topic/54e0ddf695af4a6c978472cecb01c64d "Bindings connect OpenUI5 view elements to model data, allowing changes in the model to be reflected in the view element and vice versa.")
Expand All @@ -234,9 +240,3 @@ We search for the following mock server requests:
[API Reference: `sap.ui.model.odata.v4.ODataListBinding.refresh`](https://sdk.openui5.org/#/api/sap.ui.model.odata.v4.ODataListBinding/methods/refresh)

[Troubleshooting Tutorial Step 1: Browser Developer Tools](step-1-browser-developer-tools-eadd60a.md "In this step, you will learn how to use your browser's developers tools to troubleshoot your OpenUI5 app.")

***

**Next:** [Step 3: Automatic Data Type Detection](../03/README.md)

**Previous:** [Step 1: The Initial App](../01/README.md)
14 changes: 8 additions & 6 deletions packages/odatav4/steps/03/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,18 @@ To make the *User Name* optional, we remove the parameter `Nullable="false"` fro
> 💡
> To see the metadata of an OData service, you append the `$metadata` variable to the URL of the service. You can try this, for example, with [http://services.odata.org/TripPinRESTierService/](http://services.odata.org/TripPinRESTierService/) and [http://services.odata.org/TripPinRESTierService/$metadata](http://services.odata.org/TripPinRESTierService/$metadata)

***

**Next:** [Step 4: Filtering, Sorting, and Counting](../04/README.md)

**Previous:** [Step 2: Data Access and Client-Server Communication](../02/README.md)

***

**Related Information**

[Type Determination](https://sdk.openui5.org/topic/53cdd55a77ce4f33a14bd0767a293063 "")

[API Reference: `sap.ui.model.odata.type`](https://sdk.openui5.org/#/api/sap.ui.model.odata.type)

[Sample for `sap.ui.core.mvc.XMLView`: *XML Templating: UI5 OData types*](https://sdk.openui5.org/#/entity/sap.ui.core.mvc.XMLView/sample/sap.ui.core.sample.ViewTemplate.types)

***

**Next:** [Step 4: Filtering, Sorting, and Counting](../04/README.md)

**Previous:** [Step 2: Data Access and Client-Server Communication](../02/README.md)
18 changes: 9 additions & 9 deletions packages/odatav4/steps/04/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ In this step, we add features to filter, sort, and count the user data by using

**App now has a search field, the entries can be sorted, and you can see how many entities are loaded and how many more are available**

![App now has a search field, the entries can be sorted, and you can see how
many entities are loaded and how many more are available](assets/Tutorial_OData_V4_Step_4_3ac4fcc.png "App now has a search field, the entries can be sorted, and you can see how
many entities are loaded and how many more are available")
![App now has a search field, the entries can be sorted, and you can see how many entities are loaded and how many more are available](assets/Tutorial_OData_V4_Step_4_3ac4fcc.png "App now has a search field, the entries can be sorted, and you can see how many entities are loaded and how many more are available")

You can view this step live: [🔗 Live Preview of Step 4](https://ui5.github.io/tutorials/odatav4/build/04/index-cdn.html).

Expand Down Expand Up @@ -277,16 +275,18 @@ sortNone=the sequence on the server

We add the missing texts to the properties file.

***

**Next:** [Step 5: Batch Groups](../05/README.md)

**Previous:** [Step 3: Automatic Data Type Detection](../03/README.md)

***

**Related Information**

[Filtering](https://sdk.openui5.org/topic/5338bd1f9afb45fb8b2af957c3530e8f "The OData V4 Model supports server side filtering on lists.")

[Sorting](https://sdk.openui5.org/topic/d2ce3f51e5e34198b0c1a7f6ddd98def "The OData V4 model supports server side sorting on lists.")

[Query Options under *Querying Data* in the Basic Tutorial on the OData home page](http://www.odata.org/getting-started/basic-tutorial/#queryData)

***

**Next:** [Step 5: Batch Groups](../05/README.md)

**Previous:** [Step 3: Automatic Data Type Detection](../03/README.md)
12 changes: 7 additions & 5 deletions packages/odatav4/steps/05/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ A `$batch` request uses multipart MIME to put several requests into one. This ma

- Copy the relevant part of the request or response from the developer tools to an editor and auto-format it as JSON to analyze it.

**Related Information**
***

[Batch Control](https://sdk.openui5.org/topic/74142a38e3d4467c8d6a70b28764048f "OData V4 allows you to group multiple operations into a single HTTP request payload, as described in the official OData V4 specification Part 1, Batch Requests (see the link under Related Information for more details).")
**Next:** [Step 6: Create and Edit](../06/README.md)

[Performance Aspects](https://sdk.openui5.org/topic/5a0d286c5606424b8e0d663c87445733 "The OData V4 model offers the features described below which influence performance.")
**Previous:** [Step 4: Filtering, Sorting, and Counting](../04/README.md)

***

**Next:** [Step 6: Create and Edit](../06/README.md)
**Related Information**

**Previous:** [Step 4: Filtering, Sorting, and Counting](../04/README.md)
[Batch Control](https://sdk.openui5.org/topic/74142a38e3d4467c8d6a70b28764048f "OData V4 allows you to group multiple operations into a single HTTP request payload, as described in the official OData V4 specification Part 1, Batch Requests (see the link under Related Information for more details).")

[Performance Aspects](https://sdk.openui5.org/topic/5a0d286c5606424b8e0d663c87445733 "The OData V4 model offers the features described below which influence performance.")
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading