From f3d8a140c37ac61cc7a4cddf778e6e97a7e21702 Mon Sep 17 00:00:00 2001 From: Peter Muessig Date: Thu, 9 Jul 2026 15:23:43 +0200 Subject: [PATCH 1/4] docs(tutorials): rework TS/JS download links and add AGENTS.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework how JS/TS download links are structured across all step READMEs. The old paired `
` wrappers rendered as noisy collapsibles in the raw-markdown view (and to AI tools indexing the corpus) and hid the "other language exists" signal. Replace them with inline ` (TS|JS)`: both variants read plainly in raw markdown with distinct (TS)/(JS) labels, while the rendered site still shows only the active language. Achieved with no runtime JS change — the existing `.ts-only`/`.js-only` CSS hides the inactive span and a new `.lang-suffix { display: none }` rule drops the marker. Covers the per-step block form, the overview inline form, and the two standalone-bundle links in navigation/odatav4 step 01. Also add AGENTS.md (repo guide for AI agents: layout, conventions, propagation rules, using the tutorials as a codegen source), document the new `` pattern in AGENTS.md and CONTRIBUTING.md, and refresh the remaining README normalization plus packages/*/steps.json. The one-off migration script lives under the gitignored `_/` folder. --- AGENTS.md | 128 +++++++++ CONTRIBUTING.md | 50 +++- assets/css/custom.css | 8 + assets/js/custom.js | 8 +- packages/navigation/README.md | 36 ++- packages/navigation/steps.json | 159 +++++++++++ packages/navigation/steps/01/README.md | 21 +- packages/navigation/steps/02/README.md | 20 +- packages/navigation/steps/03/README.md | 18 +- packages/navigation/steps/04/README.md | 32 ++- packages/navigation/steps/05/README.md | 14 +- packages/navigation/steps/06/README.md | 26 +- packages/navigation/steps/07/README.md | 28 +- packages/navigation/steps/08/README.md | 30 +- packages/navigation/steps/09/README.md | 6 +- packages/navigation/steps/10/README.md | 6 +- packages/navigation/steps/11/README.md | 14 +- packages/navigation/steps/12/README.md | 6 +- packages/navigation/steps/13/README.md | 6 +- packages/navigation/steps/14/README.md | 4 +- packages/navigation/steps/15/README.md | 6 +- packages/navigation/steps/16/README.md | 6 +- packages/navigation/steps/17/README.md | 6 +- packages/odatav4/README.md | 22 +- packages/odatav4/steps.json | 105 +++++++ packages/odatav4/steps/01/README.md | 43 ++- packages/odatav4/steps/02/README.md | 21 +- packages/odatav4/steps/03/README.md | 18 +- packages/odatav4/steps/04/README.md | 23 +- packages/odatav4/steps/05/README.md | 20 +- packages/odatav4/steps/06/README.md | 40 +-- packages/odatav4/steps/07/README.md | 18 +- packages/odatav4/steps/08/README.md | 22 +- packages/odatav4/steps/09/README.md | 18 +- packages/odatav4/steps/10/README.md | 18 +- packages/odatav4/steps/11/README.md | 14 +- packages/quickstart/README.md | 6 +- packages/quickstart/steps.json | 33 +++ packages/quickstart/steps/01/README.md | 18 +- packages/quickstart/steps/02/README.md | 20 +- packages/quickstart/steps/03/README.md | 16 +- packages/walkthrough/README.md | 76 +++--- packages/walkthrough/steps.json | 348 ++++++++++++++++++++++++ packages/walkthrough/steps/01/README.md | 14 +- packages/walkthrough/steps/02/README.md | 19 +- packages/walkthrough/steps/03/README.md | 18 +- packages/walkthrough/steps/04/README.md | 20 +- packages/walkthrough/steps/05/README.md | 22 +- packages/walkthrough/steps/06/README.md | 18 +- packages/walkthrough/steps/07/README.md | 18 +- packages/walkthrough/steps/08/README.md | 18 +- packages/walkthrough/steps/09/README.md | 36 ++- packages/walkthrough/steps/10/README.md | 20 +- packages/walkthrough/steps/11/README.md | 14 +- packages/walkthrough/steps/12/README.md | 14 +- packages/walkthrough/steps/13/README.md | 14 +- packages/walkthrough/steps/14/README.md | 14 +- packages/walkthrough/steps/15/README.md | 24 +- packages/walkthrough/steps/16/README.md | 18 +- packages/walkthrough/steps/17/README.md | 18 +- packages/walkthrough/steps/18/README.md | 14 +- packages/walkthrough/steps/19/README.md | 14 +- packages/walkthrough/steps/20/README.md | 18 +- packages/walkthrough/steps/21/README.md | 14 +- packages/walkthrough/steps/22/README.md | 20 +- packages/walkthrough/steps/23/README.md | 18 +- packages/walkthrough/steps/24/README.md | 14 +- packages/walkthrough/steps/25/README.md | 14 +- packages/walkthrough/steps/26/README.md | 28 +- packages/walkthrough/steps/27/README.md | 32 +-- packages/walkthrough/steps/28/README.md | 36 ++- packages/walkthrough/steps/29/README.md | 16 +- packages/walkthrough/steps/30/README.md | 22 +- packages/walkthrough/steps/31/README.md | 22 +- packages/walkthrough/steps/32/README.md | 18 +- packages/walkthrough/steps/33/README.md | 25 +- packages/walkthrough/steps/34/README.md | 14 +- packages/walkthrough/steps/35/README.md | 22 +- packages/walkthrough/steps/36/README.md | 22 +- packages/walkthrough/steps/37/README.md | 14 +- packages/walkthrough/steps/38/README.md | 14 +- tools/builder/prepare-gh-pages.js | 4 + 82 files changed, 1357 insertions(+), 914 deletions(-) create mode 100644 AGENTS.md create mode 100644 packages/navigation/steps.json create mode 100644 packages/odatav4/steps.json create mode 100644 packages/quickstart/steps.json create mode 100644 packages/walkthrough/steps.json diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..a13946bd7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,128 @@ +# Repository Guide for AI Agents + +This repository ships four OpenUI5 tutorials rendered as a GitHub Pages site with a client-side JavaScript / TypeScript language toggle. Every tutorial step is a self-contained, runnable UI5 app under `packages//steps/NN/`. This document tells an agent enough about the layout, the toolchain, and the authoring conventions to be useful without re-deriving everything from source. + +## Layout + +- `packages/quickstart/` — 3 introductory steps. +- `packages/walkthrough/` — 38 steps; the canonical reference for content shape and toolchain. +- `packages/navigation/` — 17 steps on routing and navigation. +- `packages/odatav4/` — 11 steps on OData V4. +- `packages//README.md` — the tutorial's overview and step index. +- `packages//steps.json` — machine-readable index of the tutorial's steps (`n`, `id`, `title`, `description`, `previewUrl`, `zipTs`, `zipJs`). Regenerate with `node _/generate-steps-index.js` after adding, removing, or renaming steps. +- `packages//steps/NN/` — one runnable app per step. Contains `README.md`, `webapp/`, `package.json`, `ui5.yaml`, and (for TS steps) `tsconfig.json`. +- `assets/` — CSS and JS that power the site (language toggle, code-couple tab container, `.ts/.js` extension substitution). Ships as-is to `dist/`. +- `tools/builder/` — build orchestrator (`node tools/builder`) that produces `dist/` for GitHub Pages: transpiles TS steps to JS, packages ZIP downloads, renders READMEs. +- `tools/dev-server/` — dev server (`npm start`) that mirrors the built site with live reload. +- `_/` — **gitignored** folder for one-off migration and maintenance scripts. Add throwaway tooling here; do not check it in as permanent tooling. +- `_includes/head-custom.html` — Jekyll partial that injects `assets/css/custom.css` and `assets/js/custom.js` into every GitHub Pages README render. +- `dist/` — build output, gitignored. + +## Running the code + +The repo is an npm workspaces monorepo. Each `packages/*/steps/*` is a workspace. + +```sh +# one-time +npm install + +# build every tutorial step + assemble dist/ +npm run build + +# preview the rendered site with working local live-preview links +npm start # http://localhost:1337/packages/walkthrough/ + +# run one step directly +npm start -w ui5.tutorial.walkthrough.step07 +npm start -w ui5.tutorial.odatav4.step03 +# etc. + +# typecheck every TypeScript step +npm run typecheck +``` + +Rely on `npm start -w ` when you need to see a specific step in a browser — the root dev server is heavier and only needed for site-level checks. + +## Tutorial namespace and package naming + +All four tutorials share one convention: + +- App namespace: `ui5.tutorial.` (used in `manifest.json` `sap.app.id`, `Controller` names, XML `controllerName`, `index.html` resource-roots, and JSDoc `@namespace`). +- Per-step `package.json` name: `ui5.tutorial..stepNN` (zero-padded). +- `ui5.yaml` `metadata.name`: `ui5.tutorial.`. +- tsconfig `paths`: `"ui5/tutorial//*": ["./webapp/*"]`. + +If you touch any of these, keep the naming aligned across the file set. + +## Step README authoring conventions + +Step READMEs are markdown rendered through GitHub Pages + a small client-side runtime in [assets/js/custom.js](assets/js/custom.js). Three conventions matter: + +1. **Language-specific prose blocks.** Wrap TS-only content in `
` (mirror for `js-only`). The runtime converts these to `
` elements and CSS hides the inactive one. This is for *block-level* prose only — for a paired inline TS/JS link (e.g. a download-solution link), use `` instead (see point 5). +2. **Adjacent ts/js code fences.** Write the TypeScript snippet in a `` ```ts `` fence and the JavaScript snippet in a `` ```js `` fence directly below (only whitespace between them). The runtime wraps the pair in a tabbed container. +3. **First-line path comments.** Every ts/js fence pair whose content is real source code must start with a `// path/to/File.ts` comment on line 1 (mirror `.js`). This is what tells a human reader — and an AI indexing the corpus — which file the snippet belongs to. For XML fences use ``; for INI/properties use `# path`. +4. **File extensions in prose and headings.** Write `.ts/.js` where the current language should substitute. The runtime renders it as `.ts` in TS mode or `.js` in JS mode. Do not use the older `.?s` token — it was retired. +5. **Inline TS/JS link pairs.** For a paired link that differs only by language (the canonical case is the download-solution link), write two adjacent `` with the language tokens `(TS)` and `(JS)` inside a nested `` so both variants read plainly in raw markdown while the rendered site shows only one. See any tutorial-overview README for the current shape: + + ```md + [📥 Download Solution](…step-NN.zip) (TS)[📥 Download Solution](…step-NN-js.zip) (JS) + ``` + + `` is inline — do **not** add `markdown="1"` (that attribute is for block-level elements). The CSS in [assets/css/custom.css](assets/css/custom.css) hides `.ts-only` / `.js-only` on any element based on the active language, and `.lang-suffix { display: none }` drops the `(TS)` / `(JS)` marker on the rendered side. + +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: ](../<NN+1>/README.md)` and `**Previous:** [Step N-1: <title>](../<NN-1>/README.md)`. +- 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. + +## Using the tutorials as a source for generated code + +The steps are *pedagogical*, not *canonical*. Step 01 of each tutorial is deliberately incomplete — it exists to introduce one concept at a time. If a user asks you to scaffold a new UI5 app or generate a snippet, anchor on the **last** step of the relevant tutorial, not the first. + +- **New freestyle UI5 app** → copy the shape of [packages/walkthrough/steps/38/](packages/walkthrough/steps/38/). It has the complete `manifest.json`, `Component`, router setup, i18n, tests, and `ui5.yaml` for a production build. Steps 01–09 omit the Component; steps 10–29 omit routing; earlier steps omit tests. +- **Routing / navigation questions** → [packages/navigation/steps/17/](packages/navigation/steps/17/) is the full picture. Walkthrough steps 30–32 are a routing subset (single-page → detail flow) and will mislead if the user needs deep-linking, unmatched routes, or target lifecycle. +- **OData V4** → [packages/odatav4/steps/11/](packages/odatav4/steps/11/) covers CRUD, batch groups, operations, list-detail, and `:n` navigation. **The tutorial's data layer is a mock server simulating TripPin — do not cargo-cult `webapp/localService/` into an answer about connecting to a real backend.** Point the user at a real OData destination and keep only the model/binding code from the tutorial. +- **First-15-minutes demo, not a project starter** → [packages/quickstart/](packages/quickstart/) is 3 steps and stops before Component/manifest wiring. Use it to *show* UI5, not to *scaffold* from. +- **Tooling handshake** ([npm install](packages/walkthrough/steps/01/README.md), `ui5 init`, `ui5 use OpenUI5`, `ui5 add sap.ui.core themelib_sap_horizon`, `tsconfig.json`) — always take these from [walkthrough/steps/01](packages/walkthrough/steps/01/README.md) + [walkthrough/steps/02](packages/walkthrough/steps/02/README.md). They are the only steps that document setup from scratch. + +Out of scope for this repo — do not invent code claiming these are covered here: CAP / CDS backends, Fiori elements, BTP / Cloud Foundry / ABAP deployment, `easy-ui5` / `generator-ui5-*` scaffolding, CI setup, real authentication. + +### Reading the markdown correctly + +When ingesting a step README, treat these as semantic — not decorative — markers: + +- Any element carrying the class `ts-only` or `js-only` — most often `<details class="ts-only" markdown="1">` for prose blocks or `<span class="ts-only">` for inline link pairs — is the JS/TS switch. In raw markdown both variants are present; filter by class before quoting, or you will emit TS content in a JS answer (or vice versa). +- Adjacent `` ```ts `` and `` ```js `` fences are the *same file* in two languages, not two different files. The first-line path comment inside each fence is authoritative for which file it is. +- The literal string `.ts/.js` in prose is a runtime substitution token, not a filename. Resolve it to `.ts` or `.js` based on the language you are generating for. +- The `(TS)` / `(JS)` marker inside a `<span class="lang-suffix">` is a raw-markdown readability aid; it is hidden on the rendered site. Do not treat it as part of the visible link text when you quote a snippet. + +## Propagation rules — changes are almost never local + +Tutorials in this repo are cumulative: step `NN+1` is built on top of step `NN`'s files. That makes almost every "small" change ripple. Before you consider a task done, check whether one of these applies: + +- **Editing a step's code or `README.md`** → the same change must be reflected in **every subsequent step of that tutorial** where the affected file still exists. If you rename `Foo.controller.ts` in step 10, steps 11–38 that carry `Foo.controller.ts` forward need the same rename. If you fix a bug in a snippet in step 15's README, later steps that quote or re-show that snippet need the same fix. Never leave the corpus in a state where the "diff between consecutive steps" contains changes the step's prose does not explain. +- **Editing an authoring convention** (fence syntax, callout syntax, path-comment format, `<details>` class names, step-footer shape, namespace pattern) → every existing step README and every AI-facing doc that describes the convention must be updated in the same change. Specifically: this file (`AGENTS.md`), [CONTRIBUTING.md](CONTRIBUTING.md), and any `README.md` under `packages/*/` that references the convention. A convention that is only half-migrated is worse than no convention. +- **Renaming, adding, or removing a step** → update `packages/<pkg>/README.md` (the step index prose), regenerate `packages/<pkg>/steps.json` via `node _/generate-steps-index.js`, fix the `**Next:** / **Previous:**` footer links in the neighboring steps, and re-check any cross-tutorial link that points at the old step number. +- **Editing repo-shape guidance for AI** (this file, the root [README.md](README.md), [CONTRIBUTING.md](CONTRIBUTING.md), a `packages/*/README.md`) → if the same fact is stated in more than one of these, update all of them in one commit. Divergence between AI docs and human docs is the failure mode most likely to mislead a future agent. + +Verify a rippling change with `git grep` before *and* after — the count of matches should change in a way you can explain. A cumulative-tutorial change that only touches one step is almost always incomplete. + +## When something needs to change across many files + +- One-off scripts go under `_/` (gitignored). Match the pattern of the migration scripts already there (`migrate-*.js`, `promote-*.js`, `add-*.js`) — small Node scripts, no dependencies, walk the tree with `fs.readdirSync`. +- Design scripts to be **idempotent**: re-running on already-migrated content should produce zero changes. This lets you split large migrations into review-and-apply passes. +- For content-shape changes that ripple across all four packages, verify with grep before and after (`git grep`) and touch one canary file first (usually [packages/walkthrough/steps/09/README.md](packages/walkthrough/steps/09/README.md) — it exercises every convention). + +## Verification + +- **Static:** `npm run typecheck` for TS integrity, `git grep` for corpus-wide cleanliness. +- **Rendered:** `npm start`, then load a step README in the browser (e.g. `http://localhost:1337/packages/walkthrough/steps/09/README.md`). Toggle the TS/JS button and confirm both languages render correctly. +- **Built:** `npm run build`, then `open dist/walkthrough/build/09/index-cdn.html` for the built app, or `dist/walkthrough/steps/09/README.html` for the built README. + +## Deploy + +GitHub Pages runs the `.github/workflows/deploy.yml` workflow: `npm ci` → `node tools/builder` → publish `dist/`. Jekyll is enabled on the artifact so `_includes/head-custom.html` and the front-matter emitted by `tools/builder/prepare-gh-pages.js` take effect. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8dca92657..94e336420 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,11 +43,11 @@ Readers of the tutorial can freely switch the programming language of code snipp Main features of the two-language support are: 1. Content meant only for *one* of the programming languages can be hidden in the other by enclosing it in a specific block. 2. Two adjacent code sections in different languages are automatically converted to a tab container which allows switching between languages. -3. File extensions writen as `.?s` appear automatically as `.js` or `.ts` depending on the current language. +3. File extensions writen as `.ts/.js` appear automatically as `.js` or `.ts` depending on the current language. ### Limitations -The feature 3. above does not work inside code blocks (yet). +Feature 3 above (`.ts/.js` extension substitution) does not apply inside code blocks. That is intentional: inside a code fence the fence itself already carries the language, and each ts/js fence pair also starts with a first-line path comment naming the concrete file (see *Path comments in code fences* below). No further substitution is needed there. ### Running the preview/dev server @@ -106,6 +106,25 @@ The `markdown="1"` part is required for markdown parsing within HTML and the `<s </details> +#### 1a. Inline TS/JS link pairs (`<span>`) + +The `<details>` pattern above is for *block-level* content (a paragraph, a list, a code block). For a paired inline element that differs only by language — the canonical case is the download-solution link — use adjacent `<span>` tags instead: + +```md +<span class="ts-only">[📥 Download Solution](…step-NN.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](…step-NN-js.zip)<span class="lang-suffix"> (JS)</span></span> +``` + +Rules: + +- `<span>` is inline; do **not** add `markdown="1"` (that attribute is only meaningful on block-level HTML). +- The nested `<span class="lang-suffix"> (TS)</span>` / `<span class="lang-suffix"> (JS)</span>` sits *outside* the markdown link (`](…)`) but *inside* the outer `.ts-only` / `.js-only` span. Placing the suffix outside the link text avoids nested-HTML-in-link-text quirks with both kramdown (GitHub Pages) and showdown (dev server). +- The CSS in [assets/css/custom.css](assets/css/custom.css) hides `.ts-only` / `.js-only` on any element based on the active language, and `.lang-suffix { display: none }` drops the `(TS)` / `(JS)` marker on the rendered site. Result: both links visible in raw markdown with distinct labels; only one visible on the rendered site, without the language suffix. + +##### Resulting Appearance<span class="hidden"> in markdown view (both links visible; on the rendered site only the current-language one shows and without the (TS)/(JS) suffix)</span> + +<span class="ts-only">[📥 Download Solution](…step-NN.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](…step-NN-js.zip)<span class="lang-suffix"> (JS)</span></span> + + #### 2. Switchable code blocks in both languages When a piece of code should be displayed in either JS or TS, whatever is current, then simply create two adjacent markdown-fenced code blocks. They are automatically recognized as language-specific alternatives. @@ -135,18 +154,39 @@ const i = 0; const i: number = 0; ``` +##### Path comments in code fences + +Every adjacent ts/js code-fence pair whose content is real source code should start with a first-line comment naming the concrete file. This makes the raw markdown legible to readers and to AI tools that index snippets independently of surrounding headings — each fence carries the filename it belongs to, without duplicating the surrounding prose. + +Use the comment syntax that matches the fence language: + +```md + + ```ts + // webapp/controller/App.controller.ts + … + ``` + + ```js + // webapp/controller/App.controller.js + … + ``` +``` + +For other fence languages that appear as ts/js pairs elsewhere: `<!-- path -->` for `xml` / `html`, `# path` for `ini` / `properties`. Fences used for folder-tree diagrams (`text` or unlabeled) are exempt. + #### 3. File Extensions (`.js/.ts`) -When the text or a section heading mentions the name of a file that will be JavaScript or TypeScript, depending on the language, then use the file extension `.\?s`. It will automatically be switched to the current language. +When the text or a section heading mentions the name of a file that will be JavaScript or TypeScript, depending on the language, then use the file extension `.\ts\/\.js`. It will automatically be switched to the current language. Example: ```md -In this step you create the file `Example.controller.\?s`. +In this step you create the file `Example.controller.\ts\/\.js`. ``` ##### Resulting Appearance<span class="hidden"> in markdown view (here the extension is not replaced, the replacement only happens in the dev server and in GitHub Pages)</span> -In this step you create the file `Example.controller.?s`. +In this step you create the file `Example.controller.ts/.js`. ### Converting the JS Code to TypeScript diff --git a/assets/css/custom.css b/assets/css/custom.css index 3fe4c6403..a6de3a12a 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -37,6 +37,14 @@ display: none; } +/* Suffix like "(TS)" / "(JS)" attached to language-paired inline links + (e.g. download-solution links). Visible in raw markdown for readability, + hidden on the rendered site — the surrounding .ts-only / .js-only span + already disambiguates the language once one of the pair is displayed. */ +.lang-suffix { + display: none; +} + /* tabbed switchable language area */ .code-couple-button { diff --git a/assets/js/custom.js b/assets/js/custom.js index d4a028300..19eec020c 100644 --- a/assets/js/custom.js +++ b/assets/js/custom.js @@ -191,9 +191,9 @@ function replaceFileExtensions(lang) { // iterate over each text node while (node) { let nextNode = walker.nextNode(); - if (node.nodeValue.includes(".?s")) { + if (node.nodeValue.includes(".ts/.js")) { const temp = document.createElement("div"); - temp.innerHTML = node.nodeValue.replace(/\.\?s/g, replacement); + temp.innerHTML = node.nodeValue.replace(/\.ts\/\.js/g, replacement); const fragment = document.createDocumentFragment(); while (temp.firstChild) { @@ -201,8 +201,8 @@ function replaceFileExtensions(lang) { } node.parentNode.replaceChild(fragment, node); - } else if (node.nodeValue.includes(".\\?s")) { - node.nodeValue = node.nodeValue.replace(/\.\\\?s/g, ".?s"); + } else if (node.nodeValue.includes(".\\ts\\/\\.js")) { + node.nodeValue = node.nodeValue.replace(/\.\\ts\\\/\\\.js/g, ".ts/.js"); } node = nextNode; } diff --git a/packages/navigation/README.md b/packages/navigation/README.md index 3a2cde4a6..bb458a9df 100644 --- a/packages/navigation/README.md +++ b/packages/navigation/README.md @@ -1,5 +1,3 @@ -<!-- loio1b6dcd39a6a74f528b27ddb22f15af0d --> - # Navigation and Routing Tutorial SAPUI5 comes with a powerful routing API that helps you control the state of your application efficiently. This tutorial will illustrate all major features and APIs related to navigation and routing in SAPUI5 apps by creating a simple and easy to understand mobile app. It represents a set of best practices for applying the navigation and routing features of SAPUI5 to your applications. @@ -42,23 +40,23 @@ Throughout this tutorial we will add features for navigating to pages and bookma The tutorial consists of the following steps. To start, just open the first link — you'll be guided from there. -- **[Step 1: Set Up the Initial App](./steps/01/README.md "We start by setting up a simple app for this tutorial. The app displays mock data only and mimics real OData back-end calls with the mock server as you have seen in the *Walkthrough* tutorial.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/01/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-01.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-01-js.zip) </details>) -- **[Step 2: Enable Routing](./steps/02/README.md "In this step we will modify the app and introduce routing. Instead of having the home page of the app hard coded we will configure a router to wire multiple views together when our app is called.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/02/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-02.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-02-js.zip) </details>) -- **[Step 3: Catch Invalid Hashes](./steps/03/README.md "Sometimes it is important to display an indication that the requested resource was not found.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/03/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-03.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-03-js.zip) </details>) -- **[Step 4: Add a *Back* Button to *Not Found* Page](./steps/04/README.md "When we are on the *Not Found* page because of an invalid hash, we want to get back to our app to select another page.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/04/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-04.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-04-js.zip) </details>) -- **[Step 5: Display a Target Without Changing the Hash](./steps/05/README.md "In this step, you will learn more about targets and how to display a target from the routing configuration manually.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/05/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-05.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-05-js.zip) </details>) -- **[Step 6: Navigate to Routes with Hard-Coded Patterns](./steps/06/README.md "In this step, we'll create a second button on the home page, with which we can navigate to a simple list of employees.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/06/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-06.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-06-js.zip) </details>) -- **[Step 7: Navigate to Routes with Mandatory Parameters](./steps/07/README.md "In this step, we implement a feature that allows the user to click on an employee in the list to see additional details of the employee.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/07/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-07.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-07-js.zip) </details>) -- **[Step 8: Navigate with Flip Transition](./steps/08/README.md "In this step, we want to illustrate how to navigate to a page with a custom transition animation. Both forward and backward navigation will use the “flip” transition but with a different direction.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/08/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-08.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-08-js.zip) </details>) -- **[Step 9: Allow Bookmarkable Tabs with Optional Query Parameters](./steps/09/README.md "The resume view contains four tabs as we have seen in the previous step of this tutorial. However, when the user navigates to the resume page, only the first tab is displayed initially.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/09/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-09.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-09-js.zip) </details>) -- **[Step 10: Implement “Lazy Loading”](./steps/10/README.md "In the previous steps, we have implemented a Resume view that uses tabs to display data. The complete content of all the tabs is loaded once, no matter which tab is currently displayed.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/10/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-10.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-10-js.zip) </details>) -- **[Step 11: Assign Multiple Targets](./steps/11/README.md "In this step, we will add a new button to the home page to illustrate the usage of multiple targets for a route.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/11/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-11.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-11-js.zip) </details>) -- **[Step 12: Make a Search Bookmarkable](./steps/12/README.md "In this step we will make the search bookmarkable. This allows users to search for employees in the *Employees* table and they can bookmark their search query or share the URL.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/12/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-12.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-12-js.zip) </details>) -- **[Step 13: Make Table Sorting Bookmarkable](./steps/13/README.md "In this step, we will create a button at the top of the table which will change the sorting of the table.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/13/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-13.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-13-js.zip) </details>) -- **[Step 14: Make Dialogs Bookmarkable](./steps/14/README.md "In this step, we want to allow bookmarking of the dialog box that is opened when the user clicks the *Sort* button.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/14/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-14.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-14-js.zip) </details>) -- **[Step 15: Reuse an Existing Route](./steps/15/README.md "The *Employees* table displays employee data. However, the resumes of the employees are not accessible from this view yet.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/15/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-15.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-15-js.zip) </details>) -- **[Step 16: Handle Invalid Hashes by Listening to Bypassed Events](./steps/16/README.md "So far we have created many useful routes in our app. In the very early steps we have also made sure that a *Not Found* page is displayed in case the app was called with an invalid hash.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/16/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-16.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-16-js.zip) </details>) -- **[Step 17: Listen to Matched Events of Any Route](./steps/17/README.md "In the previous step, we have listened for bypassed events to detect possible technical issues with our app.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/17/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-17.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-17-js.zip) </details>) +- **[Step 1: Set Up the Initial App](./steps/01/README.md)** — We start by setting up a simple app for this tutorial. The app displays mock data only and mimics real OData back-end calls with the mock server as you have seen in the *Walkthrough* tutorial. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/01/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 2: Enable Routing](./steps/02/README.md)** — In this step we will modify the app and introduce routing. Instead of having the home page of the app hard coded we will configure a router to wire multiple views together when our app is called. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/02/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 3: Catch Invalid Hashes](./steps/03/README.md)** — Sometimes it is important to display an indication that the requested resource was not found. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/03/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 4: Add a *Back* Button to *Not Found* Page](./steps/04/README.md)** — When we are on the *Not Found* page because of an invalid hash, we want to get back to our app to select another page. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/04/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-04.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-04-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 5: Display a Target Without Changing the Hash](./steps/05/README.md)** — In this step, you will learn more about targets and how to display a target from the routing configuration manually. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/05/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-05.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-05-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 6: Navigate to Routes with Hard-Coded Patterns](./steps/06/README.md)** — In this step, we'll create a second button on the home page, with which we can navigate to a simple list of employees. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/06/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-06.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-06-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 7: Navigate to Routes with Mandatory Parameters](./steps/07/README.md)** — In this step, we implement a feature that allows the user to click on an employee in the list to see additional details of the employee. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/07/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-07.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-07-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 8: Navigate with Flip Transition](./steps/08/README.md)** — In this step, we want to illustrate how to navigate to a page with a custom transition animation. Both forward and backward navigation will use the “flip” transition but with a different direction. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/08/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-08.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-08-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 9: Allow Bookmarkable Tabs with Optional Query Parameters](./steps/09/README.md)** — The resume view contains four tabs as we have seen in the previous step of this tutorial. However, when the user navigates to the resume page, only the first tab is displayed initially. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/09/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-09.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-09-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 10: Implement “Lazy Loading”](./steps/10/README.md)** — In the previous steps, we have implemented a Resume view that uses tabs to display data. The complete content of all the tabs is loaded once, no matter which tab is currently displayed. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/10/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-10.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-10-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 11: Assign Multiple Targets](./steps/11/README.md)** — In this step, we will add a new button to the home page to illustrate the usage of multiple targets for a route. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/11/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-11.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-11-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 12: Make a Search Bookmarkable](./steps/12/README.md)** — In this step we will make the search bookmarkable. This allows users to search for employees in the *Employees* table and they can bookmark their search query or share the URL. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/12/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-12.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-12-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 13: Make Table Sorting Bookmarkable](./steps/13/README.md)** — In this step, we will create a button at the top of the table which will change the sorting of the table. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/13/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-13.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-13-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 14: Make Dialogs Bookmarkable](./steps/14/README.md)** — In this step, we want to allow bookmarking of the dialog box that is opened when the user clicks the *Sort* button. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/14/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-14.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-14-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 15: Reuse an Existing Route](./steps/15/README.md)** — The *Employees* table displays employee data. However, the resumes of the employees are not accessible from this view yet. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/15/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-15.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-15-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 16: Handle Invalid Hashes by Listening to Bypassed Events](./steps/16/README.md)** — So far we have created many useful routes in our app. In the very early steps we have also made sure that a *Not Found* page is displayed in case the app was called with an invalid hash. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/16/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-16.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-16-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 17: Listen to Matched Events of Any Route](./steps/17/README.md)** — In the previous step, we have listened for bypassed events to detect possible technical issues with our app. ([🔗 Live Preview](https://ui5.github.io/tutorials/navigation/build/17/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-17.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/navigation/navigation-step-17-js.zip)<span class="lang-suffix"> (JS)</span></span>) *** diff --git a/packages/navigation/steps.json b/packages/navigation/steps.json new file mode 100644 index 000000000..602210736 --- /dev/null +++ b/packages/navigation/steps.json @@ -0,0 +1,159 @@ +{ + "tutorial": "navigation", + "namespace": "ui5.tutorial.navigation", + "steps": [ + { + "n": 1, + "id": "01", + "title": "Set Up the Initial App", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/01/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-01.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-01-js.zip", + "description": "We start by setting up a simple app for this tutorial. The app displays mock data only and mimics real OData back-end calls with the mock server as you have seen in the Walkthrough tutorial." + }, + { + "n": 2, + "id": "02", + "title": "Enable Routing", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/02/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-02.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-02-js.zip", + "description": "In this step we will modify the app and introduce routing. Instead of having the home page of the app hard coded we will configure a router to wire multiple views together when our app is called. The routing configuration controls the application flow when the user triggers a navigation action or opens a link to the application directly." + }, + { + "n": 3, + "id": "03", + "title": "Catch Invalid Hashes", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/03/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-03.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-03-js.zip", + "description": "Sometimes it is important to display an indication that the requested resource was not found. To give you an example: If a user tries to access an invalid pattern which does not match any of the configured routes, the user is notified that something went wrong. You might also know this as a “404” or Not Found Page from traditional web pages. In this step, we will implement a feature that detects invalid hashes and visualizes this in a nice way." + }, + { + "n": 4, + "id": "04", + "title": "Add a *Back* Button to *Not Found* Page", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/04/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-04.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-04-js.zip", + "description": "When we are on the Not Found page because of an invalid hash, we want to get back to our app to select another page. Therefore, we will add a Back button to the NotFound view and make sure that the user gets redirected to either the previous page or the overview page when the Back button is pressed." + }, + { + "n": 5, + "id": "05", + "title": "Display a Target Without Changing the Hash", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/05/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-05.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-05-js.zip", + "description": "In this step, you will learn more about targets and how to display a target from the routing configuration manually." + }, + { + "n": 6, + "id": "06", + "title": "Navigate to Routes with Hard-Coded Patterns", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/06/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-06.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-06-js.zip", + "description": "In this step, we'll create a second button on the home page, with which we can navigate to a simple list of employees. This example illustrates how to navigate to a route that has a hard-coded pattern." + }, + { + "n": 7, + "id": "07", + "title": "Navigate to Routes with Mandatory Parameters", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/07/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-07.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-07-js.zip", + "description": "In this step, we implement a feature that allows the user to click on an employee in the list to see additional details of the employee. A route pattern can have one or more mandatory parameters to identify objects in an app." + }, + { + "n": 8, + "id": "08", + "title": "Navigate with Flip Transition", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/08/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-08.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-08-js.zip", + "description": "In this step, we want to illustrate how to navigate to a page with a custom transition animation. Both forward and backward navigation will use the “flip” transition but with a different direction. We will create a simple link on the Employee view that triggers a flip navigation to a page that displays the resume data of a certain employee. Pressing the Back button will navigate back to the Employee view with a reversed flip transition." + }, + { + "n": 9, + "id": "09", + "title": "Allow Bookmarkable Tabs with Optional Query Parameters", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/09/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-09.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-09-js.zip", + "description": "The resume view contains four tabs as we have seen in the previous step of this tutorial. However, when the user navigates to the resume page, only the first tab is displayed initially. Navigating directly to a specific tab or bookmarking a tab is not yet supported in our current app." + }, + { + "n": 10, + "id": "10", + "title": "Implement “Lazy Loading”", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/10/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-10.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-10-js.zip", + "description": "In the previous steps, we have implemented a Resume view that uses tabs to display data. The complete content of all the tabs is loaded once, no matter which tab is currently displayed. We can increase the performance of our app by avoiding to load content that is not visible. Therefore, we implement a “lazy loading” feature that only loads the view and data when requested by the user." + }, + { + "n": 11, + "id": "11", + "title": "Assign Multiple Targets", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/11/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-11.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-11-js.zip", + "description": "In this step, we will add a new button to the home page to illustrate the usage of multiple targets for a route. When the button is pressed, a new page opens that contains two parts: a header part at the top and a content part. The content part displays a table of employees that can be sorted and searched. We will use the array notation in the routing configuration to assign multiple targets to a route - a feature that we have not yet introduced." + }, + { + "n": 12, + "id": "12", + "title": "Make a Search Bookmarkable", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/12/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-12.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-12-js.zip", + "description": "In this step we will make the search bookmarkable. This allows users to search for employees in the Employees table and they can bookmark their search query or share the URL." + }, + { + "n": 13, + "id": "13", + "title": "Make Table Sorting Bookmarkable", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/13/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-13.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-13-js.zip", + "description": "In this step, we will create a button at the top of the table which will change the sorting of the table. When the current sorting state of the table is changed, the sorting state will be reflected in the URL. This illustrates how to make the table sorting bookmarkable." + }, + { + "n": 14, + "id": "14", + "title": "Make Dialogs Bookmarkable", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/14/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-14.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-14-js.zip", + "description": "In this step, we want to allow bookmarking of the dialog box that is opened when the user clicks the Sort button. The dialog should automatically open when the URL contains the query parameter showDialog." + }, + { + "n": 15, + "id": "15", + "title": "Reuse an Existing Route", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/15/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-15.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-15-js.zip", + "description": "The Employees table displays employee data. However, the resumes of the employees are not accessible from this view yet. We could create a new route and a new view to visualize the resume again, but we could also simply reuse an existing route to cross-link the resume of a certain employee. In this step, we will add a feature that allows users to directly navigate to the resume of a certain employee. We will reuse the Resume page that we have created in an earlier step. This example illustrates that there can be multiple navigation paths that direct to the same page." + }, + { + "n": 16, + "id": "16", + "title": "Handle Invalid Hashes by Listening to Bypassed Events", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/16/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-16.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-16-js.zip", + "description": "So far we have created many useful routes in our app. In the very early steps we have also made sure that a Not Found page is displayed in case the app was called with an invalid hash. Now, we proceed further and track invalid hashes to be able to detect and correct any invalid links or add new URL patterns that are often requested but not found. Therefore, we simply listen to the bypassed events" + }, + { + "n": 17, + "id": "17", + "title": "Listen to Matched Events of Any Route", + "previewUrl": "https://ui5.github.io/tutorials/navigation/build/17/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/navigation/navigation-step-17.zip", + "zipJs": "https://ui5.github.io/tutorials/navigation/navigation-step-17-js.zip", + "description": "In the previous step, we have listened for bypassed events to detect possible technical issues with our app. In this step, we want to improve the analysis use case even more by listening to any matched event of the route. We could use this information to measure how the app is used and how frequently the pages are called. Many Web analytic tools track page hits this way. The collected information can be used, for example to improve our app and its usability." + } + ] +} diff --git a/packages/navigation/steps/01/README.md b/packages/navigation/steps/01/README.md index 18b0aadc4..ccd56f268 100644 --- a/packages/navigation/steps/01/README.md +++ b/packages/navigation/steps/01/README.md @@ -1,5 +1,3 @@ -<!-- loiodf245bd449a7470c8c2a0926ca8d78de --> - # Step 1: Set Up the Initial App We start by setting up a simple app for this tutorial. The app displays mock data only and mimics real OData back-end calls with the mock server as you have seen in the *Walkthrough* tutorial. @@ -14,13 +12,16 @@ The structure and data model created in this step will be used throughout the re ## Setup -1. To set up your project for this tutorial, download the files at [Navigation and Routing - Step 1](https://ui5.github.io/tutorials/navigation/navigation-step-01.zip). +The initial code for this step ships with the repository at [packages/navigation/steps/01/](./). Open it and run: -2. Extract the downloaded `.zip` file at the desired location on your local machine. +```sh +npm install +npm start +``` -3. Open a shell in the extracted folder and execute `npm install`. +This starts the local web server and opens a browser window hosting `index.html`. -4. Execute `npm start` to start the web server and to open a new browser window hosting your newly created `index.html`. +If you prefer to work outside the monorepo, download the standalone bundle at <span class="ts-only">[Navigation and Routing - Step 1](https://ui5.github.io/tutorials/navigation/navigation-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[Navigation and Routing - Step 1](https://ui5.github.io/tutorials/navigation/navigation-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>, extract it, and run the same two commands from the extracted folder. You should have the same files as displayed in the following figure: @@ -28,20 +29,20 @@ You should have the same files as displayed in the following figure: ```text webapp/ -├── Component.?s +├── Component.ts/.js ├── controller/ -│ └── App.controller.?s +│ └── App.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── index-cdn.html ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js ├── localService/ │ ├── metadata.xml │ ├── mockdata/ │ │ ├── Employees.json │ │ └── Resumes.json -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── manifest.json └── view/ └── App.view.xml diff --git a/packages/navigation/steps/02/README.md b/packages/navigation/steps/02/README.md index 45aea849c..2d814a4a8 100644 --- a/packages/navigation/steps/02/README.md +++ b/packages/navigation/steps/02/README.md @@ -1,5 +1,3 @@ -<!-- loiocf3c57c89ef0491793d1ce327ab4f9b2 --> - # Step 2: Enable Routing In this step we will modify the app and introduce routing. Instead of having the home page of the app hard coded we will configure a router to wire multiple views together when our app is called. The routing configuration controls the application flow when the user triggers a navigation action or opens a link to the application directly. @@ -19,8 +17,8 @@ You can view this step live: [🔗 Live Preview of Step 2](https://ui5.github.io ```text webapp/ ├── controller/ -│ ├── App.controller.?s -│ └── Home.controller.?s +│ ├── App.controller.ts/.js +│ └── Home.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── localService/ @@ -28,13 +26,13 @@ webapp/ │ │ ├── Employees.json │ │ └── Resumes.json │ ├── metadata.xml -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── view/ │ ├── App.view.xml │ └── Home.view.xml -├── Component.?s +├── Component.ts/.js ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js └── manifest.json ``` @@ -112,9 +110,10 @@ Single-page applications based on SAPUI5 can use a so-called “router” to dis > :note: > As of SAPUI5 version 1.30, we recommend that you define the routing in the `manifest.json` descriptor file using routes and targets. In older versions of SAPUI5, the routing configuration had to be done directly in the metadata section of the component, and with different syntax. -## `webapp/Component.?s` +## `webapp/Component.ts/.js` ```ts +// webapp/Component.ts import UIComponent from "sap/ui/core/UIComponent"; /** @@ -136,6 +135,7 @@ export default class Component extends UIComponent { ``` ```js +// webapp/Component.js sap.ui.define(["sap/ui/core/UIComponent"], function (UIComponent) { "use strict"; @@ -194,9 +194,10 @@ In the `App` view, we remove the content of `App` control. The pages will be add Create a file `Home.view.xml` in the `webapp/view` folder. The home view only contains a page control that displays a button. For illustration, we bind the title of the page to the `i18n>homePageTitle`, you can use data binding just the way you are used to it. -## `webapp/controller/Home.controller.?s` \(New\) +## `webapp/controller/Home.controller.ts/.js` \(New\) ```ts +// webapp/controller/Home.controller.ts import Controller from "sap/ui/core/mvc/Controller"; /** @@ -208,6 +209,7 @@ export default class Home extends Controller { ``` ```js +// webapp/controller/Home.controller.js sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) { "use strict"; diff --git a/packages/navigation/steps/03/README.md b/packages/navigation/steps/03/README.md index 80055b73e..9e2c62e67 100644 --- a/packages/navigation/steps/03/README.md +++ b/packages/navigation/steps/03/README.md @@ -1,5 +1,3 @@ -<!-- loioe047e0596e8a4a1db50f4a53c11f4276 --> - # Step 3: Catch Invalid Hashes Sometimes it is important to display an indication that the requested resource was not found. To give you an example: If a user tries to access an invalid pattern which does not match any of the configured routes, the user is notified that something went wrong. You might also know this as a “404” or *Not Found Page* from traditional web pages. In this step, we will implement a feature that detects invalid hashes and visualizes this in a nice way. @@ -19,9 +17,9 @@ You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io ```text webapp/ ├── controller/ -│ ├── App.controller.?s -│ ├── Home.controller.?s -│ └── NotFound.controller.?s +│ ├── App.controller.ts/.js +│ ├── Home.controller.ts/.js +│ └── NotFound.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── localService/ @@ -29,14 +27,14 @@ webapp/ │ │ ├── Employees.json │ │ └── Resumes.json │ ├── metadata.xml -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── view/ │ ├── App.view.xml │ ├── Home.view.xml │ └── NotFound.view.xml -├── Component.?s +├── Component.ts/.js ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js └── manifest.json ``` @@ -108,9 +106,10 @@ Let’s extend the routing configuration in the descriptor by adding a `bypassed Now we create the view referenced above in a new file `NotFound.view.xml` within the `webapp/view` folder. It uses a `sap.m.Page` containing a `sap.m.IllustratedMessage` control to display an error message to the user. In a real app you might use a dynamic message matching the current error situation. Here, we simply display a preconfigured text from our resource bundle. -## `webapp/controller/NotFound.controller.?s` \(New\) +## `webapp/controller/NotFound.controller.ts/.js` \(New\) ```ts +// webapp/controller/NotFound.controller.ts import Controller from "sap/ui/core/mvc/Controller"; /** @@ -125,6 +124,7 @@ export default class NotFound extends Controller { ``` ```js +// webapp/controller/NotFound.controller.js sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) { "use strict"; diff --git a/packages/navigation/steps/04/README.md b/packages/navigation/steps/04/README.md index 6d449e2b0..d533610fa 100644 --- a/packages/navigation/steps/04/README.md +++ b/packages/navigation/steps/04/README.md @@ -1,5 +1,3 @@ -<!-- loio66670b0aab3948469d5cc8276113e9ea --> - # Step 4: Add a *Back* Button to *Not Found* Page When we are on the *Not Found* page because of an invalid hash, we want to get back to our app to select another page. Therefore, we will add a *Back* button to the `NotFound` view and make sure that the user gets redirected to either the previous page or the overview page when the *Back* button is pressed. @@ -15,10 +13,10 @@ When we are on the *Not Found* page because of an invalid hash, we want to get b ```text webapp/ ├── controller/ -│ ├── App.controller.?s -│ ├── BaseController.?s -│ ├── Home.controller.?s -│ └── NotFound.controller.?s +│ ├── App.controller.ts/.js +│ ├── BaseController.ts/.js +│ ├── Home.controller.ts/.js +│ └── NotFound.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── localService/ @@ -26,14 +24,14 @@ webapp/ │ │ ├── Employees.json │ │ └── Resumes.json │ ├── metadata.xml -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── view/ │ ├── App.view.xml │ ├── Home.view.xml │ └── NotFound.view.xml -├── Component.?s +├── Component.ts/.js ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js └── manifest.json ``` @@ -67,9 +65,10 @@ You can view this step live: [🔗 Live Preview of Step 4](https://ui5.github.io In the `NotFound` view, we set the property `showNavButton` of the `Page` control to `true` which automatically displays the *Back* button. We also add an event handler function `onNavBack` to the `navButtonPress` event of the control. The `onNavBack` function will handle the actual back navigation. We could directly add this function to the `NotFound` view’s controller. However, we are smart enough to anticipate that we might need the same handler function for different views. DRY \(“Don’t Repeat Yourself”\) is the right approach for us, so let’s create a `BaseController` from which all other controllers will inherit. -## `webapp/controller/BaseController.?s` \(New\) +## `webapp/controller/BaseController.ts/.js` \(New\) ```ts +// webapp/controller/BaseController.ts import Controller from "sap/ui/core/mvc/Controller"; import History from "sap/ui/core/routing/History"; import Router from "sap/ui/core/routing/Router"; @@ -98,6 +97,7 @@ export default class BaseController extends Controller { ``` ```js +// webapp/controller/BaseController.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/core/routing/History", "sap/ui/core/UIComponent"], function (Controller, History, UIComponent) { "use strict"; @@ -128,9 +128,10 @@ The third parameter of `navTo("appHome", {}, true /*no history*/);` has the valu > :note: > In SAPUI5 there are multiple options to reuse code. We recommend to use a base controller for such helper methods because this allows us to decoratively use the `onNavBack` handler directly in any XML view without adding additional code to the controller. Our base controller is an abstract controller that will not be instantiated in any view. Therefore, the naming convention `*.controller.ts` does not apply, and we can just name the file `BaseController.ts`. By not using the naming convention `*.controller.ts` we can even prevent any unintentional usage in views. -## `webapp/controller/NotFound.controller.?s` +## `webapp/controller/NotFound.controller.ts/.js` ```ts +// webapp/controller/NotFound.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -145,6 +146,7 @@ export default class NotFound extends BaseController { ``` ```js +// webapp/controller/NotFound.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -159,9 +161,10 @@ In order to reuse the base controller implementation, we have to change the depe At this point you can open `index.html#/thisIsInvalid` in your browser and press the *Back* button to see what happens. You will be redirected to the app’s home page that is matched by the route `appHome` as you opened the *Not Found* page with an invalid hash. If you change the hash to something invalid when you are on the home page of the app, you will also go to the *Not Found* page but with a history entry. When you press back, you will get to the home page again, but this time with a native history navigation. -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -176,6 +179,7 @@ export default class App extends BaseController { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -188,9 +192,10 @@ sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (B To be consistent, we will now adjust all of our controllers to extend to inherit from the `BaseController`. Change the `App` controller as described above. -## `webapp/controller/Home.controller.?s` +## `webapp/controller/Home.controller.ts/.js` ```ts +// webapp/controller/Home.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -202,6 +207,7 @@ export default class Home extends BaseController { ``` ```js +// webapp/controller/Home.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; diff --git a/packages/navigation/steps/05/README.md b/packages/navigation/steps/05/README.md index 5b802bac7..0f74497e3 100644 --- a/packages/navigation/steps/05/README.md +++ b/packages/navigation/steps/05/README.md @@ -1,5 +1,3 @@ -<!-- loiod9efab307ecc42b180b12cc8956e22d7 --> - # Step 5: Display a Target Without Changing the Hash In this step, you will learn more about targets and how to display a target from the routing configuration manually. @@ -38,9 +36,10 @@ You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io We start by changing the `Button` control from the home view. When the button is pressed, the `onDisplayNotFound` handler is called. -## `webapp/controller/Home.controller.?s` +## `webapp/controller/Home.controller.ts/.js` ```ts +// webapp/controller/Home.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -56,6 +55,7 @@ export default class Home extends BaseController { ``` ```js +// webapp/controller/Home.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -82,9 +82,10 @@ If you now call the app and press the *Display Not Found* button you see that th When pressing the app’s *Back* button, the `onNavBack` from the previous step is called. It detects that there is no previous hash and therefore tries to navigate to the `appHome` route again. The router is smart enough to detect that the current hash did not change and therefore skips the navigation to the route. Fortunately, there is an easy workaround for us. However, we need to touch the `Home` controller again. -## `webapp/controller/Home.controller.?s` \(Changed Again\) +## `webapp/controller/Home.controller.ts/.js` \(Changed Again\) ```ts +// webapp/controller/Home.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -102,6 +103,7 @@ export default class Home extends BaseController { ``` ```js +// webapp/controller/Home.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -119,9 +121,10 @@ sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (B This time we pass on a data object as the second parameter for the display method which contains the name of the current target; the one from which we navigate to the `notFound` target. We decide to choose the key `fromTarget` but since it is a custom configuration object any other key would be fine as well. -## `webapp/controller/NotFound.controller.?s` +## `webapp/controller/NotFound.controller.ts/.js` ```ts +// webapp/controller/NotFound.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import Target, { Target$DisplayEvent } from "sap/ui/core/routing/Target"; @@ -159,6 +162,7 @@ export default class NotFound extends BaseController { ``` ```js +// webapp/controller/NotFound.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; diff --git a/packages/navigation/steps/06/README.md b/packages/navigation/steps/06/README.md index 976302cb0..ece8810f5 100644 --- a/packages/navigation/steps/06/README.md +++ b/packages/navigation/steps/06/README.md @@ -1,5 +1,3 @@ -<!-- loio782aac08f2de4003a2fcbdd39d27f374 --> - # Step 6: Navigate to Routes with Hard-Coded Patterns In this step, we'll create a second button on the home page, with which we can navigate to a simple list of employees. This example illustrates how to navigate to a route that has a hard-coded pattern. @@ -24,11 +22,11 @@ You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io webapp/ ├── controller/ │ ├── employee/ -│ │ └── EmployeeList.controller.?s -│ ├── App.controller.?s -│ ├── BaseController.?s -│ ├── Home.controller.?s -│ └── NotFound.controller.?s +│ │ └── EmployeeList.controller.ts/.js +│ ├── App.controller.ts/.js +│ ├── BaseController.ts/.js +│ ├── Home.controller.ts/.js +│ └── NotFound.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── localService/ @@ -36,16 +34,16 @@ webapp/ │ │ ├── Employees.json │ │ └── Resumes.json │ ├── metadata.xml -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── view/ │ ├── employee/ │ │ └── EmployeeList.view.xml │ ├── App.view.xml │ ├── Home.view.xml │ └── NotFound.view.xml -├── Component.?s +├── Component.ts/.js ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js └── manifest.json ``` @@ -70,9 +68,10 @@ webapp/ First, we change the `Home` view by adding the *Show Employee List* button. We register an event handler `onNavToEmployees` for the press event. -## `webapp/controller/Home.controller.?s` +## `webapp/controller/Home.controller.ts/.js` ```ts +// webapp/controller/Home.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -94,6 +93,7 @@ export default class Home extends BaseController { ``` ```js +// webapp/controller/Home.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -222,9 +222,10 @@ In the view, we use a `sap.m.List` control and bind its items to the data from o This view can be referenced by `ui5.tutorial.navigation.view.employee.EmployeeList`. -## `webapp/controller/employee/EmployeeList.controller.?s` \(New\) +## `webapp/controller/employee/EmployeeList.controller.ts/.js` \(New\) ```ts +// webapp/controller/employee/EmployeeList.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -236,6 +237,7 @@ export default class EmployeeList extends BaseController { ``` ```js +// webapp/controller/employee/EmployeeList.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; diff --git a/packages/navigation/steps/07/README.md b/packages/navigation/steps/07/README.md index dbc839971..424574606 100644 --- a/packages/navigation/steps/07/README.md +++ b/packages/navigation/steps/07/README.md @@ -1,5 +1,3 @@ -<!-- loiof96d2522a5ca4382a274ae3c6d002ca0 --> - # Step 7: Navigate to Routes with Mandatory Parameters In this step, we implement a feature that allows the user to click on an employee in the list to see additional details of the employee. A route pattern can have one or more mandatory parameters to identify objects in an app. @@ -30,12 +28,12 @@ You can view this step live: [🔗 Live Preview of Step 7](https://ui5.github.io webapp/ ├── controller/ │ ├── employee/ -│ │ ├── Employee.controller.?s -│ │ └── EmployeeList.controller.?s -│ ├── App.controller.?s -│ ├── BaseController.?s -│ ├── Home.controller.?s -│ └── NotFound.controller.?s +│ │ ├── Employee.controller.ts/.js +│ │ └── EmployeeList.controller.ts/.js +│ ├── App.controller.ts/.js +│ ├── BaseController.ts/.js +│ ├── Home.controller.ts/.js +│ └── NotFound.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── localService/ @@ -43,7 +41,7 @@ webapp/ │ │ ├── Employees.json │ │ └── Resumes.json │ ├── metadata.xml -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── view/ │ ├── employee/ │ │ ├── Employee.view.xml @@ -51,9 +49,9 @@ webapp/ │ ├── App.view.xml │ ├── Home.view.xml │ └── NotFound.view.xml -├── Component.?s +├── Component.ts/.js ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js └── manifest.json ``` @@ -198,9 +196,10 @@ Create the file `Employee.view.xml` inside the `webapp/view/employee` folder. Th > Requiring `sap/ui/layout/form/ResponsiveGridLayout` is needed because we use the `ResponsiveGridLayout` as `layout` for the `sap/ui/layout/form/SimpleForm`. > The `sap/ui/layout/form/SimpleForm` requires the configured layout, in case it's not done by the consumer but this may cause an additional rendering cycle if rendering starts before the layout finished loading. -## `webapp/controller/employee/Employee.controller.?s` \(New\) +## `webapp/controller/employee/Employee.controller.ts/.js` \(New\) ```ts +// webapp/controller/employee/Employee.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import { Route$MatchedEvent } from "sap/ui/core/routing/Route"; @@ -243,6 +242,7 @@ export default class Employee extends BaseController { ``` ```js +// webapp/controller/employee/Employee.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -321,9 +321,10 @@ We also add an event handler to the `change` event as a private function `_onBin It’s time to change the `EmployeeList` view so that we can navigate to the new view. We set the attribute type of the `StandardListItem` template to `Navigation` to make the item clickable and indicate a navigation feature to the user. Additionally, we add an event handler for the `press` event that is called when the user clicks on an employee list item. -## `webapp/controller/employee/EmployeeList.controller.?s` +## `webapp/controller/employee/EmployeeList.controller.ts/.js` ```ts +// webapp/controller/employee/EmployeeList.controller.ts import StandardListItem from "sap/m/StandardListItem"; import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import Event from "sap/ui/base/Event"; @@ -345,6 +346,7 @@ export default class EmployeeList extends BaseController { ``` ```js +// webapp/controller/employee/EmployeeList.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; diff --git a/packages/navigation/steps/08/README.md b/packages/navigation/steps/08/README.md index 0b496a8fc..d2de77545 100644 --- a/packages/navigation/steps/08/README.md +++ b/packages/navigation/steps/08/README.md @@ -1,5 +1,3 @@ -<!-- loio3e5f6f3e163f46ecaef376950258e5e4 --> - # Step 8: Navigate with Flip Transition In this step, we want to illustrate how to navigate to a page with a custom transition animation. Both forward and backward navigation will use the “flip” transition but with a different direction. We will create a simple link on the `Employee` view that triggers a flip navigation to a page that displays the resume data of a certain employee. Pressing the *Back* button will navigate back to the `Employee` view with a reversed flip transition. @@ -28,13 +26,13 @@ You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io webapp/ ├── controller/ │ ├── employee/ -│ │ ├── Employee.controller.?s -│ │ ├── EmployeeList.controller.?s -│ │ └── Resume.controller.?s -│ ├── App.controller.?s -│ ├── BaseController.?s -│ ├── Home.controller.?s -│ └── NotFound.controller.?s +│ │ ├── Employee.controller.ts/.js +│ │ ├── EmployeeList.controller.ts/.js +│ │ └── Resume.controller.ts/.js +│ ├── App.controller.ts/.js +│ ├── BaseController.ts/.js +│ ├── Home.controller.ts/.js +│ └── NotFound.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── localService/ @@ -42,7 +40,7 @@ webapp/ │ │ ├── Employees.json │ │ └── Resumes.json │ ├── metadata.xml -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── view/ │ ├── employee/ │ │ ├── Employee.view.xml @@ -52,9 +50,9 @@ webapp/ │ ├── App.view.xml │ ├── Home.view.xml │ └── NotFound.view.xml -├── Component.?s +├── Component.ts/.js ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js └── manifest.json ``` @@ -99,9 +97,10 @@ webapp/ First we add the *Flip to Resume* link to the *Employee Details* view to trigger the navigation to the resume of the employee that is currently displayed. -## `webapp/controller/employee/Employee.controller.?s` +## `webapp/controller/employee/Employee.controller.ts/.js` ```ts +// webapp/controller/employee/Employee.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import { Route$MatchedEvent } from "sap/ui/core/routing/Route"; @@ -128,6 +127,7 @@ export default class Employee extends BaseController { ``` ```js +// webapp/controller/employee/Employee.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -311,9 +311,10 @@ Create a file `Resume.view.xml` inside the `webapp/view/employee` folder. The vi In the `IconTabBar` we display four tabs. Three of them simply use a `Text` control to display the data from the service. The *Projects* tab uses a nested XML view to display the projects of the employee. SAPUI5 takes care of loading the XML view automatically when the user navigates to the *Resume* page. -## `webapp/controller/employee/Resume.controller.?s` \(New\) +## `webapp/controller/employee/Resume.controller.ts/.js` \(New\) ```ts +// webapp/controller/employee/Resume.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import { Route$MatchedEvent } from "sap/ui/core/routing/Route"; @@ -356,6 +357,7 @@ export default class Resume extends BaseController { ``` ```js +// webapp/controller/employee/Resume.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; diff --git a/packages/navigation/steps/09/README.md b/packages/navigation/steps/09/README.md index 524762927..29af24e4c 100644 --- a/packages/navigation/steps/09/README.md +++ b/packages/navigation/steps/09/README.md @@ -1,5 +1,3 @@ -<!-- loiob8561ff6f4c34c85a91ed06d20814cd3 --> - # Step 9: Allow Bookmarkable Tabs with Optional Query Parameters The `resume` view contains four tabs as we have seen in the previous step of this tutorial. However, when the user navigates to the `resume` page, only the first tab is displayed initially. Navigating directly to a specific tab or bookmarking a tab is not yet supported in our current app. @@ -116,9 +114,10 @@ The `:?query:` parameter starts and ends with `:`, which means that it is option To update the currently selected tab in the URL we listen to the select event of the `IconTabBar` by setting `select=".onTabSelect"` in the resume view. The `selectedKey` is bound to a view model. This allows to easily change the `selectedKey` according to the selected tab in the URL. -## `webapp/controller/employee/Resume.controller.?s` +## `webapp/controller/employee/Resume.controller.ts/.js` ```ts +// webapp/controller/employee/Resume.controller.ts import { IconTabBar$SelectEvent } from "sap/m/IconTabBar"; import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import { Route$MatchedEvent } from "sap/ui/core/routing/Route"; @@ -195,6 +194,7 @@ export default class Resume extends BaseController { ``` ```js +// webapp/controller/employee/Resume.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController", "sap/ui/model/json/JSONModel"], function (BaseController, JSONModel) { "use strict"; diff --git a/packages/navigation/steps/10/README.md b/packages/navigation/steps/10/README.md index ef04bd853..da1c41e75 100644 --- a/packages/navigation/steps/10/README.md +++ b/packages/navigation/steps/10/README.md @@ -1,5 +1,3 @@ -<!-- loiocdab0a1123514cc08cf2c8deb22f312e --> - # Step 10: Implement “Lazy Loading” In the previous steps, we have implemented a `Resume` view that uses tabs to display data. The complete content of all the tabs is loaded once, no matter which tab is currently displayed. We can increase the performance of our app by avoiding to load content that is not visible. Therefore, we implement a “lazy loading” feature that only loads the view and data when requested by the user. @@ -83,9 +81,10 @@ Create the file `ResumeHobbies.view.xml` in the `webapp/view/employee` folder. M Create the file `ResumeNotes.view.xml` in the `webapp/view/employee` folder similar to the *Hobbies* view to transform this tab to a separate view as well. -## `webapp/controller/employee/Resume.controller.?s` +## `webapp/controller/employee/Resume.controller.ts/.js` ```ts +// webapp/controller/employee/Resume.controller.ts import { IconTabBar$SelectEvent } from "sap/m/IconTabBar"; import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import { Route$MatchedEvent } from "sap/ui/core/routing/Route"; @@ -139,6 +138,7 @@ export default class Resume extends BaseController { ``` ```js +// webapp/controller/employee/Resume.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController", "sap/ui/model/json/JSONModel"], function (BaseController, JSONModel) { "use strict"; diff --git a/packages/navigation/steps/11/README.md b/packages/navigation/steps/11/README.md index 4e22c8bef..e186eeeed 100644 --- a/packages/navigation/steps/11/README.md +++ b/packages/navigation/steps/11/README.md @@ -1,5 +1,3 @@ -<!-- loiob01840ec42ef48e6bfd2bc12612f501f --> - # Step 11: Assign Multiple Targets In this step, we will add a new button to the home page to illustrate the usage of multiple targets for a route. When the button is pressed, a new page opens that contains two parts: a header part at the top and a content part. The content part displays a table of employees that can be sorted and searched. We will use the array notation in the routing configuration to assign multiple targets to a route - a feature that we have not yet introduced. @@ -48,9 +46,10 @@ You can view this step live: [🔗 Live Preview of Step 11](https://ui5.github.i First we add a new button to the `Home` view and add an event handler for the `press` event. -## `webapp/controller/Home.controller.?s` +## `webapp/controller/Home.controller.ts/.js` ```ts +// webapp/controller/Home.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -69,6 +68,7 @@ export default class Home extends BaseController { ``` ```js +// webapp/controller/Home.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -218,9 +218,10 @@ The router makes sure that the parent view is loaded in addition to the target v First we create the parent view by creating the folder `overview` under `webapp/view/employee` and placing the file `EmployeeOverview.view.xml` into that folder. This view contains a `Page` control that is referenced from the targets in our `manifest.json` descriptor file. The content aggregation of the page will be filled by the router with the top and content part when the corresponding route has been hit. -## `webapp/controller/employee/overview/EmployeeOverview.controller.?s` \(New\) +## `webapp/controller/employee/overview/EmployeeOverview.controller.ts/.js` \(New\) ```ts +// webapp/controller/employee/overview/EmployeeOverview.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; /** @@ -232,6 +233,7 @@ export default class EmployeeOverview extends BaseController { ``` ```js +// webapp/controller/employee/overview/EmployeeOverview.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (BaseController) { "use strict"; @@ -301,9 +303,10 @@ Create the file `EmployeeOverviewTop.view.xml` and place it in the `webapp/view/ Create the file EmployeeOverviewContent.view.xml in the `webapp/view/employee/overview` folder. This view displays a responsive table with several columns containing employee data like *Employee ID*, *First Name*, *Last Name* and so on. In the `headerToolbar`, we add the `SearchField` and a `Button`. The `SearchField` in the header area allows to search in the table. The `Button` next to it opens a dialog to adjust the sorting of the table. -## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.?s` \(New\) +## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts/.js` \(New\) ```ts +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts import SearchField, { SearchField$SearchEvent } from "sap/m/SearchField"; import Table from "sap/m/Table"; import ViewSettingsDialog, { ViewSettingsDialog$ConfirmEvent } from "sap/m/ViewSettingsDialog"; @@ -438,6 +441,7 @@ export default class EmployeeOverviewContent extends BaseController { ``` ```js +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.js sap.ui.define(["sap/m/ViewSettingsDialog", "sap/m/ViewSettingsItem", "ui5/tutorial/navigation/controller/BaseController", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/ui/model/Sorter"], function (ViewSettingsDialog, ViewSettingsItem, BaseController, Filter, FilterOperator, Sorter) { "use strict"; diff --git a/packages/navigation/steps/12/README.md b/packages/navigation/steps/12/README.md index 99b7acbac..55cae08d5 100644 --- a/packages/navigation/steps/12/README.md +++ b/packages/navigation/steps/12/README.md @@ -1,5 +1,3 @@ -<!-- loioe85da535ea19430a90c381f3c2bd748e --> - # Step 12: Make a Search Bookmarkable In this step we will make the search bookmarkable. This allows users to search for employees in the *Employees* table and they can bookmark their search query or share the URL. @@ -72,9 +70,10 @@ You can view this step live: [🔗 Live Preview of Step 12](https://ui5.github.i In order to make the search bookmarkable we have to think about how the pattern of the corresponding route should match the bookmark. We decide to allow `/#/employees/overview?search=mySearchQueryString` in order to bookmark a search. Therefore, we simply extend our routing configuration a little. We add the optional `:?query:` parameter to the route `employeeOverview`. We keep in mind that we want to use `search` as the URL parameter for the search term that was entered in the search field. -## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.?s` +## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts/.js` ```ts +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts import SearchField, { SearchField$SearchEvent } from "sap/m/SearchField"; import Table from "sap/m/Table"; import ViewSettingsDialog, { ViewSettingsDialog$ConfirmEvent } from "sap/m/ViewSettingsDialog"; @@ -134,6 +133,7 @@ export default class EmployeeOverviewContent extends BaseController { ``` ```js +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.js sap.ui.define(["sap/m/ViewSettingsDialog", "sap/m/ViewSettingsItem", "ui5/tutorial/navigation/controller/BaseController", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/ui/model/Sorter"], function (ViewSettingsDialog, ViewSettingsItem, BaseController, Filter, FilterOperator, Sorter) { "use strict"; diff --git a/packages/navigation/steps/13/README.md b/packages/navigation/steps/13/README.md index c0a8bbdee..cb57d6aca 100644 --- a/packages/navigation/steps/13/README.md +++ b/packages/navigation/steps/13/README.md @@ -1,5 +1,3 @@ -<!-- loio39759878ae4f48dcad0cf34da1d299f0 --> - # Step 13: Make Table Sorting Bookmarkable In this step, we will create a button at the top of the table which will change the sorting of the table. When the current sorting state of the table is changed, the sorting state will be reflected in the URL. This illustrates how to make the table sorting bookmarkable. @@ -14,9 +12,10 @@ In this step, we will create a button at the top of the table which will change You can view this step live: [🔗 Live Preview of Step 13](https://ui5.github.io/tutorials/navigation/build/13/index-cdn.html). -## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.?s` +## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts/.js` ```ts +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts import SearchField, { SearchField$SearchEvent } from "sap/m/SearchField"; import Table from "sap/m/Table"; import ViewSettingsDialog, { ViewSettingsDialog$ConfirmEvent } from "sap/m/ViewSettingsDialog"; @@ -66,6 +65,7 @@ export default class EmployeeOverviewContent extends BaseController { ``` ```js +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.js sap.ui.define(["sap/m/ViewSettingsDialog", "sap/m/ViewSettingsItem", "ui5/tutorial/navigation/controller/BaseController", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/ui/model/Sorter"], function (ViewSettingsDialog, ViewSettingsItem, BaseController, Filter, FilterOperator, Sorter) { "use strict"; diff --git a/packages/navigation/steps/14/README.md b/packages/navigation/steps/14/README.md index c90077a8f..f77989a87 100644 --- a/packages/navigation/steps/14/README.md +++ b/packages/navigation/steps/14/README.md @@ -1,5 +1,3 @@ -<!-- loio5cc3147afc8d4854b5d3c5fc20923f77 --> - # Step 14: Make Dialogs Bookmarkable In this step, we want to allow bookmarking of the dialog box that is opened when the user clicks the *Sort* button. The dialog should automatically open when the URL contains the query parameter `showDialog`. @@ -17,6 +15,7 @@ You can view this step live: [🔗 Live Preview of Step 14](https://ui5.github.i ## /controller/employee/overview/EmployeeOverviewContent.controller.ts ```ts +// /controller/employee/overview/EmployeeOverviewContent.controller.ts import SearchField, { SearchField$SearchEvent } from "sap/m/SearchField"; import Table from "sap/m/Table"; import ViewSettingsDialog, { ViewSettingsDialog$ConfirmEvent, ViewSettingsDialog$CancelEvent } from "sap/m/ViewSettingsDialog"; @@ -82,6 +81,7 @@ export default class EmployeeOverviewContent extends BaseController { ``` ```js +// /controller/employee/overview/EmployeeOverviewContent.controller.js sap.ui.define(["sap/m/ViewSettingsDialog", "sap/m/ViewSettingsItem", "ui5/tutorial/navigation/controller/BaseController", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/ui/model/Sorter"], function (ViewSettingsDialog, ViewSettingsItem, BaseController, Filter, FilterOperator, Sorter) { "use strict"; diff --git a/packages/navigation/steps/15/README.md b/packages/navigation/steps/15/README.md index 84ff89a08..5862c0801 100644 --- a/packages/navigation/steps/15/README.md +++ b/packages/navigation/steps/15/README.md @@ -1,5 +1,3 @@ -<!-- loio877d57e3b5654b19a2d2e5190dc43b0d --> - # Step 15: Reuse an Existing Route The *Employees* table displays employee data. However, the resumes of the employees are not accessible from this view yet. We could create a new route and a new view to visualize the resume again, but we could also simply reuse an existing route to cross-link the resume of a certain employee. In this step, we will add a feature that allows users to directly navigate to the resume of a certain employee. We will reuse the *Resume* page that we have created in an earlier step. This example illustrates that there can be multiple navigation paths that direct to the same page. @@ -43,9 +41,10 @@ You can view this step live: [🔗 Live Preview of Step 15](https://ui5.github.i In the `EmployeeOverviewContent` view we register an event handler for the `itemPress` event and set the type attribute of the `ColumnListItem` to `Active` so that we can choose an item and trigger the navigation. -## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.?s` +## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts/.js` ```ts +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts import { ListBase$ItemPressEvent } from "sap/m/ListBase"; import SearchField, { SearchField$SearchEvent } from "sap/m/SearchField"; import Table from "sap/m/Table"; @@ -79,6 +78,7 @@ export default class EmployeeOverviewContent extends BaseController { ``` ```js +// webapp/controller/employee/overview/EmployeeOverviewContent.controller.js sap.ui.define(["sap/m/ViewSettingsDialog", "sap/m/ViewSettingsItem", "ui5/tutorial/navigation/controller/BaseController", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/ui/model/Sorter"], function (ViewSettingsDialog, ViewSettingsItem, BaseController, Filter, FilterOperator, Sorter) { "use strict"; diff --git a/packages/navigation/steps/16/README.md b/packages/navigation/steps/16/README.md index 8e94e3cc3..ee7d2567b 100644 --- a/packages/navigation/steps/16/README.md +++ b/packages/navigation/steps/16/README.md @@ -1,5 +1,3 @@ -<!-- loioa7932a791adc4a58ae4693400801ea5f --> - # Step 16: Handle Invalid Hashes by Listening to Bypassed Events So far we have created many useful routes in our app. In the very early steps we have also made sure that a *Not Found* page is displayed in case the app was called with an invalid hash. Now, we proceed further and track invalid hashes to be able to detect and correct any invalid links or add new URL patterns that are often requested but not found. Therefore, we simply listen to the bypassed events @@ -14,9 +12,10 @@ So far we have created many useful routes in our app. In the very early steps we You can view this step live: [🔗 Live Preview of Step 16](https://ui5.github.io/tutorials/navigation/build/16/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import Log from "sap/base/Log"; import { Router$BypassedEvent } from "sap/ui/core/routing/Router"; @@ -47,6 +46,7 @@ export default class App extends BaseController { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController", "sap/base/Log"], function (BaseController, Log) { "use strict"; diff --git a/packages/navigation/steps/17/README.md b/packages/navigation/steps/17/README.md index b671ae77d..386af3f82 100644 --- a/packages/navigation/steps/17/README.md +++ b/packages/navigation/steps/17/README.md @@ -1,5 +1,3 @@ -<!-- loio4a063b8250f24d0cbf7c689821df7199 --> - # Step 17: Listen to Matched Events of Any Route In the previous step, we have listened for bypassed events to detect possible technical issues with our app. In this step, we want to improve the analysis use case even more by listening to any matched event of the route. We could use this information to measure how the app is used and how frequently the pages are called. Many Web analytic tools track page hits this way. The collected information can be used, for example to improve our app and its usability. @@ -14,9 +12,10 @@ In the previous step, we have listened for bypassed events to detect possible te You can view this step live: [🔗 Live Preview of Step 17](https://ui5.github.io/tutorials/navigation/build/17/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts import BaseController from "ui5/tutorial/navigation/controller/BaseController"; import Log from "sap/base/Log"; import { Router$BypassedEvent, Router$RouteMatchedEvent } from "sap/ui/core/routing/Router"; @@ -55,6 +54,7 @@ export default class App extends BaseController { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["ui5/tutorial/navigation/controller/BaseController", "sap/base/Log"], function (BaseController, Log) { "use strict"; diff --git a/packages/odatav4/README.md b/packages/odatav4/README.md index a82e957ef..08cf9f9fc 100644 --- a/packages/odatav4/README.md +++ b/packages/odatav4/README.md @@ -23,17 +23,17 @@ We start with an initial app that simply retrieves data from an OData V4 service The tutorial consists of the following steps. To start, just open the first link — you'll be guided from there. -- **[Step 1: The Initial App](./steps/01/README.md "We start by setting up a simple app that loads data from an OData service and displays it in a table. We use a mock server to simulate requests to and responses from the service.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/01/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-01-js.zip) </details>) -- **[Step 2: Data Access and Client-Server Communication](./steps/02/README.md "In this step, we see how the Table that is bound to the People entity set initially requests its data, and how the data can be refreshed.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/02/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip) </details>) -- **[Step 3: Automatic Data Type Detection](./steps/03/README.md "In this step, we use the automatic data type detection of the OData V4 model to parse, validate, and format user entries.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip) </details>) -- **[Step 4: Filtering, Sorting, and Counting](./steps/04/README.md "In this step, we add features to filter, sort, and count the user data by using the OData V4 model API to apply OData system query options $filter, $orderby, and $count.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/04/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip) </details>) -- **[Step 5: Batch Groups](./steps/05/README.md "In this step, we have a closer look at batch groups. Batch groups are used to group multiple requests into one server request to improve the overall performance.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip) </details>) -- **[Step 6: Create and Edit](./steps/06/README.md "In this step, we will make it possible to create and edit (update) user data from the user interface and send the data to the back end.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/06/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip) </details>) -- **[Step 7: Delete](./steps/07/README.md "In this step, we make it possible to delete user data.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/07/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip) </details>) -- **[Step 8: OData Operations](./steps/08/README.md "Our OData service provides one OData operation: the ResetDataSource action. In this step, we add a button that resets all data changes we made during the tutorial to their original state using this action.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/08/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip) </details>) -- **[Step 9: List-Detail Scenario](./steps/09/README.md "In this step we add a detail area with additional information.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/09/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip) </details>) -- **[Step 10: Enable Data Reuse](./steps/10/README.md "In this step we avoid unnecessary back-end requests by preventing the destruction of data shown in the detail area when sorting or filtering the list.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/10/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip) </details>) -- **[Step 11: Add Table with :n Navigation to Detail Area](./steps/11/README.md "In this step we add a table with additional information to the detail area.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/11/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip) </details>) +- **[Step 1: The Initial App](./steps/01/README.md)** — We start by setting up a simple app that loads data from an OData service and displays it in a table. We use a mock server to simulate requests to and responses from the service. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/01/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 2: Data Access and Client-Server Communication](./steps/02/README.md)** — In this step, we see how the Table that is bound to the People entity set initially requests its data, and how the data can be refreshed. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/02/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 3: Automatic Data Type Detection](./steps/03/README.md)** — In this step, we use the automatic data type detection of the OData V4 model to parse, validate, and format user entries. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 4: Filtering, Sorting, and Counting](./steps/04/README.md)** — In this step, we add features to filter, sort, and count the user data by using the OData V4 model API to apply OData system query options $filter, $orderby, and $count. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/04/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 5: Batch Groups](./steps/05/README.md)** — In this step, we have a closer look at batch groups. Batch groups are used to group multiple requests into one server request to improve the overall performance. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 6: Create and Edit](./steps/06/README.md)** — In this step, we will make it possible to create and edit (update) user data from the user interface and send the data to the back end. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/06/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 7: Delete](./steps/07/README.md)** — In this step, we make it possible to delete user data. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/07/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 8: OData Operations](./steps/08/README.md)** — Our OData service provides one OData operation: the ResetDataSource action. In this step, we add a button that resets all data changes we made during the tutorial to their original state using this action. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/08/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 9: List-Detail Scenario](./steps/09/README.md)** — In this step we add a detail area with additional information. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/09/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 10: Enable Data Reuse](./steps/10/README.md)** — In this step we avoid unnecessary back-end requests by preventing the destruction of data shown in the detail area when sorting or filtering the list. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/10/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 11: Add Table with :n Navigation to Detail Area](./steps/11/README.md)** — In this step we add a table with additional information to the detail area. ([🔗 Live Preview](https://ui5.github.io/tutorials/odatav4/build/11/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip)<span class="lang-suffix"> (JS)</span></span>) *** diff --git a/packages/odatav4/steps.json b/packages/odatav4/steps.json new file mode 100644 index 000000000..e329fb52a --- /dev/null +++ b/packages/odatav4/steps.json @@ -0,0 +1,105 @@ +{ + "tutorial": "odatav4", + "namespace": "ui5.tutorial.odatav4", + "steps": [ + { + "n": 1, + "id": "01", + "title": "The Initial App", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/01/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-01-js.zip", + "description": "We start by setting up a simple app that loads data from an OData service and displays it in a table. We use a mock server to simulate requests to and responses from the service." + }, + { + "n": 2, + "id": "02", + "title": "Data Access and Client-Server Communication", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/02/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 2<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 2<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 3, + "id": "03", + "title": "Automatic Data Type Detection", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 3<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 3<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 4, + "id": "04", + "title": "Filtering, Sorting, and Counting", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/04/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 4<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 4<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 5, + "id": "05", + "title": "Batch Groups", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 5<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 5<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 6, + "id": "06", + "title": "Create and Edit", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/06/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 6<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 6<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 7, + "id": "07", + "title": "Delete", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/07/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 7<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 7<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 8, + "id": "08", + "title": "OData Operations", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/08/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 8<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 8<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 9, + "id": "09", + "title": "List-Detail Scenario", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/09/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 9<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 9<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 10, + "id": "10", + "title": "Enable Data Reuse", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/10/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 10<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 10<span class=\"lang-suffix\"> (JS)</span></span>." + }, + { + "n": 11, + "id": "11", + "title": "Add Table with :n Navigation to Detail Area", + "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/11/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip", + "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip", + "description": "You can download the solution for this step here: <span class=\"ts-only\">📥 Download step 11<span class=\"lang-suffix\"> (TS)</span></span><span class=\"js-only\">📥 Download step 11<span class=\"lang-suffix\"> (JS)</span></span>." + } + ] +} diff --git a/packages/odatav4/steps/01/README.md b/packages/odatav4/steps/01/README.md index 3ac1be673..4be075def 100644 --- a/packages/odatav4/steps/01/README.md +++ b/packages/odatav4/steps/01/README.md @@ -8,29 +8,24 @@ The structure and data model created in this step will be used throughout this t **Initial app with a simple table** -![](assets/Tutorial_OData_V4_Step1_Preview_9d0182f.png "Initial app with a simple table") +![Initial app with a simple table](assets/Tutorial_OData_V4_Step1_Preview_9d0182f.png "Initial app with a simple table") -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ## Setup -1. To set up your project for this tutorial, download the files at [OData V4 - Step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip). +The initial code for this step ships with the repository at [packages/odatav4/steps/01/](./). Open it and run: + +```sh +npm install +npm start +``` + +This starts the local web server and opens a browser window hosting `index.html`. -2. Extract the downloaded `.zip` file at the desired location on your local machine. -3. Open a shell in the extracted folder and run `npm install`. -4. Run `npm start` to start the web server and to open a new browser window hosting your newly created `index.html`. +If you prefer to work outside the monorepo, download the standalone bundle at <span class="ts-only">[OData V4 - Step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[OData V4 - Step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>, extract it, and run the same two commands from the extracted folder. You should now have the following files: @@ -38,22 +33,22 @@ You should now have the following files: ```text webapp/ -├── Component.?s +├── Component.ts/.js ├── controller/ -│ └── App.controller.?s +│ └── App.controller.ts/.js ├── i18n/ │ └── i18n.properties ├── index-cdn.html ├── index.html -├── initMockServer.?s +├── initMockServer.ts/.js ├── localService/ │ ├── metadata.xml │ ├── mockdata/ │ │ └── people.json -│ └── mockserver.?s +│ └── mockserver.ts/.js ├── manifest.json ├── model/ -│ └── models.?s +│ └── models.ts/.js └── view/ └── App.view.xml ``` @@ -92,7 +87,7 @@ The `manifest.json` descriptor file contains the app configuration. In the `sap. > :note: > The mock server included in this tutorial is only meant to support the features needed in this tutorial. Currently, there is no "general-purpose mock server" for application development available with OData V4 \(like there is for OData V2\). -The `mockserver.?s` file contains the implementation of the mock server. It is quite simple since the mock server is only used to simulate certain types of requests to the *TripPin* service. +The `mockserver.ts/.js` file contains the implementation of the mock server. It is quite simple since the mock server is only used to simulate certain types of requests to the *TripPin* service. The `metadata.xml` file contains the service metadata that includes, for example, entity types and entity sets. Those define the possible requests as well as the structure of responses. @@ -104,9 +99,9 @@ In this tutorial, we only use the entity type `Person` of the *TripPin* service. [*OData* Reference Services including *TripPin*](http://www.odata.org/odata-services/) -[Bootstrapping: Loading and Initializing](../04_Essentials/bootstrapping-loading-and-initializing-a04b0d1.md "To use SAPUI5 features in your HTML page, you have to load and initialize the SAPUI5 library.") +[Bootstrapping: Loading and Initializing](https://sdk.openui5.org/topic/a04b0d10fb494d1cb722b9e341b584ba.html "To use SAPUI5 features in your HTML page, you have to load and initialize the SAPUI5 library.") -[Descriptor for Applications, Components, and Libraries \(manifest.json\)](../04_Essentials/descriptor-for-applications-components-and-libraries-manifest-json-be0cf40.md "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.") +[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.") *** diff --git a/packages/odatav4/steps/02/README.md b/packages/odatav4/steps/02/README.md index 40098f4b6..616a6d215 100644 --- a/packages/odatav4/steps/02/README.md +++ b/packages/odatav4/steps/02/README.md @@ -1,16 +1,6 @@ # Step 2: Data Access and Client-Server Communication -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step, we see how the `Table` that is bound to the `People` entity set initially requests its data, and how the data can be refreshed. We use the *Console* tab in the browser developer tools to monitor the communication between the browser and the server. We see the initial request as well as the requests for refreshing the data. @@ -18,16 +8,18 @@ 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** -![](assets/Tutorial_OData_V4_Step_2_0abcbb6.png "App with a toolbar that contains a Refresh +![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") ## Coding You can view this step live: [🔗 Live Preview of Step 2](https://ui5.github.io/tutorials/odatav4/build/02/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import MessageToast from "sap/m/MessageToast"; import MessageBox from "sap/m/MessageBox"; @@ -91,6 +83,7 @@ export default class App extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast", "sap/m/MessageBox", "sap/ui/model/json/JSONModel"], function (Controller, MessageToast, MessageBox, JSONModel) { "use strict"; @@ -234,7 +227,7 @@ We search for the following mock server requests: **Related Information** -[Bindings](../04_Essentials/bindings-54e0ddf.md "Bindings connect SAPUI5 view elements to model data, allowing changes in the model to be reflected in the view element and vice versa.") +[Bindings](https://sdk.openui5.org/topic/54e0ddf "Bindings connect SAPUI5 view elements to model data, allowing changes in the model to be reflected in the view element and vice versa.") [API Reference: `sap.ui.model.odata.v4.ODataMetaModel`](https://ui5.sap.com/#/api/sap.ui.model.odata.v4.ODataMetaModel) diff --git a/packages/odatav4/steps/03/README.md b/packages/odatav4/steps/03/README.md index 6fd58301c..fe3f851ea 100644 --- a/packages/odatav4/steps/03/README.md +++ b/packages/odatav4/steps/03/README.md @@ -1,16 +1,6 @@ # Step 3: Automatic Data Type Detection -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step, we use the automatic data type detection of the OData V4 model to parse, validate, and format user entries. The service metadata contains type information for the properties of each entity. @@ -20,13 +10,13 @@ The OData V4 Model utilizes this information to compute the corresponding SAPUI5 **Input does not match the underlying data type** -![](assets/Tutorial_OData_V4_Step_3_8320fcf.png "Input does not match the underlying data type") +![Input does not match the underlying data type](assets/Tutorial_OData_V4_Step_3_8320fcf.png "Input does not match the underlying data type") ## Coding You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html). -## `webapp/manifest.?s`on +## `webapp/manifest.ts/.js`on ```json { @@ -92,7 +82,7 @@ To make the *User Name* optional, we remove the parameter `Nullable="false"` fro **Related Information** -[Type Determination](../04_Essentials/type-determination-53cdd55.md "") +[Type Determination](https://sdk.openui5.org/topic/53cdd55 "") [API Reference: `sap.ui.model.odata.type`](https://ui5.sap.com/#/api/sap.ui.model.odata.type) diff --git a/packages/odatav4/steps/04/README.md b/packages/odatav4/steps/04/README.md index 869fbaa78..ddd691d24 100644 --- a/packages/odatav4/steps/04/README.md +++ b/packages/odatav4/steps/04/README.md @@ -1,16 +1,6 @@ # Step 4: Filtering, Sorting, and Counting -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step, we add features to filter, sort, and count the user data by using the OData V4 model API to apply OData system query options `$filter`, `$orderby`, and `$count`. @@ -18,16 +8,18 @@ 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** -![](assets/Tutorial_OData_V4_Step_4_3ac4fcc.png "App now has a search field, the entries can be sorted, and you can see how +![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") ## Coding You can view this step live: [🔗 Live Preview of Step 4](https://ui5.github.io/tutorials/odatav4/build/04/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import MessageToast from "sap/m/MessageToast"; import MessageBox from "sap/m/MessageBox"; @@ -84,6 +76,7 @@ import JSONModel from "sap/ui/model/json/JSONModel"; ``` ```js +// webapp/controller/App.controller.js sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/m/MessageToast", @@ -286,9 +279,9 @@ We add the missing texts to the properties file. **Related Information** -[Filtering](../04_Essentials/filtering-5338bd1.md "The OData V4 Model supports server side filtering on lists.") +[Filtering](https://sdk.openui5.org/topic/5338bd1 "The OData V4 Model supports server side filtering on lists.") -[Sorting](../04_Essentials/sorting-d2ce3f5.md "The OData V4 model supports server side sorting on lists.") +[Sorting](https://sdk.openui5.org/topic/d2ce3f5 "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) diff --git a/packages/odatav4/steps/05/README.md b/packages/odatav4/steps/05/README.md index 029b790a9..73202630a 100644 --- a/packages/odatav4/steps/05/README.md +++ b/packages/odatav4/steps/05/README.md @@ -1,16 +1,6 @@ # Step 5: Batch Groups -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step, we have a closer look at batch groups. Batch groups are used to group multiple requests into one server request to improve the overall performance. @@ -18,13 +8,13 @@ In this step, we have a closer look at batch groups. Batch groups are used to gr **No visual change compared to the last step** -![](assets/Tutorial_OData_V4_Step_4_3ac4fcc.png "No visual change compared to the last step") +![No visual change compared to the last step](assets/Tutorial_OData_V4_Step_4_3ac4fcc.png "No visual change compared to the last step") ## Coding You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html). -## `webapp/manifest.?s`on +## `webapp/manifest.ts/.js`on ```json ... @@ -55,9 +45,9 @@ A `$batch` request uses multipart MIME to put several requests into one. This ma **Related Information** -[Batch Control](../04_Essentials/batch-control-74142a3.md "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).") +[Batch Control](https://sdk.openui5.org/topic/74142a3 "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](../04_Essentials/performance-aspects-5a0d286.md "The OData V4 model offers the features described below which influence performance.") +[Performance Aspects](https://sdk.openui5.org/topic/5a0d286 "The OData V4 model offers the features described below which influence performance.") *** diff --git a/packages/odatav4/steps/06/README.md b/packages/odatav4/steps/06/README.md index 89ae78a27..a67c084aa 100644 --- a/packages/odatav4/steps/06/README.md +++ b/packages/odatav4/steps/06/README.md @@ -1,16 +1,6 @@ # Step 6: Create and Edit -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step, we will make it possible to create and edit \(update\) user data from the user interface and send the data to the back end. @@ -18,15 +8,16 @@ In this step, we will make it possible to create and edit \(update\) user data f **Data can now be edited and added.** -![](assets/Tutorial_OData_V4_Step_6_baf7417.png "Data can now be edited and added.") +![Data can now be edited and added.](assets/Tutorial_OData_V4_Step_6_baf7417.png "Data can now be edited and added.") ## Coding You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io/tutorials/odatav4/build/06/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts ... onInit() { const oMessageManager = sap.ui.getCore().getMessageManager(), @@ -48,6 +39,7 @@ You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io ``` ```js +// webapp/controller/App.controller.js ... onInit : function () { var oMessageManager = sap.ui.getCore().getMessageManager(), @@ -72,6 +64,7 @@ You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io We change the `onInit` method: The `appView` model receives two additional properties, which we will use to control whether certain controls in the view are enabled or visible during user entries. We also make the `MessageModel` available to the view and add a `ListBinding`. When the OData service reports errors while writing data, the OData Model adds them to the `MessageModel` as technical messages. Therefore we apply a filter to the `ListBinding`. We register our own handler to the `change` event of that `ListBinding` in order to capture any errors. ```ts +// webapp/controller/App.controller.ts ... onSort() { ... @@ -94,6 +87,7 @@ We change the `onInit` method: The `appView` model receives two additional prope ``` ```js +// webapp/controller/App.controller.js ... onSort : function () { ... @@ -119,6 +113,7 @@ We change the `onInit` method: The `appView` model receives two additional prope We add the `_setUIChanges` private method that lets us set the property `hasUIChanges` of the `appView` model. Unless there are currently technical messages in the `MessageModel` or it is called with a given value for its `bHasUIChanges` parameter, the method uses `ODataModel.hasPendingChanges`. That method returns `true` if there are any changes that have not yet been written to the service. ```ts +// webapp/controller/App.controller.ts ... onInit() { ... @@ -147,6 +142,7 @@ We add the `_setUIChanges` private method that lets us set the property `hasUICh ``` ```js +// webapp/controller/App.controller.js ... onInit: function () { ... @@ -181,6 +177,7 @@ We add the `onCreate` event handler that responds to the `press` event of the *A We also use the binding context returned by the `create` method to focus and select the new row in which the new data can be entered. ```ts +// webapp/controller/App.controller.ts ... onRefresh() { ... @@ -217,6 +214,7 @@ We also use the binding context returned by the `create` method to focus and sel ``` ```js +// webapp/controller/App.controller.js ... onRefresh: function () { ... @@ -260,6 +258,7 @@ The `submitBatch` method returns a `Promise` that is rejected only if the batch We also define a `_setBusy` private function to lock the whole UI while the data is submitted to the back end. ```ts +// webapp/controller/App.controller.ts ... onSort() { ... @@ -294,6 +293,7 @@ We also define a `_setBusy` private function to lock the whole UI while the data ``` ```js +// webapp/controller/App.controller.js ... onSort : function () { ... @@ -331,6 +331,7 @@ We also define a `_setBusy` private function to lock the whole UI while the data We implement the event handler for the `change` event of the `ListBinding` to the `MessageModel`. We created the `ListBinding` with a filter to only include technical messages. That means that the `change` event will be fired with every change but only technical messages will have a binding context. In case of technical messages, we get the first one and display it as an error. We also make sure that the toolbar for saving or discarding changes stays visible. We delete the technical messages so that they do not accumulate. ```ts +// webapp/controller/App.controller.ts ... onRefresh() { ... @@ -347,6 +348,7 @@ We implement the event handler for the `change` event of the `ListBinding` to th ``` ```js +// webapp/controller/App.controller.js ... onRefresh: function () { ... @@ -365,6 +367,7 @@ We implement the event handler for the `change` event of the `ListBinding` to th The `onResetChanges` method handles discarding pending changes. It uses the `resetChanges` method of the `ODataListBinding` API to remove any such changes. Then it calls the `_setUIChanges` private method to enable the elements of the header toolbar again and hide the footer. ```ts +// webapp/controller/App.controller.ts ... onCreate() { ... @@ -386,6 +389,7 @@ The `onResetChanges` method handles discarding pending changes. It uses the `res ``` ```js +// webapp/controller/App.controller.js ... onCreate: function () { ... @@ -568,15 +572,15 @@ We add the new message texts. **Related Information** -[Model Instantiation and Data Access](../04_Essentials/model-instantiation-and-data-access-9613f1f.md "One OData V4 model instance can only cover one OData service. This section describes the creation of a model instance in more detail.") +[Model Instantiation and Data Access](https://sdk.openui5.org/topic/9613f1f "One OData V4 model instance can only cover one OData service. This section describes the creation of a model instance in more detail.") -[Batch Control](../04_Essentials/batch-control-74142a3.md "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).") +[Batch Control](https://sdk.openui5.org/topic/74142a3 "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).") -[OData Operations](../04_Essentials/odata-operations-b54f789.md "The OData V4 model supports OData operations (ActionImport, FunctionImport, bound Actions and bound Functions). Unbound parameters are limited to primitive values.") +[OData Operations](https://sdk.openui5.org/topic/b54f789 "The OData V4 model supports OData operations (ActionImport, FunctionImport, bound Actions and bound Functions). Unbound parameters are limited to primitive values.") -[Creating an Entity](../04_Essentials/creating-an-entity-c9723f8.md "The sap.ui.model.odata.v4.ODataListBinding#create method creates a new entity. Users can interact with a newly created entity even before it has been sent to the server.") +[Creating an Entity](https://sdk.openui5.org/topic/c9723f8 "The sap.ui.model.odata.v4.ODataListBinding#create method creates a new entity. Users can interact with a newly created entity even before it has been sent to the server.") -[Message Model](../04_Essentials/message-model-8956f0a.md "The message model contains all messages and is used to bind to the messages to display them.") +[Message Model](https://sdk.openui5.org/topic/8956f0a "The message model contains all messages and is used to bind to the messages to display them.") [API Reference: `sap.ui.model.odata.v4.ODataContextBinding`](https://ui5.sap.com/#/api/sap.ui.model.odata.v4.ODataContextBinding) diff --git a/packages/odatav4/steps/07/README.md b/packages/odatav4/steps/07/README.md index d2511133f..b44cd0ec3 100644 --- a/packages/odatav4/steps/07/README.md +++ b/packages/odatav4/steps/07/README.md @@ -1,16 +1,6 @@ # Step 7: Delete -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step, we make it possible to delete user data. @@ -18,13 +8,13 @@ In this step, we make it possible to delete user data. **A new Delete User button is added** -![](assets/Tutorial_OData_V4_Step_7_32509f4.png "A new Delete User button is added") +![A new Delete User button is added](assets/Tutorial_OData_V4_Step_7_32509f4.png "A new Delete User button is added") ## Coding You can view this step live: [🔗 Live Preview of Step 7](https://ui5.github.io/tutorials/odatav4/build/07/index-cdn.html). -## `webapp/App.controller.?s` +## `webapp/App.controller.ts/.js` ``` ... @@ -192,7 +182,7 @@ We add the missing texts to the properties file. **Related Information** -[Deleting an Entity](../04_Essentials/deleting-an-entity-2613ebc.md "The v4.Context.delete method deletes an entity on the server and updates the user interface accordingly.") +[Deleting an Entity](https://sdk.openui5.org/topic/2613ebc "The v4.Context.delete method deletes an entity on the server and updates the user interface accordingly.") *** diff --git a/packages/odatav4/steps/08/README.md b/packages/odatav4/steps/08/README.md index 9d0649974..d35ed1fe5 100644 --- a/packages/odatav4/steps/08/README.md +++ b/packages/odatav4/steps/08/README.md @@ -1,16 +1,6 @@ # Step 8: OData Operations -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip)<span class="lang-suffix"> (JS)</span></span>. Our OData service provides one OData operation: the `ResetDataSource` action. In this step, we add a button that resets all data changes we made during the tutorial to their original state using this action. @@ -18,15 +8,16 @@ Our OData service provides one OData operation: the `ResetDataSource` action. In **A Restart Tutorial button is added** -![](assets/Tutorial_OData_V4_Step_8_e518deb.png "A Restart Tutorial button is added") +![A Restart Tutorial button is added](assets/Tutorial_OData_V4_Step_8_e518deb.png "A Restart Tutorial button is added") ## Coding You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io/tutorials/odatav4/build/08/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts ... onResetChanges() { this.byId("peopleList").getBinding("items").resetChanges(); @@ -51,6 +42,7 @@ You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io ``` ```js +// webapp/controller/App.controller.js ... onResetChanges : function () { this.byId("peopleList").getBinding("items").resetChanges(); @@ -131,9 +123,9 @@ And now we are done! We built a simple application with user data from an OData **Related Information** -[Bindings](../04_Essentials/bindings-54e0ddf.md "Bindings connect SAPUI5 view elements to model data, allowing changes in the model to be reflected in the view element and vice versa.") +[Bindings](https://sdk.openui5.org/topic/54e0ddf "Bindings connect SAPUI5 view elements to model data, allowing changes in the model to be reflected in the view element and vice versa.") -[OData Operations](../04_Essentials/odata-operations-b54f789.md "The OData V4 model supports OData operations (ActionImport, FunctionImport, bound Actions and bound Functions). Unbound parameters are limited to primitive values.") +[OData Operations](https://sdk.openui5.org/topic/b54f789 "The OData V4 model supports OData operations (ActionImport, FunctionImport, bound Actions and bound Functions). Unbound parameters are limited to primitive values.") *** diff --git a/packages/odatav4/steps/09/README.md b/packages/odatav4/steps/09/README.md index 87bcf975e..a1d8b46f6 100644 --- a/packages/odatav4/steps/09/README.md +++ b/packages/odatav4/steps/09/README.md @@ -1,16 +1,6 @@ # Step 9: List-Detail Scenario -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step we add a detail area with additional information. @@ -24,9 +14,10 @@ In this step we add a detail area with additional information. You can view this step live: [🔗 Live Preview of Step 9](https://ui5.github.io/tutorials/odatav4/build/09/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts ... onDelete() { const oContext, @@ -84,6 +75,7 @@ You can view this step live: [🔗 Live Preview of Step 9](https://ui5.github.io ``` ```js +// webapp/controller/App.controller.js ... onDelete : function () { var oContext, @@ -355,7 +347,7 @@ We add the missing texts to the properties file. **Related Information** -[Data Reuse](../04_Essentials/data-reuse-648e360.md "The OData V4 model keeps data with respect to bindings, which allows different views on the same data, but also means that data is not automatically shared between bindings. There are mechanisms for sharing data to avoid redundant requests and to keep the same data in different controls in sync.") +[Data Reuse](https://sdk.openui5.org/topic/648e360 "The OData V4 model keeps data with respect to bindings, which allows different views on the same data, but also means that data is not automatically shared between bindings. There are mechanisms for sharing data to avoid redundant requests and to keep the same data in different controls in sync.") *** diff --git a/packages/odatav4/steps/10/README.md b/packages/odatav4/steps/10/README.md index 5c9061d30..e980c9c91 100644 --- a/packages/odatav4/steps/10/README.md +++ b/packages/odatav4/steps/10/README.md @@ -1,16 +1,6 @@ # Step 10: Enable Data Reuse -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step we avoid unnecessary back-end requests by preventing the destruction of data shown in the detail area when sorting or filtering the list. @@ -24,9 +14,10 @@ In this step we avoid unnecessary back-end requests by preventing the destructio You can view this step live: [🔗 Live Preview of Step 10](https://ui5.github.io/tutorials/odatav4/build/10/index-cdn.html). -## `webapp/controller/App.controller.?s` +## `webapp/controller/App.controller.ts/.js` ```ts +// webapp/controller/App.controller.ts ... onMessageBindingChange(oEvent) { ... @@ -72,6 +63,7 @@ You can view this step live: [🔗 Live Preview of Step 10](https://ui5.github.i ``` ```js +// webapp/controller/App.controller.js ... onMessageBindingChange : function (oEvent) { ... @@ -124,7 +116,7 @@ You can use the `Context#setKeepAlive` method to prevent the destruction of info **Related Information** -[Extending the Lifetime of a Context that is not Used Exclusively by a Table Collection](../04_Essentials/data-reuse-648e360.md#loio648e360fa22d46248ca783dc6eb44531__section_ELC) +[Extending the Lifetime of a Context that is not Used Exclusively by a Table Collection](https://sdk.openui5.org/topic/648e360#loio648e360fa22d46248ca783dc6eb44531__section_ELC) *** diff --git a/packages/odatav4/steps/11/README.md b/packages/odatav4/steps/11/README.md index 0363e192b..162829905 100644 --- a/packages/odatav4/steps/11/README.md +++ b/packages/odatav4/steps/11/README.md @@ -1,16 +1,6 @@ # Step 11: Add Table with :n Navigation to Detail Area -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip)<span class="lang-suffix"> (JS)</span></span>. In this step we add a table with additional information to the detail area. @@ -18,7 +8,7 @@ In this step we add a table with additional information to the detail area. **A table containing information about friends of the selected user is added** -![](assets/Tut_OD4_Step_11_45abd62.png "A table containing information about friends of the selected user is added") +![A table containing information about friends of the selected user is added](assets/Tut_OD4_Step_11_45abd62.png "A table containing information about friends of the selected user is added") ## Coding diff --git a/packages/quickstart/README.md b/packages/quickstart/README.md index 4b4c339d6..618b4f173 100644 --- a/packages/quickstart/README.md +++ b/packages/quickstart/README.md @@ -20,9 +20,9 @@ We first introduce you to the basic development paradigms like *Model-View-Contr The tutorial consists of the following steps. To start, just open the first link - you`ll be guided from there. -- **[Step 1: Ready...](steps/01/README.md "Let's get you ready for your journey! We bootstrap OpenUI5 in an HTML page and implement a simple "Hello World" example.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/quickstart/build/01/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-01.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-01-js.zip) </details> ) -- **[Step 2: Steady...](steps/02/README.md "Now we extend our minimalist HTML page to a basic app with a view and a controller.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/quickstart/build/02/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-02.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-02-js.zip) </details> ) -- **[Step 3: Go!](steps/03/README.md "Now it is time to build our first little UI by replacing the "Hello World" text in the HTML body by the OpenUI5 control sap/m/Text. In the beginning, we will use the JavaScript control interface to set up the UI, the control instance is then placed into the HTML body. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/quickstart/build/03/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-03.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-03-js.zip)</details> ) +- **[Step 1: Ready...](steps/01/README.md)** — Let's get you ready for your journey! We bootstrap OpenUI5 in an HTML page and implement a simple "Hello World" example. ([🔗 Live Preview](https://ui5.github.io/tutorials/quickstart/build/01/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span> ) +- **[Step 2: Steady...](steps/02/README.md)** — Now we extend our minimalist HTML page to a basic app with a view and a controller. ([🔗 Live Preview](https://ui5.github.io/tutorials/quickstart/build/02/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span> ) +- **[Step 3: Go!](steps/03/README.md)** — Now it is time to build our first little UI by replacing the "Hello World" text in the HTML body by the OpenUI5 control sap/m/Text. In the beginning, we will use the JavaScript control interface to set up the UI, the control instance is then placed into the HTML body. ([🔗 Live Preview](https://ui5.github.io/tutorials/quickstart/build/03/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/quickstart/quickstart-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span> ) ## License diff --git a/packages/quickstart/steps.json b/packages/quickstart/steps.json new file mode 100644 index 000000000..2d43da8a6 --- /dev/null +++ b/packages/quickstart/steps.json @@ -0,0 +1,33 @@ +{ + "tutorial": "quickstart", + "namespace": "ui5.tutorial.quickstart", + "steps": [ + { + "n": 1, + "id": "01", + "title": "Ready...", + "previewUrl": "https://ui5.github.io/tutorials/quickstart/build/01/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/quickstart/quickstart-step-01.zip", + "zipJs": "https://ui5.github.io/tutorials/quickstart/quickstart-step-01-js.zip", + "description": "Let's get you ready for your journey! We bootstrap OpenUI5 in an HTML page and implement a simple \"Hello World\" example." + }, + { + "n": 2, + "id": "02", + "title": "Steady...", + "previewUrl": "https://ui5.github.io/tutorials/quickstart/build/02/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/quickstart/quickstart-step-02.zip", + "zipJs": "https://ui5.github.io/tutorials/quickstart/quickstart-step-02-js.zip", + "description": "Now we extend our minimalist HTML page to a basic app with a view and a controller." + }, + { + "n": 3, + "id": "03", + "title": "Go!", + "previewUrl": "https://ui5.github.io/tutorials/quickstart/build/03/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/quickstart/quickstart-step-03.zip", + "zipJs": "https://ui5.github.io/tutorials/quickstart/quickstart-step-03-js.zip", + "description": "Finally, we add a second page to our app showcasing some of the key OpenUI5 concepts." + } + ] +} diff --git a/packages/quickstart/steps/01/README.md b/packages/quickstart/steps/01/README.md index 87a3402a1..47e8795a1 100644 --- a/packages/quickstart/steps/01/README.md +++ b/packages/quickstart/steps/01/README.md @@ -9,7 +9,7 @@ Let's get you ready for your journey! We bootstrap OpenUI5 in an HTML page and i ### Preview -![](assets/loio9c157e9764b846fea7de519d141c33ac_LowRes.png "The browser shows a "Ready" button that triggers a "Hello World" message") +![The browser shows a "Ready" button that triggers a "Hello World" message](assets/loio9c157e9764b846fea7de519d141c33ac_LowRes.png "The browser shows a Ready button that triggers a Hello World message") <sup>*The browser shows a "Ready" button that triggers a "Hello World" message*</sup> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 1](https://ui5.github.io/tutorials/quickstart-step-01.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 1](https://ui5.github.io/tutorials/quickstart-step-01-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 1](https://ui5.github.io/tutorials/quickstart-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 1](https://ui5.github.io/tutorials/quickstart-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** @@ -75,11 +65,12 @@ With the `script` tag, we load and initialize OpenUI5 with typical bootstrap par *** -### webapp/index.?s \(New\) +### webapp/index.ts/.js \(New\) In your `webapp` folder, create a new file `index.js` that will be called as soon as OpenUI5 is loaded and initialized. We load two UI controls - a button and a message toast - and place the button in the element with the `content` ID. The button is defined with a `text` property and a callback attached to its `press` event. ```ts +// webapp/index.ts import Button from "sap/m/Button"; import MessageToast from "sap/m/MessageToast"; @@ -92,6 +83,7 @@ new Button({ ``` ```js +// webapp/index.js sap.ui.define([ "sap/m/Button", "sap/m/MessageToast" diff --git a/packages/quickstart/steps/02/README.md b/packages/quickstart/steps/02/README.md index d6f5dbfb6..8be09e6be 100644 --- a/packages/quickstart/steps/02/README.md +++ b/packages/quickstart/steps/02/README.md @@ -9,7 +9,7 @@ Now we extend our minimalist HTML page to a basic app with a view and a controll ### Preview -![](assets/loio240ef5357d7f4d36955092cdaf1884a2_LowRes.png "The browser shows a Steady button in an app") +![The browser shows a Steady button in an app](assets/loio240ef5357d7f4d36955092cdaf1884a2_LowRes.png "The browser shows a Steady button in an app") <sup>*The browser shows a Steady button in an app*</sup> @@ -19,27 +19,18 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/quickstart-step-02.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/quickstart-step-02-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 2](https://ui5.github.io/tutorials/quickstart-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 2](https://ui5.github.io/tutorials/quickstart-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/index.?s +### webapp/index.ts/.js Now we replace most of the code in this file: We remove the inline button from the previous step, and introduce a proper XML view to separate the presentation from the controller logic. We prefix the view name `ui5.tutorial.quickstart.App` with our newly defined namespace. The view is loaded asynchronously. Similar to the step before, the view is placed in the element with the `content` ID after it has finished loading. ```ts +// webapp/index.ts import XMLView from "sap/ui/core/mvc/XMLView"; XMLView.create({ @@ -47,6 +38,7 @@ XMLView.create({ }).then((oView) => oView.placeAt("content")); ``` ```js +// webapp/index.js sap.ui.define([ "sap/ui/core/mvc/XMLView" ], (XMLView) => { @@ -89,6 +81,7 @@ We outsource the controller logic to an app controller. The `.onPress` event now In our controller, we load the `Controller` base class and extend it to define the behavior of our app. We also add the event handler for our button. The `MessageToast` is also loaded as a dependency. When the button is pressed, we now display a "Hello App" message. ```ts +// webapp/App.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import MessageToast from "sap/m/MessageToast"; @@ -99,6 +92,7 @@ export default class App extends Controller { } ``` ```js +// webapp/App.controller.js sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/m/MessageToast" diff --git a/packages/quickstart/steps/03/README.md b/packages/quickstart/steps/03/README.md index cd1e665b7..faa87dc66 100644 --- a/packages/quickstart/steps/03/README.md +++ b/packages/quickstart/steps/03/README.md @@ -9,7 +9,7 @@ Finally, we add a second page to our app showcasing some of the key OpenUI5 conc ### Preview -![](assets/loio79e1157d948c488c9717ef840fa9b396_LowRes.png "The second page shows plenty of UI controls and concepts to explore") +![The second page shows plenty of UI controls and concepts to explore](assets/loio79e1157d948c488c9717ef840fa9b396_LowRes.png "The second page shows plenty of UI controls and concepts to explore") <sup>*The second page shows plenty of UI controls and concepts to explore*</sup> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/quickstart-step-03.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/quickstart-step-03-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 3](https://ui5.github.io/tutorials/quickstart-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 3](https://ui5.github.io/tutorials/quickstart-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** @@ -135,6 +125,7 @@ We display these texts on the second page using data binding. The `InfoLabel` ta Finally, we make the Panel in the lower part of the view interactive by attaching an `onChange` event to the switch defined there. OpenUI5 comes with a large set of feature-rich controls that you can combine as you need. ```ts +// webapp/App.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import MessageToast from "sap/m/MessageToast"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -169,6 +160,7 @@ export default class AppController extends Controller { ``` ```js +// webapp/App.controller.js sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/m/MessageToast", diff --git a/packages/walkthrough/README.md b/packages/walkthrough/README.md index 63a7ec7f5..dfabe1918 100644 --- a/packages/walkthrough/README.md +++ b/packages/walkthrough/README.md @@ -20,44 +20,44 @@ We first introduce you to the basic development paradigms like *Model-View-Contr The tutorial consists of the following steps. To start, just open the first link - you`ll be guided from there. -- **[Step 1: Hello World!](steps/01/README.md "As you know OpenUI5 is all about HTML5. Let’s get started with building a first "Hello World" with only HTML.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/01/index.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01-js.zip) </details> ) -- **[Step 2: Bootstrap](steps/02/README.md "Before we can do something with OpenUI5, we need to load and initialize it. This process of loading and initializing OpenUI5 is called bootstrapping. Once this bootstrapping is finished, we simply display an alert.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/02/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02-js.zip) </details> ) -- **[Step 3: Controls](steps/03/README.md "Now it is time to build our first little UI by replacing the "Hello World" text in the HTML body by the OpenUI5 control sap/m/Text. In the beginning, we will use the JavaScript control interface to set up the UI, the control instance is then placed into the HTML body. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/03/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03-js.zip)</details> ) -- **[Step 4: XML Views](steps/04/README.md "Putting all our UI into the index.ts file will very soon result in a messy setup, and there is quite a bit of work ahead of us. So let’s do a first modularization by putting the sap/m/Text control into a dedicated view.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/04/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04.zip) </details> <details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04-js.zip) </details>) -- **[Step 5: Controllers](steps/05/README.md "In this step, we replace the text with a button and show the "Hello World" message when the button is pressed. The handling of the button's press event is implemented in the controller of the view.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/05/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05-js.zip) </details>) -- **[Step 6: Modules](steps/06/README.md "In OpenUI5, resources are often referred to as modules. In this step, we replace the alert from the last exercise with a proper Message Toast from the sap.m library.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/06/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06.zip) </details> <details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06-js.zip) </details>) -- **[Step 7: JSON Model](steps/07/README.md "Now that we have set up the view and controller, it’s about time to think about the M in MVC.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/07/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07-js.zip) </details>) -- **[Step 8: Translatable Texts](steps/08/README.md "In this step we move the texts of our UI to a separate resource file.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/08/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08-js.zip) </details>) -- **[Step 9: Component Configuration](steps/09/README.md "After we have introduced all three parts of the Model-View-Controller /(MVC/) concept, we now come to another important structural aspect of OpenUI5. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/09/index-cdn.html) \| <details class="ts-only" markdown="1">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09-js.zip)</details>) -- **[Step 10: Descriptor for Applications](steps/10/README.md "All application-specific configuration settings will now further be put in a separate descriptor file called manifest.json. This clearly separates the application coding from the configuration settings and makes our app even more flexible. For example, all SAP Fiori applications are realized as components and come with a descriptor file in order to be hosted in the SAP Fiori launchpad.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/10/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10-js.zip) </details>) -- **[Step 11: Pages and Panels](steps/11/README.md "After all the work on the app structure it’s time to improve the look of our app. We will use two controls from the sap.m library to add a bit more "bling" to our UI. You will also learn about control aggregations in this step.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/11/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11-js.zip) </details>) -- **[Step 12: Shell Control as Container](steps/12/README.md "Now we use a shell control as container for our app and use it as our new root element. The shell takes care of visual adaptation of the application to the device’s screen size by introducing a so-called letterbox on desktop screens.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/12/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12-js.zip) </details>) -- **[Step 13: Margins and Paddings](steps/13/README.md "Our app content is still glued to the corners of the letterbox. To fine-tune our layout, we can add margins and paddings to the controls that we added in the previous step. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/13/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13-js.zip) </details>) -- **[Step 14: Custom CSS and Theme Colors](steps/14/README.md "Sometimes we need to define some more fine-granular layouts and this is when we can use the flexibility of CSS by adding custom style classes to controls and style them as we like. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/14/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14-js.zip)</details>) -- **[Step 15: Nested Views](steps/15/README.md "Our panel content is getting more and more complex and now it is time to move the panel content to a separate view. With that approach, the application structure is much easier to understand, and the individual parts of the app can be reused.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/15/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15-js.zip)</details>) -- **[Step 16: Dialogs and Fragments](steps/16/README.md "In this step, we will take a closer look at another element which can be used to assemble views: the fragment. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/16/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16-js.zip)</details>) -- **[Step 17: Fragment Callbacks](steps/17/README.md "Now that we have integrated the dialog, it's time to add some user interaction. The user will definitely want to close the dialog again at some point, so we add a button to close the dialog and assign an event handler.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/17/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17-js.zip)</details>) -- **[Step 18: Icons](steps/18/README.md "Our dialog is still pretty much empty. Since OpenUI5 is shipped with a large icon font that contains more than 500 icons, we will add an icon to greet our users when the dialog is opened.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/18/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18-js.zip)</details>) -- **[Step 19: Aggregation Binding](steps/19/README.md "Now that we have established a good structure for our app, it's time to add some more functionality. We start exploring more features of data binding by adding some invoice data in JSON format that we display in a list below the panel.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/19/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19-js.zip)</details>) -- **[Step 20: Data Types](steps/20/README.md "The list of invoices is already looking nice, but what is an invoice without a price assigned? Typically prices are stored in a technical format and with a /'./' delimiter in the data model. For example, our invoice for pineapples has the calculated price 87.2 without a currency. We are going to use the OpenUI5 data types to format the price properly, with a locale-dependent decimal separator and two digits after the separator.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/20/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20-js.zip) </details>) -- **[Step 21: Expression Binding](steps/21/README.md "Sometimes the predefined types of OpenUI5 are not flexible enough and you want to do a simple calculation or formatting in the view - that is where expressions are really helpful. We use them to format our price according to the current number in the data model.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/21/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21-js.zip)</details>) -- **[Step 22: Custom Formatters](steps/22/README.md "If we want to do a more complex logic for formatting properties of our data model, we can also write a custom formatting function. We will now add a localized status with a custom formatter, because the status in our data model is in a rather technical format.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/22/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22-js.zip)</details>) -- **[Step 23: Filtering](steps/23/README.md "In this step, we add a search field for our product list and define a filter that represents the search term. When searching, the list is automatically updated to show only the items that match the search term.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/23/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23-js.zip)</details>) -- **[Step 24: Sorting and Grouping](steps/24/README.md "To make our list of invoices even more user-friendly, we sort it alphabetically instead of just showing the order from the data model. Additionally, we introduce groups and add the company that ships the products so that the data is easier to consume.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/24/index-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24-js.zip)</details>) -- **[Step 25: Remote OData Service](steps/25/README.md "So far we have worked with local JSON data, but now we will access a real OData service to visualize remote data.")** (🔗 Live Preview *unfeasible* \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25-js.zip)</details>) -- **[Step 26: Mock Server Configuration](steps/26/README.md "We just ran our app against a real service, but for developing and testing our app we do not want to rely on the availability of the “real” service or put additional load on the system where the data service is located.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/26/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26-js.zip)</details>) -- **[Step 27: Unit Test with QUnit](steps/27/README.md "Now that we have a test folder in the app, we can start to increase our test coverage. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/27/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=unit/unitTests) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27-js.zip)</details>) -- **[Step 28: Integration Test with OPA](steps/28/README.md "If we want to test interaction patterns or more visual features of our app, we can also write an integration test. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/28/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=integration/opaTests) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28-js.zip)</details>) -- **[Step 29: Debugging Tools](steps/29/README.md "Even though we have added a basic test coverage in the previous steps, it seems like we accidentally broke our app, because it does not display prices to our invoices anymore. We need to debug the issue and fix it before someone finds out.")** (*code remains unchanged from the previous step*) -- **[Step 30: Routing and Navigation](steps/30/README.md "So far, we have put all app content on one single page. As we add more and more features, we want to split the content and put it on separate pages.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/30/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30-js.zip)</details>) -- **[Step 31: Routing with Parameters](steps/31/README.md "We can now navigate between the overview and the detail page, but the actual item that we selected in the overview is not displayed on the detail page yet. A typical use case for our app is to show additional information for the selected item on the detail page. ")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/31/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31-js.zip)</details>) -- **[Step 32: Routing Back and History](steps/32/README.md "Now we can navigate to our detail page and display an invoice, but we cannot go back to the overview page yet. We'll add a back button to the detail page and implement a function that shows our overview page again.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/32/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32-js.zip)</details>) -- **[Step 33: Custom Controls](steps/33/README.md "In this step, we are going to extend the functionality of OpenUI5 with a custom control. We want to rate the product shown on the detail page, so we create a composition of multiple standard controls using the OpenUI5 extension mechanism and add some glue code to make them work nicely together. This way, we can reuse the control across the app and keep all related functionality in one module.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/33/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33.zip)</details> <details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33-js.zip)</details>) -- **[Step 34: Responsiveness](steps/34/README.md "In this step, we improve the responsiveness of our app. OpenUI5 applications can be run on phone, tablet, and desktop devices and we can configure the application to make best use of the screen estate for each scenario. Fortunately, OpenUI5 controls like the sap.m.Table already deliver a lot of features that we can use.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/34/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34-js.zip)</details>) -- **[Step 35: Device Adaptation](steps/35/README.md "We now configure the visibility and properties of controls based on the device that we run the application on. By making use of the sap.ui.Device API and defining a device model we will make the app look great on many devices.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/35/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35-js.zip)</details>) -- **[Step 36: Content Density](steps/36/README.md "In this step of our Walkthrough tutorial, we adjust the content density based on the user’s device. OpenUI5 contains different content densities allowing you to display larger controls for touch-enabled devices and a smaller, more compact design for devices that are operated by mouse. In our app, we will detect the device and adjust the density accordingly.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/36/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36-js.zip)</details>) -- **[Step 37: Accessibility](steps/37/README.md "In this step we're going to improve the accessibility of our app.")** ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/37/test/mockServer-cdn.html) \| <details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37-js.zip)</details>) -- **[Step 38: Build Your Application](steps/38/README.md "In this step we're going to build our application and consume the speed of a built OpenUI5 application.")** (<details class="ts-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38.zip) </details><details class="js-only" markdown="1"> [📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38-js.zip)</details>) +- **[Step 1: Hello World!](steps/01/README.md)** — As you know OpenUI5 is all about HTML5. Let’s get started with building a first "Hello World" with only HTML. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/01/index.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span> ) +- **[Step 2: Bootstrap](steps/02/README.md)** — Before we can do something with OpenUI5, we need to load and initialize it. This process of loading and initializing OpenUI5 is called bootstrapping. Once this bootstrapping is finished, we simply display an alert. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/02/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span> ) +- **[Step 3: Controls](steps/03/README.md)** — Now it is time to build our first little UI by replacing the "Hello World" text in the HTML body by the OpenUI5 control sap/m/Text. In the beginning, we will use the JavaScript control interface to set up the UI, the control instance is then placed into the HTML body. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/03/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span> ) +- **[Step 4: XML Views](steps/04/README.md)** — Putting all our UI into the index.ts file will very soon result in a messy setup, and there is quite a bit of work ahead of us. So let’s do a first modularization by putting the sap/m/Text control into a dedicated view. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/04/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 5: Controllers](steps/05/README.md)** — In this step, we replace the text with a button and show the "Hello World" message when the button is pressed. The handling of the button's press event is implemented in the controller of the view. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/05/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 6: Modules](steps/06/README.md)** — In OpenUI5, resources are often referred to as modules. In this step, we replace the alert from the last exercise with a proper Message Toast from the sap.m library. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/06/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 7: JSON Model](steps/07/README.md)** — Now that we have set up the view and controller, it’s about time to think about the M in MVC. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/07/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 8: Translatable Texts](steps/08/README.md)** — In this step we move the texts of our UI to a separate resource file. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/08/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 9: Component Configuration](steps/09/README.md)** — After we have introduced all three parts of the Model-View-Controller /(MVC/) concept, we now come to another important structural aspect of OpenUI5. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/09/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 10: Descriptor for Applications](steps/10/README.md)** — All application-specific configuration settings will now further be put in a separate descriptor file called manifest.json. This clearly separates the application coding from the configuration settings and makes our app even more flexible. For example, all SAP Fiori applications are realized as components and come with a descriptor file in order to be hosted in the SAP Fiori launchpad. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/10/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 11: Pages and Panels](steps/11/README.md)** — After all the work on the app structure it’s time to improve the look of our app. We will use two controls from the sap.m library to add a bit more "bling" to our UI. You will also learn about control aggregations in this step. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/11/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 12: Shell Control as Container](steps/12/README.md)** — Now we use a shell control as container for our app and use it as our new root element. The shell takes care of visual adaptation of the application to the device’s screen size by introducing a so-called letterbox on desktop screens. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/12/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 13: Margins and Paddings](steps/13/README.md)** — Our app content is still glued to the corners of the letterbox. To fine-tune our layout, we can add margins and paddings to the controls that we added in the previous step. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/13/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 14: Custom CSS and Theme Colors](steps/14/README.md)** — Sometimes we need to define some more fine-granular layouts and this is when we can use the flexibility of CSS by adding custom style classes to controls and style them as we like. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/14/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 15: Nested Views](steps/15/README.md)** — Our panel content is getting more and more complex and now it is time to move the panel content to a separate view. With that approach, the application structure is much easier to understand, and the individual parts of the app can be reused. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/15/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 16: Dialogs and Fragments](steps/16/README.md)** — In this step, we will take a closer look at another element which can be used to assemble views: the fragment. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/16/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 17: Fragment Callbacks](steps/17/README.md)** — Now that we have integrated the dialog, it's time to add some user interaction. The user will definitely want to close the dialog again at some point, so we add a button to close the dialog and assign an event handler. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/17/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 18: Icons](steps/18/README.md)** — Our dialog is still pretty much empty. Since OpenUI5 is shipped with a large icon font that contains more than 500 icons, we will add an icon to greet our users when the dialog is opened. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/18/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 19: Aggregation Binding](steps/19/README.md)** — Now that we have established a good structure for our app, it's time to add some more functionality. We start exploring more features of data binding by adding some invoice data in JSON format that we display in a list below the panel. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/19/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 20: Data Types](steps/20/README.md)** — The list of invoices is already looking nice, but what is an invoice without a price assigned? Typically prices are stored in a technical format and with a /'./' delimiter in the data model. For example, our invoice for pineapples has the calculated price 87.2 without a currency. We are going to use the OpenUI5 data types to format the price properly, with a locale-dependent decimal separator and two digits after the separator. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/20/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 21: Expression Binding](steps/21/README.md)** — Sometimes the predefined types of OpenUI5 are not flexible enough and you want to do a simple calculation or formatting in the view - that is where expressions are really helpful. We use them to format our price according to the current number in the data model. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/21/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 22: Custom Formatters](steps/22/README.md)** — If we want to do a more complex logic for formatting properties of our data model, we can also write a custom formatting function. We will now add a localized status with a custom formatter, because the status in our data model is in a rather technical format. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/22/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 23: Filtering](steps/23/README.md)** — In this step, we add a search field for our product list and define a filter that represents the search term. When searching, the list is automatically updated to show only the items that match the search term. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/23/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 24: Sorting and Grouping](steps/24/README.md)** — To make our list of invoices even more user-friendly, we sort it alphabetically instead of just showing the order from the data model. Additionally, we introduce groups and add the company that ships the products so that the data is easier to consume. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/24/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 25: Remote OData Service](steps/25/README.md)** — So far we have worked with local JSON data, but now we will access a real OData service to visualize remote data. (🔗 Live Preview *unfeasible* \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 26: Mock Server Configuration](steps/26/README.md)** — We just ran our app against a real service, but for developing and testing our app we do not want to rely on the availability of the “real” service or put additional load on the system where the data service is located. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/26/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 27: Unit Test with QUnit](steps/27/README.md)** — Now that we have a test folder in the app, we can start to increase our test coverage. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/27/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=unit/unitTests) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 28: Integration Test with OPA](steps/28/README.md)** — If we want to test interaction patterns or more visual features of our app, we can also write an integration test. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/28/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=integration/opaTests) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 29: Debugging Tools](steps/29/README.md)** — Even though we have added a basic test coverage in the previous steps, it seems like we accidentally broke our app, because it does not display prices to our invoices anymore. We need to debug the issue and fix it before someone finds out. (*code remains unchanged from the previous step*) +- **[Step 30: Routing and Navigation](steps/30/README.md)** — So far, we have put all app content on one single page. As we add more and more features, we want to split the content and put it on separate pages. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/30/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 31: Routing with Parameters](steps/31/README.md)** — We can now navigate between the overview and the detail page, but the actual item that we selected in the overview is not displayed on the detail page yet. A typical use case for our app is to show additional information for the selected item on the detail page. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/31/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 32: Routing Back and History](steps/32/README.md)** — Now we can navigate to our detail page and display an invoice, but we cannot go back to the overview page yet. We'll add a back button to the detail page and implement a function that shows our overview page again. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/32/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 33: Custom Controls](steps/33/README.md)** — In this step, we are going to extend the functionality of OpenUI5 with a custom control. We want to rate the product shown on the detail page, so we create a composition of multiple standard controls using the OpenUI5 extension mechanism and add some glue code to make them work nicely together. This way, we can reuse the control across the app and keep all related functionality in one module. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/33/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 34: Responsiveness](steps/34/README.md)** — In this step, we improve the responsiveness of our app. OpenUI5 applications can be run on phone, tablet, and desktop devices and we can configure the application to make best use of the screen estate for each scenario. Fortunately, OpenUI5 controls like the sap.m.Table already deliver a lot of features that we can use. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/34/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 35: Device Adaptation](steps/35/README.md)** — We now configure the visibility and properties of controls based on the device that we run the application on. By making use of the sap.ui.Device API and defining a device model we will make the app look great on many devices. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/35/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 36: Content Density](steps/36/README.md)** — In this step of our Walkthrough tutorial, we adjust the content density based on the user’s device. OpenUI5 contains different content densities allowing you to display larger controls for touch-enabled devices and a smaller, more compact design for devices that are operated by mouse. In our app, we will detect the device and adjust the density accordingly. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/36/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 37: Accessibility](steps/37/README.md)** — In this step we're going to improve the accessibility of our app. ([🔗 Live Preview](https://ui5.github.io/tutorials/walkthrough/build/37/test/mockServer-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 38: Build Your Application](steps/38/README.md)** — In this step we're going to build our application and consume the speed of a built OpenUI5 application. (<span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38-js.zip)<span class="lang-suffix"> (JS)</span></span>) ## License diff --git a/packages/walkthrough/steps.json b/packages/walkthrough/steps.json new file mode 100644 index 000000000..8bb23338f --- /dev/null +++ b/packages/walkthrough/steps.json @@ -0,0 +1,348 @@ +{ + "tutorial": "walkthrough", + "namespace": "ui5.tutorial.walkthrough", + "steps": [ + { + "n": 1, + "id": "01", + "title": "Hello World!", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/01/index.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01-js.zip", + "description": "As you know, OpenUI5 is all about HTML5. Let's get started with building a first \"Hello World\" with only HTML. In addition we'll initialize the UI5 CLI, so we can benefit from it from the beginning." + }, + { + "n": 2, + "id": "02", + "title": "Bootstrap", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/02/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02-js.zip", + "description": "Before we can do something with OpenUI5, we need to load and initialize it. This process of loading and initializing OpenUI5 is called bootstrapping. Once this bootstrapping is finished, we simply display an alert." + }, + { + "n": 3, + "id": "03", + "title": "Controls", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/03/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03-js.zip", + "description": "Now it is time to build our first little UI by replacing the “Hello World” text in the HTML body by the OpenUI5 control sap/m/Text. In the beginning, we will create an OpenUI5 control instance and place into the HTML body." + }, + { + "n": 4, + "id": "04", + "title": "XML Views", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/04/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04-js.zip", + "description": "Putting all our UI into the index.ts file will very soon result in a messy setup and there is quite a bit of work ahead of us. So let’s do a first modularization by putting the sap/m/Text control into a dedicated view." + }, + { + "n": 5, + "id": "05", + "title": "Controllers", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/05/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05-js.zip", + "description": "In this step, we replace the text with a button and show the “Hello World” message when the button is pressed. The handling of the button's press event is implemented in the controller of the view." + }, + { + "n": 6, + "id": "06", + "title": "Modules", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/06/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06-js.zip", + "description": "In OpenUI5, resources are often referred to as modules. In this step, we replace the alert from the last exercise with a proper Message Toast from the sap.m library." + }, + { + "n": 7, + "id": "07", + "title": "JSON Model", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/07/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07-js.zip", + "description": "Now that we have set up the view and controller, it’s about time to think about the M in MVC." + }, + { + "n": 8, + "id": "08", + "title": "Translatable Texts", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/08/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08-js.zip", + "description": "In this step we move the texts of our UI to a separate resource file." + }, + { + "n": 9, + "id": "09", + "title": "Component Configuration", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/09/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09-js.zip", + "description": "After we have introduced all three parts of the Model-View-Controller \\(MVC\\) concept, we now come to another important structural aspect of OpenUI5." + }, + { + "n": 10, + "id": "10", + "title": "Descriptor for Applications", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/10/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10-js.zip", + "description": "All application-specific configuration settings will now be put into the manifest. This clearly separates the application coding from the configuration settings and makes our app even more flexible." + }, + { + "n": 11, + "id": "11", + "title": "Pages and Panels", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/11/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11-js.zip", + "description": "After all the work on the app structure it’s time to improve the look of our app. We will use two controls from the sap.m library to add a bit more \"bling\" to our UI. You will also learn about control aggregations in this step." + }, + { + "n": 12, + "id": "12", + "title": "Shell Control as Container", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/12/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12-js.zip", + "description": "Now we use a shell control as container for our app and use it as our new root element. The shell takes care of visual adaptation of the application to the device’s screen size by introducing a so-called letterbox on desktop screens." + }, + { + "n": 13, + "id": "13", + "title": "Margins and Paddings", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/13/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13-js.zip", + "description": "Our app content is still glued to the corners of the letterbox. To fine-tune our layout, we can add margins and paddings to the controls that we added in the previous step." + }, + { + "n": 14, + "id": "14", + "title": "Custom CSS and Theme Colors", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/14/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14-js.zip", + "description": "Sometimes we need to define some more fine-granular layouts and this is when we can use the flexibility of CSS by adding custom style classes to controls and style them as we like." + }, + { + "n": 15, + "id": "15", + "title": "Nested Views", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/15/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15-js.zip", + "description": "Our panel content is getting more and more complex and now it is time to move the panel content to a separate view. With that approach, the application structure is much easier to understand, and the individual parts of the app can be reused." + }, + { + "n": 16, + "id": "16", + "title": "Dialogs and Fragments", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/16/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16-js.zip", + "description": "In this step, we will take a closer look at another element which can be used to assemble views: the fragment." + }, + { + "n": 17, + "id": "17", + "title": "Fragment Callbacks", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/17/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17-js.zip", + "description": "Now that we have integrated the dialog, it's time to add some user interaction. The user will definitely want to close the dialog again at some point, so we add a button to close the dialog and assign an event handler." + }, + { + "n": 18, + "id": "18", + "title": "Icons", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/18/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18-js.zip", + "description": "Our dialog is still pretty much empty. Since OpenUI5 is shipped with a large icon font that contains more than 500 icons, we will add an icon to greet our users when the dialog is opened." + }, + { + "n": 19, + "id": "19", + "title": "Aggregation Binding", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/19/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19-js.zip", + "description": "Now that we have established a good structure for our app, it's time to add some more functionality. We start exploring more features of data binding by adding some invoice data in JSON format that we display in a list below the panel." + }, + { + "n": 20, + "id": "20", + "title": "Data Types", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/20/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20-js.zip", + "description": "The list of invoices is already looking nice, but what is an invoice without a price assigned? Typically prices are stored in a technical format and with a '.' delimiter in the data model. For example, our invoice for pineapples has the calculated price 87.2 without a currency. We are going to use the OpenUI5 data types to format the price properly, with a locale-dependent decimal separator and two digits after the separator." + }, + { + "n": 21, + "id": "21", + "title": "Expression Binding", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/21/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21-js.zip", + "description": "Sometimes the predefined types of OpenUI5 are not flexible enough and you want to do a simple calculation or formatting in the view - that is where expressions are really helpful. We use them to format our price according to the current number in the data model." + }, + { + "n": 22, + "id": "22", + "title": "Custom Formatters", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/22/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22-js.zip", + "description": "If we want to do a more complex logic for formatting properties of our data model, we can also write a custom formatting function. We will now add a localized status with a custom formatter, because the status in our data model is in a rather technical format." + }, + { + "n": 23, + "id": "23", + "title": "Filtering", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/23/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23-js.zip", + "description": "In this step, we add a search field for our product list and define a filter that represents the search term. When searching, the list is automatically updated to show only the items that match the search term." + }, + { + "n": 24, + "id": "24", + "title": "Sorting and Grouping", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/24/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24-js.zip", + "description": "To make our list of invoices even more user-friendly, we sort it alphabetically instead of just showing the order from the data model. Additionally, we introduce groups and add the company that ships the products so that the data is easier to consume." + }, + { + "n": 25, + "id": "25", + "title": "Remote OData Service", + "previewUrl": null, + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25-js.zip", + "description": "So far we have worked with local JSON data, but now we will access a real OData service to visualize remote data." + }, + { + "n": 26, + "id": "26", + "title": "Mock Server Configuration", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/26/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26-js.zip", + "description": "We just ran our app against a real service, but for developing and testing our app we do not want to rely on the availability of the “real” service or put additional load on the system where the data service is located." + }, + { + "n": 27, + "id": "27", + "title": "Unit Test with QUnit", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/27/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=unit/unitTests", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27-js.zip", + "description": "Now that we have a test folder in the app, we can start to increase our test coverage." + }, + { + "n": 28, + "id": "28", + "title": "Integration Test with OPA", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/28/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=integration/opaTests", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28-js.zip", + "description": "If we want to test interaction patterns or more visual features of our app, we can also write an integration test." + }, + { + "n": 29, + "id": "29", + "title": "Debugging Tools", + "previewUrl": null, + "zipTs": null, + "zipJs": null, + "description": "Even though we have added a basic test coverage in the previous steps, it seems like we accidentally broke our app, because it does not display prices to our invoices anymore. We need to debug the issue and fix it before someone finds out." + }, + { + "n": 30, + "id": "30", + "title": "Routing and Navigation", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/30/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30-js.zip", + "description": "So far, we have put all app content on one single page. As we add more and more features, we want to split the content and put it on separate pages." + }, + { + "n": 31, + "id": "31", + "title": "Routing with Parameters", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/31/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31-js.zip", + "description": "We can now navigate between the overview and the detail page, but the actual item that we selected in the overview is not displayed on the detail page yet. A typical use case for our app is to show additional information for the selected item on the detail page." + }, + { + "n": 32, + "id": "32", + "title": "Routing Back and History", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/32/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32-js.zip", + "description": "Now we can navigate to our detail page and display an invoice, but we cannot go back to the overview page yet. We'll add a back button to the detail page and implement a function that shows our overview page again." + }, + { + "n": 33, + "id": "33", + "title": "Custom Controls", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/33/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33-js.zip", + "description": "In this step, we are going to extend the functionality of OpenUI5 with a custom control. We want to rate the product shown on the detail page, so we create a composition of multiple standard controls using the OpenUI5 extension mechanism and add some glue code to make them work nicely together. This way, we can reuse the control across the app and keep all related functionality in one module." + }, + { + "n": 34, + "id": "34", + "title": "Responsiveness", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/34/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34-js.zip", + "description": "In this step, we improve the responsiveness of our app. OpenUI5 applications can be run on phone, tablet, and desktop devices and we can configure the application to make best use of the screen estate for each scenario. Fortunately, OpenUI5 controls like the sap.m.Table already deliver a lot of features that we can use." + }, + { + "n": 35, + "id": "35", + "title": "Device Adaptation", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/35/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35-js.zip", + "description": "We now configure the visibility and properties of controls based on the device that we run the application on. By making use of the sap.ui.Device API and defining a device model we will make the app look great on many devices." + }, + { + "n": 36, + "id": "36", + "title": "Content Density", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/36/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36-js.zip", + "description": "In this step of our Walkthrough tutorial, we adjust the content density based on the user’s device. Content density refers to the spacing and sizing of the UI controls and elements within your application. OpenUI5 contains different content densities allowing you to display larger controls for touch-enabled devices and a smaller, more compact design for devices that are operated by mouse. In our app, we will detect the device and adjust the density accordingly." + }, + { + "n": 37, + "id": "37", + "title": "Accessibility", + "previewUrl": "https://ui5.github.io/tutorials/walkthrough/build/37/test/mockServer-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37-js.zip", + "description": "In this step we're going to improve the accessibility of our app." + }, + { + "n": 38, + "id": "38", + "title": "Build Your Application", + "previewUrl": null, + "zipTs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38.zip", + "zipJs": "https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38-js.zip", + "description": "In this step we're going to build our application and consume the speed of a built OpenUI5 application." + } + ] +} diff --git a/packages/walkthrough/steps/01/README.md b/packages/walkthrough/steps/01/README.md index 9073c3713..f3c9e5fe9 100644 --- a/packages/walkthrough/steps/01/README.md +++ b/packages/walkthrough/steps/01/README.md @@ -9,7 +9,7 @@ As you know, OpenUI5 is all about HTML5. Let's get started with building a first ### Preview -![](assets/loio1dd456361379431aab7e5bcdaaeff00f_LowRes.png "The browser shows the text "Hello World"") +![The browser shows the text "Hello World"](assets/loio1dd456361379431aab7e5bcdaaeff00f_LowRes.png "The browser shows the text "Hello World"") <sup>*The browser shows the text "Hello World"*</sup> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 1](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 1](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 1](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 1](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp \(New\) diff --git a/packages/walkthrough/steps/02/README.md b/packages/walkthrough/steps/02/README.md index a6bb3391b..f1b6ce750 100644 --- a/packages/walkthrough/steps/02/README.md +++ b/packages/walkthrough/steps/02/README.md @@ -9,7 +9,7 @@ Before we can do something with OpenUI5, we need to load and initialize it. This ### Preview -![](assets/loio0f6b6b9dc46a474da9287c382c8d3456_LowRes.png "An alert "UI5 is ready" is displayed") +![An alert "UI5 is ready" is displayed](assets/loio0f6b6b9dc46a474da9287c382c8d3456_LowRes.png "An alert "UI5 is ready" is displayed") <sup>*An alert "UI5 is ready" is displayed*</sup> @@ -18,17 +18,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 2](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 2](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### Development Environment @@ -110,11 +100,12 @@ Let's go through the compiler options specified in the file: </details> -### webapp/index.?s \(New\) +### webapp/index.ts/.js \(New\) -Now let's move on to the UI work. We create a new `index.?s` script in the webapp folder. In this script, we add a native `alert()` method with the message "UI5 is ready". +Now let's move on to the UI work. We create a new `index.ts/.js` script in the webapp folder. In this script, we add a native `alert()` method with the message "UI5 is ready". ```js +// webapp/index.js alert("UI5 is ready"); ``` diff --git a/packages/walkthrough/steps/03/README.md b/packages/walkthrough/steps/03/README.md index 837978c08..113f7bc5c 100644 --- a/packages/walkthrough/steps/03/README.md +++ b/packages/walkthrough/steps/03/README.md @@ -8,7 +8,7 @@ Now it is time to build our first little UI by replacing the “Hello World” t ### Preview -![](assets/loio30a42d381b9e4388bf7fdc0b941e5381_LowRes.png "The "Hello World" text is now displayed by a OpenUI5 control") +![The "Hello World" text is now displayed by a OpenUI5 control](assets/loio30a42d381b9e4388bf7fdc0b941e5381_LowRes.png "The "Hello World" text is now displayed by a OpenUI5 control") <sup>*The "Hello World" text is now displayed by an OpenUI5 control*</sup> @@ -17,17 +17,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 3](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 3](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** <details class="ts-only" markdown="1"> @@ -44,7 +34,7 @@ npm install @openui5/types --save-dev </details> -### webapp/index.?s +### webapp/index.ts/.js <details class="ts-only" markdown="1"> @@ -70,6 +60,7 @@ To place the text control to our HTML document, we chain the constructor call of </details> ```ts +// webapp/index.ts import Text from "sap/m/Text"; new Text({ @@ -78,6 +69,7 @@ new Text({ ``` ```js +// webapp/index.js sap.ui.define(["sap/m/Text"], function (Text) { "use strict"; diff --git a/packages/walkthrough/steps/04/README.md b/packages/walkthrough/steps/04/README.md index eeada18cc..7f21458d1 100644 --- a/packages/walkthrough/steps/04/README.md +++ b/packages/walkthrough/steps/04/README.md @@ -10,7 +10,7 @@ When working with OpenUI5, we recommend the use of XML views, as this produces t ### Preview -![](assets/loio05f6775a39d3409ea673f4acc3812142_LowRes.png "The "Hello World" text is now displayed by a OpenUI5 control \(No visual changes to last step\)") +![The "Hello World" text is now displayed by a OpenUI5 control \(No visual changes to last step\)](assets/loio05f6775a39d3409ea673f4acc3812142_LowRes.png "The "Hello World" text is now displayed by a OpenUI5 control \(No visual changes to last step\)") <sup>*The "Hello World" text is now displayed by an OpenUI5 control \(No visual changes to last step\)*</sup> @@ -20,17 +20,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 4](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 4](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-04-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/view/App.view.xml \(New\) @@ -57,11 +47,12 @@ We have created an XML view that displays a text control with the text "Hello Wo *** -### webapp/index.?s +### webapp/index.ts/.js -As a next step, we are going to replace the `sap/m/Text` control in our `index.?s` file with the app view that we've just created. To do this, we utilize the `XMLView.create` function, which is a part of the `sap/ui/core/mvc/View` module. This function needs a `viewName` property, which indicates the resource that needs to be loaded. The `viewName` is a combination of the namespace defined in the bootstrap and the path to the app view, but without the ".view.xml" extension. In addition, we set the `id` property to "app". Providing a stable ID is beneficial as it offers an easy and consistent way to identify and refer to specific views and elements in your code, thus helping to keep your code organized. +As a next step, we are going to replace the `sap/m/Text` control in our `index.ts/.js` file with the app view that we've just created. To do this, we utilize the `XMLView.create` function, which is a part of the `sap/ui/core/mvc/View` module. This function needs a `viewName` property, which indicates the resource that needs to be loaded. The `viewName` is a combination of the namespace defined in the bootstrap and the path to the app view, but without the ".view.xml" extension. In addition, we set the `id` property to "app". Providing a stable ID is beneficial as it offers an easy and consistent way to identify and refer to specific views and elements in your code, thus helping to keep your code organized. ```ts +// webapp/index.ts import XMLView from "sap/ui/core/mvc/XMLView"; XMLView.create({ @@ -74,6 +65,7 @@ XMLView.create({ ``` ```js +// webapp/index.js sap.ui.define(["sap/ui/core/mvc/XMLView"], function (XMLView) { "use strict"; diff --git a/packages/walkthrough/steps/05/README.md b/packages/walkthrough/steps/05/README.md index 4b4975ca1..d0a569fd1 100644 --- a/packages/walkthrough/steps/05/README.md +++ b/packages/walkthrough/steps/05/README.md @@ -9,7 +9,7 @@ In this step, we replace the text with a button and show the “Hello World” m ### Preview -![](assets/loiocedfdf89b30643ddbfcab1fe50bfa892_LowRes.png "A Say Hello button is added") +![A Say Hello button is added](assets/loiocedfdf89b30643ddbfcab1fe50bfa892_LowRes.png "A Say Hello button is added") <sup>*A Say Hello button is added*</sup> @@ -19,26 +19,17 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 5](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 download step 5](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 5](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 5](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-05-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/App.controller.?s \(New\) +### webapp/controller/App.controller.ts/.js \(New\) First of all, we need a controller for our app view that defines how the view should react to user inputs, such as a button press event. -We create a new folder called `controller` inside the `webapp` folder. This folder will hold all our controller files. Inside the `controller` folder, we create a new file called `App.controller.?s`. We define the app controller in its own file by extending the OpenUI5-provided `sap/ui/core/mvc/Controller`. In the beginning, it holds only a single function called `onShowHello` that shows an alert with the static text "Hello World". +We create a new folder called `controller` inside the `webapp` folder. This folder will hold all our controller files. Inside the `controller` folder, we create a new file called `App.controller.ts/.js`. We define the app controller in its own file by extending the OpenUI5-provided `sap/ui/core/mvc/Controller`. In the beginning, it holds only a single function called `onShowHello` that shows an alert with the static text "Hello World". ```ts +// webapp/controller/App.controller.ts import Controller from "sap/ui/core/mvc/Controller"; /** @@ -54,6 +45,7 @@ export default class AppController extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) { "use strict"; @@ -108,7 +100,7 @@ A view does not necessarily need an explicitly assigned controller. You do not h - Event handlers are prefixed with `on` -- Controller names always end with `*.controller.?s` +- Controller names always end with `*.controller.ts/.js`   diff --git a/packages/walkthrough/steps/06/README.md b/packages/walkthrough/steps/06/README.md index 29b18b4a2..eca32c429 100644 --- a/packages/walkthrough/steps/06/README.md +++ b/packages/walkthrough/steps/06/README.md @@ -8,7 +8,7 @@ In OpenUI5, resources are often referred to as modules. In this step, we replace ### Preview -![](assets/loio2f629a95211f49afa367b60d233fb390_LowRes.png "A message toast displays the "Hello World" message") +![A message toast displays the "Hello World" message](assets/loio2f629a95211f49afa367b60d233fb390_LowRes.png "A message toast displays the "Hello World" message") <sup>*A message toast displays the "Hello World" message*</sup> @@ -18,25 +18,16 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 6](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 6](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-06-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/App.controller.?s +### webapp/controller/App.controller.ts/.js We now replace the native `alert` function with the `show` method of the `sap.m.MessageToast` control of OpenUI5. ```ts +// webapp/controller/App.controller.ts import MessageToast from "sap/m/MessageToast"; import Controller from "sap/ui/core/mvc/Controller"; @@ -52,6 +43,7 @@ export default class AppController extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/m/MessageToast", "sap/ui/core/mvc/Controller"], function (MessageToast, Controller) { "use strict"; diff --git a/packages/walkthrough/steps/07/README.md b/packages/walkthrough/steps/07/README.md index 6d7dcc312..9fc8783d8 100644 --- a/packages/walkthrough/steps/07/README.md +++ b/packages/walkthrough/steps/07/README.md @@ -10,7 +10,7 @@ We'll create a view model in our controller, add an input field to our app, bind ### Preview -![](assets/loioafc105517a644407bd90662e3d94ea01_LowRes.png "An input field and a description displaying the value of the input field") +![An input field and a description displaying the value of the input field](assets/loioafc105517a644407bd90662e3d94ea01_LowRes.png "An input field and a description displaying the value of the input field") <sup>*An input field and a description displaying the value of the input field*</sup> @@ -20,20 +20,10 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 7](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 7](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-07-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/App.controller.?s +### webapp/controller/App.controller.ts/.js In the controller, we'll create a new data model and link it to the view that is related to the controller. The best time to create a model is during the `onInit` method. This is a special method in the Controller class that is automatically invoked by the framework when the controller is first set up. @@ -43,6 +33,7 @@ Next, we need to link this data model to our view. We do this by first obtaining ```ts +// webapp/controller/App.controller.ts import MessageToast from "sap/m/MessageToast"; import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -70,6 +61,7 @@ export default class AppController extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/m/MessageToast", "sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel"], function (MessageToast, Controller, JSONModel) { "use strict"; diff --git a/packages/walkthrough/steps/08/README.md b/packages/walkthrough/steps/08/README.md index 28b47b796..3c3671374 100644 --- a/packages/walkthrough/steps/08/README.md +++ b/packages/walkthrough/steps/08/README.md @@ -10,7 +10,7 @@ This way, they are all in a central place and can be easily translated into othe ### Preview -![](assets/loio0eb579e2f2a64c5a9894086322c7faa0_LowRes.png "An input field and a description displaying the value of the input field \(No visual changes to last step\(") +![An input field and a description displaying the value of the input field \(No visual changes to last step\(](assets/loio0eb579e2f2a64c5a9894086322c7faa0_LowRes.png "An input field and a description displaying the value of the input field \(No visual changes to last step\(") <sup>*An input field and a description displaying the value of the input field \(No visual changes to last step\)*</sup> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 8](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 8](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-08-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties \(New\) @@ -47,7 +37,7 @@ In this tutorial we'll only have one properties file. However, in real-world pro *** -### webapp/controller/App.controller.?s +### webapp/controller/App.controller.ts/.js In the controller, we'll create a new resource model that refers to our resource bundle file (`i18n.properties`) and link it to the view associated with the controller. This allows us to bind control properties in the view to translatable texts. We'll also modify the `onShowHello` event handler function to replace the static "Hello World" text with a dynamic greeting text. @@ -57,6 +47,7 @@ In the `onShowHello` event handler function, we first get access to the data mod ```ts +// webapp/controller/App.controller.ts import MessageToast from "sap/m/MessageToast"; import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -97,6 +88,7 @@ export default class AppController extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/m/MessageToast", "sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel", "sap/ui/model/resource/ResourceModel"], function (MessageToast, Controller, JSONModel, ResourceModel) { "use strict"; diff --git a/packages/walkthrough/steps/09/README.md b/packages/walkthrough/steps/09/README.md index 6b0e3b431..242f39100 100644 --- a/packages/walkthrough/steps/09/README.md +++ b/packages/walkthrough/steps/09/README.md @@ -13,45 +13,36 @@ By encapsulating our application as a component, we can seamlessly integrate it ### Preview -![](assets/loiocac9bcfa902c44c496d115acd7ee7376_LowRes.png "An input field and a description displaying the value of the input field (No visual changes to last step)") +![An input field and a description displaying the value of the input field (No visual changes to last step)](assets/loiocac9bcfa902c44c496d115acd7ee7376_LowRes.png "An input field and a description displaying the value of the input field (No visual changes to last step)") <sup>*An input field and a description displaying the value of the input field \(No visual changes to last step\)*</sup> You can access the live preview by clicking on this link: [🔗 Live Preview of Step 9](https://ui5.github.io/tutorials/walkthrough/build/09/index-cdn.html). -After this step your project structure will look like the figure below. We will create the `Component.?s` file now and modify the related files in the app. +After this step your project structure will look like the figure below. We will create the `Component.ts/.js` file now and modify the related files in the app. -![](assets/loio1e237a36972a44ac8522dd1a540ac062_LowRes.png "Folder Structure for this Step") +![Folder Structure for this Step](assets/loio1e237a36972a44ac8522dd1a540ac062_LowRes.png "Folder Structure for this Step") <sup>*Folder Structure for this Step*</sup> *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 9](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 9](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-09-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/Component.?s \(New\) +### webapp/Component.ts/.js \(New\) -We navigate to the `webapp` folder and place the `Component.?s` file to it. This file is commonly referred to as the component controller. A component is organized in a unique namespace \(which is synonymous with the application namespace\). All required and optional resources of the component have to be organized in the namespace of the component. +We navigate to the `webapp` folder and place the `Component.ts/.js` file to it. This file is commonly referred to as the component controller. A component is organized in a unique namespace \(which is synonymous with the application namespace\). All required and optional resources of the component have to be organized in the namespace of the component. We define the component by extending `sap/ui/core/UIComponent` and supplement the component with additional metadata. Within the `interfaces` settings, we specify that the component should implement the `sap/ui/core/IAsyncContentCreation` interface. This allows the component to be generated asynchronously, which in turn sets the component's rootView and router configuration to async. When the component is instantiated, OpenUI5 automatically calls the `init` function of the component. It's obligatory to make the super call to the `init` function of the base class in the overridden `init` method. In this section, we also instantiate our data model and the `i18n` model, similar to what we did earlier in the `onInit` function of our app controller (*Hint: besides the `bundleName` we also specifiy the `supportedLocales` with an empty string as value in an array and an empty `fallbackLocale` to ensure that just the default `messagebundle.properties` is loaded without a language suffix to avoid 404s during development!*). -Finally we call the `createContent` hook method of the component. This method creates the content \(UI Control Tree\) of this component. Here, we create the view as we did in the `index.?s` file to set our app view as the root view of the component. +Finally we call the `createContent` hook method of the component. This method creates the content \(UI Control Tree\) of this component. Here, we create the view as we did in the `index.ts/.js` file to set our app view as the root view of the component. ```ts +// webapp/Component.ts import Control from "sap/ui/core/Control"; import XMLView from "sap/ui/core/mvc/XMLView"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -96,6 +87,7 @@ export default class Component extends UIComponent { ``` ```js +// webapp/Component.js sap.ui.define(["sap/ui/core/UIComponent", "sap/ui/core/mvc/XMLView", "sap/ui/model/json/JSONModel", "sap/ui/model/resource/ResourceModel"], function (UIComponent, XMLView, JSONModel, ResourceModel) { "use strict"; @@ -139,11 +131,12 @@ Be aware that the models are set directly on the component and not on the root v *** -### webapp/controller/App.controller.?s +### webapp/controller/App.controller.ts/.js We delete the `onInit` function from the app controller; this is now done in the component controller. ```ts +// webapp/controller/App.controller.ts import MessageToast from "sap/m/MessageToast"; import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -167,6 +160,7 @@ export default class AppController extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/m/MessageToast", "sap/ui/core/mvc/Controller"], function (MessageToast, Controller) { "use strict"; @@ -191,7 +185,7 @@ sap.ui.define(["sap/m/MessageToast", "sap/ui/core/mvc/Controller"], function (Me ``` *** -#### webapp/index.?s +#### webapp/index.ts/.js We'll replace the view with a UI component. To do this, we use a control called `ComponentContainer`. This control allows us to wrap a UI Component and place it in our HTML document. We configure this instance by providing the following options: @@ -208,6 +202,7 @@ We'll replace the view with a UI component. To do this, we use a control called Finally, we position our newly created `ComponentContainer` control within the HTML element with the id `content`. ```ts +// webapp/index.ts import ComponentContainer from "sap/ui/core/ComponentContainer"; new ComponentContainer({ @@ -223,6 +218,7 @@ new ComponentContainer({ ``` ```js +// webapp/index.js sap.ui.define(["sap/ui/core/ComponentContainer"], function (ComponentContainer) { "use strict"; @@ -242,7 +238,7 @@ sap.ui.define(["sap/ui/core/ComponentContainer"], function (ComponentContainer) ### Conventions -- The component is named `Component.?s`. +- The component is named `Component.ts/.js`. - Together with all UI assets of the app, the component is located in the `webapp` folder. diff --git a/packages/walkthrough/steps/10/README.md b/packages/walkthrough/steps/10/README.md index b6f1e6e4d..ab68c8043 100644 --- a/packages/walkthrough/steps/10/README.md +++ b/packages/walkthrough/steps/10/README.md @@ -9,7 +9,7 @@ Instead of relying on a local HTML file for the bootstrap, the manifest is parse *** ### Preview -![](assets/loio7b2aef85c016485da4a31c087bf4c0f0_LowRes.png "An input field and a description displaying the value of the input field \(No visual changes to last step\)") +![An input field and a description displaying the value of the input field \(No visual changes to last step\)](assets/loio7b2aef85c016485da4a31c087bf4c0f0_LowRes.png "An input field and a description displaying the value of the input field \(No visual changes to last step\)") <sup>*An input field and a description displaying the value of the input field \(No visual changes to last step\)*</sub> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 10](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 10](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-10-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties @@ -161,13 +151,14 @@ In our current scenario, we only have one model called `i18n`, which is a resour *** -### webapp/Component.?s +### webapp/Component.ts/.js To apply the settings specified in the manifest to the component, we need to include the manifest in the component's metadata. To do this, we add a `manifest` property to the `metadata` section of the component and set it to "json". This property acts as a reference to the `manifest.json` file, which will be loaded and used. Now that the resource model is automatically instantiated based on the configuration in the manifest, we can safely remove the corresponding code block from the `init` method in our component controller. This also means that we can remove the import statement for the `ResourceModel` module from `sap/ui/model/resource/ResourceModel`, as it is no longer needed. Additionally, we can remove the `createContent` call since the configuration of the rootView is specified in the manifest and therefore makes the implementation in this method unnecessary. ```ts +// webapp/Component.ts import UIComponent from "sap/ui/core/UIComponent"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -197,6 +188,7 @@ export default class Component extends UIComponent { ``` ```js +// webapp/Component.js sap.ui.define(["sap/ui/core/UIComponent", "sap/ui/model/json/JSONModel"], function (UIComponent, JSONModel) { "use strict"; @@ -259,7 +251,7 @@ It's worth noting that the `ComponentSupport` module enforces asynchronous loadi </html> ``` -We can now delete our `index.?s` file, because our component is now initiated directly in the HTML markup. +We can now delete our `index.ts/.js` file, because our component is now initiated directly in the HTML markup. *** diff --git a/packages/walkthrough/steps/11/README.md b/packages/walkthrough/steps/11/README.md index 9a61b5cd4..49c487ce9 100644 --- a/packages/walkthrough/steps/11/README.md +++ b/packages/walkthrough/steps/11/README.md @@ -8,7 +8,7 @@ After all the work on the app structure it’s time to improve the look of our a ### Preview -![](assets/loio97feb5417c89462ead5b4259f3ecfd47_LowRes.png "A panel is now displaying the controls from the previous steps") +![A panel is now displaying the controls from the previous steps](assets/loio97feb5417c89462ead5b4259f3ecfd47_LowRes.png "A panel is now displaying the controls from the previous steps") <sup>*A panel is now displaying the controls from the previous steps*</sup> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 11](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 11](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-11-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties diff --git a/packages/walkthrough/steps/12/README.md b/packages/walkthrough/steps/12/README.md index ecdd05ad8..efaf345e0 100644 --- a/packages/walkthrough/steps/12/README.md +++ b/packages/walkthrough/steps/12/README.md @@ -8,7 +8,7 @@ Now we use a shell control as container for our app and use it as our new root e ### Preview -![](assets/loio0becf3ee81f5486a864e3b39ba036402_LowRes.png "The app is now run in a shall that limits the app width") +![The app is now run in a shall that limits the app width](assets/loio0becf3ee81f5486a864e3b39ba036402_LowRes.png "The app is now run in a shall that limits the app width") <sup>*The app is now run in a shell that limits the app width*</sup> @@ -18,17 +18,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 12](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 12](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 12](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 12](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-12-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/view/App.view.xml diff --git a/packages/walkthrough/steps/13/README.md b/packages/walkthrough/steps/13/README.md index 6b57edd76..a48ffca7e 100644 --- a/packages/walkthrough/steps/13/README.md +++ b/packages/walkthrough/steps/13/README.md @@ -10,7 +10,7 @@ Instead of manually adding CSS to the controls, we will use the standard classes ### Preview -![](assets/loio0becf3ee81f5486a864e3b39ba036402_LowRes.png "The layout of the panel and its content now has margins and padding") +![The layout of the panel and its content now has margins and padding](assets/loio0becf3ee81f5486a864e3b39ba036402_LowRes.png "The layout of the panel and its content now has margins and padding") <sup>*The layout of the panel and its content now has margins and padding*</sup> @@ -20,17 +20,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 13](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 13](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 13](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 13](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-13-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/view/App.view.xml diff --git a/packages/walkthrough/steps/14/README.md b/packages/walkthrough/steps/14/README.md index 92bf4f26f..36647909f 100644 --- a/packages/walkthrough/steps/14/README.md +++ b/packages/walkthrough/steps/14/README.md @@ -11,7 +11,7 @@ Sometimes we need to define some more fine-granular layouts and this is when we ### Preview -![](assets/loiod9a40e539b7c49c485be821efbd3821f_LowRes.png "The space between the button and the input field is now smaller and the output text is bold") +![The space between the button and the input field is now smaller and the output text is bold](assets/loiod9a40e539b7c49c485be821efbd3821f_LowRes.png "The space between the button and the input field is now smaller and the output text is bold") <sup>*The space between the button and the input field is now smaller and the output text is bold*</sup> @@ -22,17 +22,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 14](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 14](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 14](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 14](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-14-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/css/style.css \(New\) diff --git a/packages/walkthrough/steps/15/README.md b/packages/walkthrough/steps/15/README.md index 4c391b99f..9855b5f28 100644 --- a/packages/walkthrough/steps/15/README.md +++ b/packages/walkthrough/steps/15/README.md @@ -8,7 +8,7 @@ Our panel content is getting more and more complex and now it is time to move th ### Preview -![](assets/loiof3724d2f97e94a78b27d8ab01ff9c37d_LowRes.png "The panel content is now refactored to a separate view \(No visual changes to last step\)") +![The panel content is now refactored to a separate view \(No visual changes to last step\)](assets/loiof3724d2f97e94a78b27d8ab01ff9c37d_LowRes.png "The panel content is now refactored to a separate view \(No visual changes to last step\)") <sup>*The panel content is now refactored to a separate view \(No visual changes to last step\)*</sup> @@ -19,24 +19,15 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 15](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 15](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 15](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 15](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-15-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/HelloPanel.controller.?s \(New\) +### webapp/controller/HelloPanel.controller.ts/.js \(New\) -In folder `webapp/controller` we create a new `HelloPanel.controller.?s` file and move the method `onShowHello` of the app controller to it, so we get a reusable asset. +In folder `webapp/controller` we create a new `HelloPanel.controller.ts/.js` file and move the method `onShowHello` of the app controller to it, so we get a reusable asset. ```ts +// webapp/controller/HelloPanel.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import MessageToast from "sap/m/MessageToast"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -62,6 +53,7 @@ export default class HelloPanel extends Controller { ``` ```js +// webapp/controller/HelloPanel.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast"], function (Controller, MessageToast) { "use strict"; @@ -135,11 +127,12 @@ In the App view, we remove the panel control and its content and put the `XMLVie </mvc:View> ``` -### webapp/controller/App.controller.?s +### webapp/controller/App.controller.ts/.js We remove the `onShowHello` method from the App controller, as this is not needed anymore. ```ts +// webapp/controller/App.controller.ts import Controller from "sap/ui/core/mvc/Controller"; /** * @namespace ui5.tutorial.walkthrough.controller @@ -151,6 +144,7 @@ export default class App extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) { "use strict"; diff --git a/packages/walkthrough/steps/16/README.md b/packages/walkthrough/steps/16/README.md index ae1a167ee..d9f1e7d53 100644 --- a/packages/walkthrough/steps/16/README.md +++ b/packages/walkthrough/steps/16/README.md @@ -14,7 +14,7 @@ We will now add a dialog to our app. Dialogs are special, because they open on t ### Preview -![](assets/loio0916080895e144ed8b31963bfb18e17f_LowRes.png "A dialog opens when the new "Say Hello With Dialog" button is clicked") +![A dialog opens when the new "Say Hello With Dialog" button is clicked](assets/loio0916080895e144ed8b31963bfb18e17f_LowRes.png "A dialog opens when the new "Say Hello With Dialog" button is clicked") <sup>*A dialog opens when the new “Say Hello With Dialog” button is clicked*</sup> @@ -25,17 +25,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 16](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 16](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 16](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 16](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-16-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/view/HelloDialog.fragment.xml \(New\) @@ -54,7 +44,7 @@ We add a new XML file to declaratively define our dialog in a fragment. The Frag   The syntax is similar to a view, but since fragments do not have a controller this attribute is missing. Also, the fragment does not have any footprint in the DOM tree of the app, and there is no control instance of the fragment itself (only the contained controls). It is simply a container for a set of reuse controls. -### webapp/controller/HelloPanel.controller.?s +### webapp/controller/HelloPanel.controller.ts/.js In the HelloPanel controller, we define a new event handler function `onOpenDialog` which calls the dialog in the HelloDialog fragment when triggered. To do so we need the `sap.m.Dialog` module. @@ -63,6 +53,7 @@ Using async/await, we handle the opening of the dialog asynchronously whenever t If the dialog fragment does not exist yet, the fragment is instantiated by calling the `loadFragment` API. We then store the dialog on the controller instance. This allows us to reuse the dialog every time the event is triggered again. ```ts +// webapp/controller/HelloPanel.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import MessageToast from "sap/m/MessageToast"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -89,6 +80,7 @@ export default class HelloPanel extends Controller { ``` ```js +// webapp/controller/HelloPanel.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast"], function (Controller, MessageToast) { "use strict"; diff --git a/packages/walkthrough/steps/17/README.md b/packages/walkthrough/steps/17/README.md index 357059633..ddb300017 100644 --- a/packages/walkthrough/steps/17/README.md +++ b/packages/walkthrough/steps/17/README.md @@ -8,7 +8,7 @@ Now that we have integrated the dialog, it's time to add some user interaction. ### Preview -![](assets/loioc351bbd078824c43bf1758b0c3679cbd_LowRes.png "The dialog now has an "OK" button") +![The dialog now has an "OK" button](assets/loioc351bbd078824c43bf1758b0c3679cbd_LowRes.png "The dialog now has an "OK" button") <sup>*The dialog now has an "OK" button to close the dialog*</sup> @@ -17,24 +17,15 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 17](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 17](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 17](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 17](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-17-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/HelloPanel.controller.?s +### webapp/controller/HelloPanel.controller.ts/.js We add an `onCloseDialog` event handler function into the HelloPanel controller file that closes the dialog when triggered. To get the dialog instance we use the `byId` function and then call the `close` function of the dialog. ```ts +// webapp/controller/HelloPanel.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import MessageToast from "sap/m/MessageToast"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -69,6 +60,7 @@ export default class HelloPanel extends Controller { ``` ```js +// webapp/controller/HelloPanel.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast"], function (Controller, MessageToast) { "use strict"; diff --git a/packages/walkthrough/steps/18/README.md b/packages/walkthrough/steps/18/README.md index d21eaa02c..21fa68209 100644 --- a/packages/walkthrough/steps/18/README.md +++ b/packages/walkthrough/steps/18/README.md @@ -8,7 +8,7 @@ Our dialog is still pretty much empty. Since OpenUI5 is shipped with a large ico ### Preview -![](assets/loiofbc48e23cc7d45e393cc95bbbfc6e0a3_LowRes.png "An icon is now displayed in the dialog box") +![An icon is now displayed in the dialog box](assets/loiofbc48e23cc7d45e393cc95bbbfc6e0a3_LowRes.png "An icon is now displayed in the dialog box") <sup>*An icon is now displayed in the dialog box*</sup> @@ -17,17 +17,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 18](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 18](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 18](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 18](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-18-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/view/HelloPanel.view.xml diff --git a/packages/walkthrough/steps/19/README.md b/packages/walkthrough/steps/19/README.md index 689ce0c50..7f70613cf 100644 --- a/packages/walkthrough/steps/19/README.md +++ b/packages/walkthrough/steps/19/README.md @@ -7,7 +7,7 @@ Now that we have established a good structure for our app, it's time to add some ### Preview -![](assets/loiob05bdb47393b4abda3e1b54498959c38_LowRes.png "A list of invoices is displayed below the panel") +![A list of invoices is displayed below the panel](assets/loiob05bdb47393b4abda3e1b54498959c38_LowRes.png "A list of invoices is displayed below the panel") <sup>*A list of invoices is displayed below the panel*</sup> @@ -17,17 +17,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 19](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 19](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 19](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 19](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-19-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/model/localInvoices.json \(New\) diff --git a/packages/walkthrough/steps/20/README.md b/packages/walkthrough/steps/20/README.md index b6b176b75..f312729c6 100644 --- a/packages/walkthrough/steps/20/README.md +++ b/packages/walkthrough/steps/20/README.md @@ -8,7 +8,7 @@ The list of invoices is already looking nice, but what is an invoice without a p ### Preview -![](assets/loiodc9e919119564ddab78b8d0550ecfa9b_LowRes.png "The list of invoices with prices and number units") +![The list of invoices with prices and number units](assets/loiodc9e919119564ddab78b8d0550ecfa9b_LowRes.png "The list of invoices with prices and number units") <sup>*The list of invoices with prices and number units*</sup> @@ -17,26 +17,17 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 20](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 20](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 20](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 20](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-20-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/InvoiceList.controller.?s \(New\) +### webapp/controller/InvoiceList.controller.ts/.js \(New\) We want to display in our list view the price in Euro. Since currency information isn't available in our backend data model, we'll handle the currency formatting within the application. We'll create a controller for the InvoiceList view and use a JSON model (`sap/ui/model/json/JSONModel`) to store the currency code. This model will contain a single property, `currency: "EUR"`, which will be used for formatting the prices in the view. ```ts +// webapp/controller/InvoiceList.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -56,6 +47,7 @@ export default class App extends Controller { ``` ```js +// webapp/controller/InvoiceList.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel"], function (Controller, JSONModel) { "use strict"; diff --git a/packages/walkthrough/steps/21/README.md b/packages/walkthrough/steps/21/README.md index 88668940a..82c4c9bbb 100644 --- a/packages/walkthrough/steps/21/README.md +++ b/packages/walkthrough/steps/21/README.md @@ -8,7 +8,7 @@ Sometimes the predefined types of OpenUI5 are not flexible enough and you want t ### Preview -![](assets/loio636b7008113442c8a4765bb710dd8ea9_LowRes.png "The price is now formatted according to its number") +![The price is now formatted according to its number](assets/loio636b7008113442c8a4765bb710dd8ea9_LowRes.png "The price is now formatted according to its number") <sup>*The price is now formatted according to its number*</sup> @@ -18,17 +18,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 21](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 21](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 21](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 21](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-21-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/view/InvoiceList.view.xml diff --git a/packages/walkthrough/steps/22/README.md b/packages/walkthrough/steps/22/README.md index 1d44fe235..57924029c 100644 --- a/packages/walkthrough/steps/22/README.md +++ b/packages/walkthrough/steps/22/README.md @@ -8,7 +8,7 @@ If we want to do a more complex logic for formatting properties of our data mode ### Preview -![](assets/loio7aa185a90dd7495cb6ec30c96bc80a54_LowRes.png "A status is now displayed with a custom formatter") +![A status is now displayed with a custom formatter](assets/loio7aa185a90dd7495cb6ec30c96bc80a54_LowRes.png "A status is now displayed with a custom formatter") <sup>*A status is now displayed with a custom formatter*</sup> @@ -18,17 +18,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 22](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 22](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 22](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 22](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-22-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties @@ -47,13 +37,14 @@ invoiceStatusB=In Progress invoiceStatusC=Done ``` -### webapp/model/formatter.?s \(New\) +### webapp/model/formatter.ts/.js \(New\) We will create a formatter function to transform status codes into user-friendly text labels. -We create a file named `formatter.?s` within the `model` folder. This module contains the `statusText` function which takes a status code as input, retrieves the corresponding descriptive text from the resource bundle, and returns it. If no matching text is found in the resource bundle, or if the resource bundle can't be found, the function returns the original status code itself. +We create a file named `formatter.ts/.js` within the `model` folder. This module contains the `statusText` function which takes a status code as input, retrieves the corresponding descriptive text from the resource bundle, and returns it. If no matching text is found in the resource bundle, or if the resource bundle can't be found, the function returns the original status code itself. ```ts +// webapp/model/formatter.ts import ResourceBundle from "sap/base/i18n/ResourceBundle"; import Controller from "sap/ui/core/mvc/Controller"; import ResourceModel from "sap/ui/model/resource/ResourceModel"; @@ -77,6 +68,7 @@ export default { ``` ```js +// webapp/model/formatter.js sap.ui.define([], function () { "use strict"; diff --git a/packages/walkthrough/steps/23/README.md b/packages/walkthrough/steps/23/README.md index 96f186500..d161b4250 100644 --- a/packages/walkthrough/steps/23/README.md +++ b/packages/walkthrough/steps/23/README.md @@ -8,7 +8,7 @@ In this step, we add a search field for our product list and define a filter tha ### Preview -![](assets/loio472ab6bf88674c23ba103efd97163133_LowRes.png "A search field is displayed above the list") +![A search field is displayed above the list](assets/loio472ab6bf88674c23ba103efd97163133_LowRes.png "A search field is displayed above the list") <sup>*A search field is displayed above the list*</sup> @@ -18,20 +18,10 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 23](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 23](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 23](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 23](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-23-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/InvoiceList.controller.?s +### webapp/controller/InvoiceList.controller.ts/.js We will implement a new `onFilterInvoices` event handler function to our controller. This function will enable users to filter the invoice list based on a search term entered in a `sap/m/SearchField` control. @@ -42,6 +32,7 @@ If the query is empty, we filter the binding with an empty array. This will make Finally we apply the filter to the items binding of the invoice list in our view, updating the displayed items. ```ts +// webapp/controller/InvoiceList.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; import { SearchField$SearchEvent } from "sap/m/SearchField"; @@ -78,6 +69,7 @@ export default class App extends Controller { ``` ```js +// webapp/controller/InvoiceList.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel", "sap/ui/model/Filter", "sap/ui/model/FilterOperator"], function (Controller, JSONModel, Filter, FilterOperator) { "use strict"; diff --git a/packages/walkthrough/steps/24/README.md b/packages/walkthrough/steps/24/README.md index 10b896498..ad0b37cb7 100644 --- a/packages/walkthrough/steps/24/README.md +++ b/packages/walkthrough/steps/24/README.md @@ -9,7 +9,7 @@ To make our list of invoices even more user-friendly, we sort it alphabetically ### Preview -![](assets/loio33f71b44bb644d1fa2a0ab14f1fcc02a_LowRes.png "The list is now sorted and grouped by the shipping company") +![The list is now sorted and grouped by the shipping company](assets/loio33f71b44bb644d1fa2a0ab14f1fcc02a_LowRes.png "The list is now sorted and grouped by the shipping company") <sup>*The list is now sorted and grouped by the shipping company*</sup> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 24](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 24](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 24](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 24](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-24-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/view/InvoiceList.view.xml diff --git a/packages/walkthrough/steps/25/README.md b/packages/walkthrough/steps/25/README.md index 48cc1ab59..2b85edbe5 100644 --- a/packages/walkthrough/steps/25/README.md +++ b/packages/walkthrough/steps/25/README.md @@ -12,7 +12,7 @@ In the real world, data often resides on remote servers and is accessed via an O ### Preview -![](assets/loio5b76bb4b15eb44e1862d0b6c1c802571_LowRes.png "Products from the OData invoices test service are now shown within our app") +![Products from the OData invoices test service are now shown within our app](assets/loio5b76bb4b15eb44e1862d0b6c1c802571_LowRes.png "Products from the OData invoices test service are now shown within our app") <sup>*Products from the OData invoices test service are now shown within our app*</sup> @@ -22,17 +22,7 @@ In the real world, data often resides on remote servers and is accessed via an O ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 25](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 25](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 25](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 25](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-25-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### Development Environment diff --git a/packages/walkthrough/steps/26/README.md b/packages/walkthrough/steps/26/README.md index 3c3327a97..f13d4e2a6 100644 --- a/packages/walkthrough/steps/26/README.md +++ b/packages/walkthrough/steps/26/README.md @@ -11,7 +11,7 @@ This system is the so-called back-end system that we will now simulate with anOp ### Preview -![](assets/loiofe1403346ce9499f8bb102beaa4986d5_LowRes.png "The list of invoices is now served by the Mock Server") +![The list of invoices is now served by the Mock Server](assets/loiofe1403346ce9499f8bb102beaa4986d5_LowRes.png "The list of invoices is now served by the Mock Server") <sup>*The list of invoices is now served by the Mock Server*</sup> @@ -19,7 +19,7 @@ The folder structure of our app project is clearly separating test and productiv The new `localService` folder contains a `metadata.xml` service description file for OData, the `mockserver.js` file that simulates a real service with local data, and the `mockdata` subfolder that contains the local test data \(`Invoices.json`\). -![](assets/loio7a5e2b02d72d40d388f5e601d7de74df_LowRes.png "Folder Structure for this Step") +![Folder Structure for this Step](assets/loio7a5e2b02d72d40d388f5e601d7de74df_LowRes.png "Folder Structure for this Step") <sup>*Folder Structure for this Step*</sup> @@ -28,17 +28,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 26](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 26](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 26](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 26](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-26-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/localService/metadata.xml \(New\) @@ -137,9 +127,9 @@ In folder `localService` we create the new folder `mockdata`. The mock server de This file will automatically be found and read by the mock server. *** -### webapp/localService/mockserver.?s \(New\) +### webapp/localService/mockserver.ts/.js \(New\) -Now we can write the code to initialize the mock server which will then simulate any OData request to the real Northwind server. For this we add a new file `mockserver.?s` to the `localService` folder. +Now we can write the code to initialize the mock server which will then simulate any OData request to the real Northwind server. For this we add a new file `mockserver.ts/.js` to the `localService` folder. We import the standard OpenUI5 `MockServer` module and create a helper object that defines an `init` method to start the server. The `init` method creates a `MockServer` instance with the same URL as the real service calls. The URL in the `rootUri` configuration parameter has to point to the same URL as defined in the `uri` property of the data source in the `manifest.json` descriptor file. In the `manifest.json`, OpenUI5 automatically interprets a relative URL as being relative to the application namespace. In the TypeScript code, you can ensure this by using the `sap.ui.require.toUrl` method. The `sap/ui/core/util/MockServer` then catches every request to the real service and returns a response. @@ -150,6 +140,7 @@ To simulate a service, we can simply call the `simulate` method on the `MockServ Finally, we call the `start` method on the `MockServer`. From this point, each request to the URL pattern `rootUri` will be processed by the `MockServer`. ```ts +// webapp/localService/mockserver.ts import MockServer from "sap/ui/core/util/MockServer"; export default { @@ -179,6 +170,7 @@ export default { ``` ```js +// webapp/localService/mockserver.js sap.ui.define(["sap/ui/core/util/MockServer"], function (MockServer) { "use strict"; @@ -208,13 +200,14 @@ sap.ui.define(["sap/ui/core/util/MockServer"], function (MockServer) { ``` -### webapp/test/initMockServer.?s \(New\) +### webapp/test/initMockServer.ts/.js \(New\) -As a next step, we create a module that initializes our local `mockserver`. For this, we add the new `test` folder to our App folder where we place the new `initmockServer.?s` file. +As a next step, we create a module that initializes our local `mockserver`. For this, we add the new `test` folder to our App folder where we place the new `initmockServer.ts/.js` file. First, we call the `init` method of our local `mockserver`, then we initialize the app component. ```ts +// webapp/test/initMockServer.ts import mockserver from "../localService/mockserver"; // initialize the mock server @@ -226,6 +219,7 @@ import("sap/ui/core/ComponentSupport"); ``` ```js +// webapp/test/initMockServer.js sap.ui.define(["../localService/mockserver"], function (mockserver) { "use strict"; diff --git a/packages/walkthrough/steps/27/README.md b/packages/walkthrough/steps/27/README.md index ba7e2e99e..60abc3d79 100644 --- a/packages/walkthrough/steps/27/README.md +++ b/packages/walkthrough/steps/27/README.md @@ -13,13 +13,13 @@ Actually, every feature that we added to the app so far, would require a separat ### Preview -![](assets/loio0d29491d96574cfe8d8158d60a0a32e2_LowRes.png "A unit test for our formatters is now available") +![A unit test for our formatters is now available](assets/loio0d29491d96574cfe8d8158d60a0a32e2_LowRes.png "A unit test for our formatters is now available") <sup>*A unit test for our formatters is now available*</sup> We add a new folder `unit` under the `test` folder and a `model` subfolder where we will place our formatter unit test. The folder structure matches the app structure to easily find the corresponding unit tests. -![](assets/loio1b5613ac3ab94757af2c7823039222a9_LowRes.png "Folder Structure for this Step") +![Folder Structure for this Step](assets/loio1b5613ac3ab94757af2c7823039222a9_LowRes.png "Folder Structure for this Step") <sup>*Folder Structure for this Step*</sup> You can access the live preview by clicking on this link: [🔗 Live Preview of Step 27](https://ui5.github.io/tutorials/walkthrough/build/27/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=unit/unitTests). @@ -28,22 +28,12 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 27](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 27](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 27](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 27](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-27-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/test/unit/model/formatter.?s \(New\) +### webapp/test/unit/model/formatter.ts/.js \(New\) -We create a new `formatter.?s` file under `webapp/test/unit/model` where the unit test for the custom formatter is implemented. The formatter function that we want to test is from the `formatter.ts` file located in the `webapp/model` folder. +We create a new `formatter.ts/.js` file under `webapp/test/unit/model` where the unit test for the custom formatter is implemented. The formatter function that we want to test is from the `formatter.ts` file located in the `webapp/model` folder. The new formatter file just contains one QUnit module for our formatter function and one unit test for the formatter function. In the implementation of the `statusText` function that we created in Step 23, we use the translated texts when calling the formatter. As we do not want to test the OpenUI5 binding functionality, we just use text in the test instead of a `ResourceBundle`. @@ -53,6 +43,7 @@ Finally, we perform our assertions. We check each branch of the formatter logic > Test code needs to import the modules under test (i.e. productive code) using their full namespace (in our case `ui5/tutorial/walkthrough/`), rather than using relative paths. This is because the test code uses a different namespace (`test-resources/ui5/tutorial/walkthrough/`). ```ts +// webapp/test/unit/model/formatter.ts import ResourceModel from "sap/ui/model/resource/ResourceModel"; import Controller from "sap/ui/core/mvc/Controller"; import formatter from "ui5/tutorial/walkthrough/model/formatter"; @@ -91,6 +82,7 @@ QUnit.test("Should return the translated texts", (assert) => { ``` ```js +// webapp/test/unit/model/formatter.js sap.ui.define(["sap/ui/model/resource/ResourceModel", "ui5/tutorial/walkthrough/model/formatter"], function (ResourceModel, formatter) { "use strict"; @@ -124,19 +116,21 @@ sap.ui.define(["sap/ui/model/resource/ResourceModel", "ui5/tutorial/walkthrough/ ``` -### webapp/test/unit/unitTests.qunit.?s \(New\) +### webapp/test/unit/unitTests.qunit.ts/.js \(New\) -We create a new `unitTests.qunit.?s` file under `webapp/test/unit/`. +We create a new `unitTests.qunit.ts/.js` file under `webapp/test/unit/`. This module will serve as the entry point for all our unit tests. It will be referenced in the test suite that we will set up later on. -Inside the `unitTests.qunit.?s` file, we import the unit test for the custom formatter. This ensures that any tests related to the custom formatter functionality will be included when running our unit tests. +Inside the `unitTests.qunit.ts/.js` file, we import the unit test for the custom formatter. This ensures that any tests related to the custom formatter functionality will be included when running our unit tests. ```ts +// webapp/test/unit/unitTests.qunit.ts import "./model/formatter"; ``` ```js +// webapp/test/unit/unitTests.qunit.js sap.ui.define(["./model/formatter"], function (___model_formatter) { "use strict"; }); @@ -187,6 +181,7 @@ The previously created generic `Test.qunit.html` file is referenced as the test For more information, read [Test Starter - Concept and Basic Setup](https://sdk.openui5.org/#/topic/22f50c0f0b104bf3ba84620880793d3f). ```ts +// webapp/test/testsuite.qunit.ts import type {SuiteConfiguration} from "sap/ui/test/starter/config"; export default { name: "QUnit test suite for UI5 TypeScript Walkthrough", @@ -214,6 +209,7 @@ export default { ``` ```js +// webapp/test/testsuite.qunit.js sap.ui.define([], function () { "use strict"; diff --git a/packages/walkthrough/steps/28/README.md b/packages/walkthrough/steps/28/README.md index a9bd254a4..e6bb559a7 100644 --- a/packages/walkthrough/steps/28/README.md +++ b/packages/walkthrough/steps/28/README.md @@ -13,13 +13,13 @@ We haven’t thought about testing our interaction with the app yet, so in this ### Preview -![](assets/loio250d5b92921d44a4b432cc0fade88cc9_LowRes.png "An OPA test opens the "Hello" dialog from step 16") +![An OPA test opens the "Hello" dialog from step 16](assets/loio250d5b92921d44a4b432cc0fade88cc9_LowRes.png "An OPA test opens the "Hello" dialog from step 16") <sup>*An OPA test opens the "Hello" dialog from step 16*</sup> We add a new folder `integration` below the `test` folder, where we put our new test cases. Page objects that help structuring such integration tests are put in the `pages` subfolder that we also create now. -![](assets/loio27e84d5bd72a485498564b92894869b5_LowRes.png "Folder Structure for this Step") +![Folder Structure for this Step](assets/loio27e84d5bd72a485498564b92894869b5_LowRes.png "Folder Structure for this Step") <sup>*Folder Structure for this Step*</sup> You can access the live preview by clicking on this link: [🔗 Live Preview of Step 28](https://ui5.github.io/tutorials/walkthrough/build/28/test/Test.cdn.qunit.html?testsuite=test-resources/ui5/tutorial/walkthrough/testsuite.cdn.qunit&test=integration/opaTests). @@ -29,20 +29,10 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 28](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 28](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 28](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 28](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-28-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/test/integration/pages/HelloPanelPage.?s \(New\) +### webapp/test/integration/pages/HelloPanelPage.ts/.js \(New\) We create a new `HelloPanelPage.ts` file under `webapp/test/integration/pages`. @@ -53,6 +43,7 @@ In the actions section of the page object we define a function to click the "Hel In the assertions section we define a `waitFor` statement that checks if a `sap.m.Dialog` control is existing in the DOM of the app. When the dialog has been found, the test is successful and we can immediately confirm by calling an `ok` statement with a meaningful message. ```ts +// webapp/test/integration/pages/HelloPanelPage.ts import Opa5 from "sap/ui/test/Opa5"; import Press from "sap/ui/test/actions/Press"; @@ -85,6 +76,7 @@ export default class HelloPanelPage extends Opa5 { ``` ```js +// webapp/test/integration/pages/HelloPanelPage.js sap.ui.define(["sap/ui/test/Opa5", "sap/ui/test/actions/Press"], function (Opa5, Press) { "use strict"; @@ -118,7 +110,7 @@ sap.ui.define(["sap/ui/test/Opa5", "sap/ui/test/actions/Press"], function (Opa5, ``` -### webapp/test/integration/NavigationJourney.?s \(New\) +### webapp/test/integration/NavigationJourney.ts/.js \(New\) We create a new `NavigationJourney` file under `webapp/test/integration/`. @@ -138,6 +130,7 @@ The function `opaTest` is the main aspect for defining integration tests with OP In our journey, we create a very simple test that starts the `MainPage` and load our app. Then, we carry out the the actions we defined in our `MainPage` and expect that they will be executed successfully. Finally, we shut down the page again by calling function `iTeardownMyApp` on the `MainPage`. ```ts +// webapp/test/integration/NavigationJourney.ts import opaTest from "sap/ui/test/opaQunit"; import HelloPanelPage from "./pages/HelloPanelPage"; @@ -167,6 +160,7 @@ opaTest("Should open the Hello dialog", function () { ``` ```js +// webapp/test/integration/NavigationJourney.js sap.ui.define(["sap/ui/test/opaQunit", "./pages/HelloPanelPage"], function (opaTest, HelloPanelPage) { "use strict"; @@ -195,28 +189,31 @@ sap.ui.define(["sap/ui/test/opaQunit", "./pages/HelloPanelPage"], function (opaT   As you can see, the test case reads like a user story, we actually do not need the implementation of the methods yet to understand the meaning of the test case. This approach is called "Behavior Driven Development" or simply BDD and is popular in "Agile Software Development". -### webapp/test/integration/opaTests.qunit.?s \(New\) +### webapp/test/integration/opaTests.qunit.ts/.js \(New\) -We create a new `opaTests.qunit.?s` file under `webapp/test/integration/`. +We create a new `opaTests.qunit.ts/.js` file under `webapp/test/integration/`. This module imports our `NavigationJourney` and is the entrypoint for all integration tests in the project. ```ts +// webapp/test/integration/opaTests.qunit.ts import "./NavigationJourney"; ``` ```js +// webapp/test/integration/opaTests.qunit.js sap.ui.define(["./NavigationJourney"], function (___NavigationJourney) { "use strict"; }); ``` -### webapp/test/testsuite.qunit.?s +### webapp/test/testsuite.qunit.ts/.js -Finally we reference the new `integration/opaTests.qunit.?s` in the `testsuite.qunit.?s` file. The `.qunit.?s` extension is omitted and will be added automatically during runtime. +Finally we reference the new `integration/opaTests.qunit.ts/.js` in the `testsuite.qunit.ts/.js` file. The `.qunit.ts/.js` extension is omitted and will be added automatically during runtime. ```ts +// webapp/test/testsuite.qunit.ts import type {SuiteConfiguration} from "sap/ui/test/starter/config"; export default { // ... @@ -233,6 +230,7 @@ export default { ``` ```js +// webapp/test/testsuite.qunit.js sap.ui.define([], function () { "use strict"; //... diff --git a/packages/walkthrough/steps/29/README.md b/packages/walkthrough/steps/29/README.md index 8e847c68f..04c0e36fe 100644 --- a/packages/walkthrough/steps/29/README.md +++ b/packages/walkthrough/steps/29/README.md @@ -11,7 +11,7 @@ Luckily, OpenUI5 provides a couple of debugging tools that we can use within the ### Preview -![](assets/loio930de31b311f43ffa9df9261ca760da0_LowRes.png "The diagnostics window") +![The diagnostics window](assets/loio930de31b311f43ffa9df9261ca760da0_LowRes.png "The diagnostics window") <sup>*The diagnostics window*</sup> @@ -21,17 +21,7 @@ Luckily, OpenUI5 provides a couple of debugging tools that we can use within the ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 29](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-29.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 29](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-29-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 29](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-29.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 29](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-29-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** @@ -112,7 +102,7 @@ Sometimes errors are not as easy to spot and you actually need to debug the Java > This makes debugging harder, because the code is a lot less readable. You can load the debug sources by adding the URL parameter `sap-ui-debug=true` or by pressing [Ctrl\] + [Shift\] + [Alt\] /[Option\] + [P\] and selecting *Use Debug Sources* in the dialog box that is displayed. After reloading the page, you can see in the *Network* tab of the browser’s developer tools that now a lot of files are loaded that have a `–dbg` suffix. These are the source code files that include comments and the uncompressed code of the app and the OpenUI5 artifacts. -![](assets/loio34c4b02c74eb4848b8b720d86042bfdc_LowRes.png "Technical information dialog ") +![Technical information dialog ](assets/loio34c4b02c74eb4848b8b720d86042bfdc_LowRes.png "Technical information dialog ") <sup>*Technical information dialog*</sup> diff --git a/packages/walkthrough/steps/30/README.md b/packages/walkthrough/steps/30/README.md index 86777b093..6859f780b 100644 --- a/packages/walkthrough/steps/30/README.md +++ b/packages/walkthrough/steps/30/README.md @@ -11,7 +11,7 @@ In this step, we will use the OpenUI5 navigation features to load and show a sep ### Preview -![](assets/loio94152a595fe24d45b12223e0abcccb9c_LowRes.png "A second page is added to display the invoice") +![A second page is added to display the invoice](assets/loio94152a595fe24d45b12223e0abcccb9c_LowRes.png "A second page is added to display the invoice") <sup>*A second page is added to display the invoice*</sup> @@ -22,17 +22,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 30](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 30](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 30](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 30](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-30-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties @@ -181,11 +171,12 @@ The overview view is always shown when the hash is empty. The detail view is sho > :info: > The sequence of the routes in the routes definition is important. As soon as a pattern is matched, the following patterns are ignored. To prevent this for a specific route, you use the `greedy` parameter. If set to `true`, the route is always taken into account. -### webapp/Component.?s +### webapp/Component.ts/.js In the component initialization method, we now add a call to initialize the router. ```ts +// webapp/Component.ts import UIComponent from "sap/ui/core/UIComponent"; import JSONModel from "sap/ui/model/json/JSONModel"; @@ -218,6 +209,7 @@ export default class Component extends UIComponent { ``` ```js +// webapp/Component.js sap.ui.define(["sap/ui/core/UIComponent", "sap/ui/model/json/JSONModel"], function (UIComponent, JSONModel) { "use strict"; @@ -255,11 +247,12 @@ Initializing the router will evaluate the current URL and load the corresponding *** -### webapp/controller/InvoiceList.controller.?s +### webapp/controller/InvoiceList.controller.ts/.js What is still missing is the event handler that performs a navigation to the detail page by clicking an item in the invoice list: To access the router instance for our app use the static method `getRouterFor()` on the `UIComponent` module. On the router we call the `navTo` method passing the pattern name we defined in our app descriptor for routing to the details page. ```ts +// webapp/controller/InvoiceList.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; import { SearchField$SearchEvent } from "sap/m/SearchField"; @@ -283,6 +276,7 @@ export default class App extends Controller { ``` ```js +// webapp/controller/InvoiceList.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/ui/core/UIComponent"], function (Controller, JSONModel, Filter, FilterOperator, UIComponent) { "use strict"; diff --git a/packages/walkthrough/steps/31/README.md b/packages/walkthrough/steps/31/README.md index 5602523a5..071ca3093 100644 --- a/packages/walkthrough/steps/31/README.md +++ b/packages/walkthrough/steps/31/README.md @@ -11,7 +11,7 @@ To make this work, we have to pass over the information which item has been sele ### Preview -![](assets/loio31da9d48ae204c36a991146b90648c21_LowRes.png "The selected invoice details are now shown in the details page") +![The selected invoice details are now shown in the details page](assets/loio31da9d48ae204c36a991146b90648c21_LowRes.png "The selected invoice details are now shown in the details page") <sup>*The selected invoice details are now shown in the details page*</sup> @@ -21,17 +21,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 31](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 31](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 31](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 31](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-31-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** @@ -83,7 +73,7 @@ We want to hand over the information for the selected item when navigating to th } ``` -### webapp/controller/InvoiceList.controller.?s +### webapp/controller/InvoiceList.controller.ts/.js In the controller for the invoice list view, we extend the `onPress` event handler in such a way, it not only triggers the navigation to the detail view but also passes the selected item to the routing. @@ -95,6 +85,7 @@ Typically we woudl use the key of the item in the back-end system to identify th ```ts +// webapp/controller/InvoiceList.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import JSONModel from "sap/ui/model/json/JSONModel"; import { SearchField$SearchEvent } from "sap/m/SearchField"; @@ -125,6 +116,7 @@ export default class App extends Controller { ``` ```js +// webapp/controller/InvoiceList.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel", "sap/ui/model/Filter", "sap/ui/model/FilterOperator", "sap/ui/core/UIComponent"], function (Controller, JSONModel, Filter, FilterOperator, UIComponent) { "use strict"; @@ -146,7 +138,7 @@ sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel", "sap ``` -### webapp/controller/Detail.controller.?s \(New\) +### webapp/controller/Detail.controller.ts/.js \(New\) Now we need to create a new detail controller to set the content we passed in with the URL parameter `invoicePath` on the detail view. This will allow us to access the data of the selected item and display them on the view. @@ -160,6 +152,7 @@ The `bindElement` function is creating a binding context for a OpenUI5 control a ```ts +// webapp/controller/Detail.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import UIComponent from "sap/ui/core/UIComponent"; import Route, { Route$PatternMatchedEvent } from "sap/ui/core/routing/Route"; @@ -185,6 +178,7 @@ export default class Detail extends Controller { ``` ```js +// webapp/controller/Detail.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/core/UIComponent"], function (Controller, UIComponent) { "use strict"; diff --git a/packages/walkthrough/steps/32/README.md b/packages/walkthrough/steps/32/README.md index 8574b0346..5f9624eb9 100644 --- a/packages/walkthrough/steps/32/README.md +++ b/packages/walkthrough/steps/32/README.md @@ -9,7 +9,7 @@ Now we can navigate to our detail page and display an invoice, but we cannot go ### Preview -![](assets/loio33a8341077bb458685274c64d2317f6b_LowRes.png "A back button is now displayed on the detail page") +![A back button is now displayed on the detail page](assets/loio33a8341077bb458685274c64d2317f6b_LowRes.png "A back button is now displayed on the detail page") <sup>*A back button is now displayed on the detail page*</sup> @@ -19,21 +19,11 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 32](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 32](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 32](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 32](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-32-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/controller/Detail.controller.?s +### webapp/controller/Detail.controller.ts/.js To be able to navigate from the detail view back to the view we came from we implement a new event handler function in the controller of the detail view. For a start we load a new class called `History` from the `sap.ui.core.routing` namespace. This class provides methods for navigating through the history of the application. @@ -43,6 +33,7 @@ In the event handler we access the navigation history and try to determine the p If no navigation has happened before, we get a reference to the router and use the `navTo` method to navigate to the "overview" route. As a second parameter we specify an empty array \(`{}`\) as we do not pass any additional parameters to the route. The third parameter is set to `true`. This tells the router to replace the current history state with the new one since we actually do a back navigation by ourselves and we do not want to have an entry in the browser history. ```ts +// webapp/controller/Detail.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import { Route$PatternMatchedEvent } from "sap/ui/core/routing/Route"; import History from "sap/ui/core/routing/History"; @@ -71,6 +62,7 @@ export default class Detail extends Controller { ``` ```js +// webapp/controller/Detail.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/core/routing/History", "sap/ui/core/UIComponent"], function (Controller, History, UIComponent) { "use strict"; diff --git a/packages/walkthrough/steps/33/README.md b/packages/walkthrough/steps/33/README.md index 3b9a47f58..dede2ccd8 100644 --- a/packages/walkthrough/steps/33/README.md +++ b/packages/walkthrough/steps/33/README.md @@ -8,7 +8,7 @@ In this step, we are going to extend the functionality of OpenUI5 with a custom ### Preview -![](assets/loio21dd14c37b67473b817c8865f168f668_LowRes.png "A custom product rating control is added to the detail page") +![A custom product rating control is added to the detail page](assets/loio21dd14c37b67473b817c8865f168f668_LowRes.png "A custom product rating control is added to the detail page") <sup>*A custom product rating control is added to the detail page*</sup> @@ -17,17 +17,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 33](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 33](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 33](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 33](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-33-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties @@ -73,7 +63,7 @@ html[dir="rtl"] .myAppDemoWT .myCustomButton.sapMBtn { We could also do this with more HTML in the renderer but this is the simplest way and it will only be applied inside our custom control. However, please be aware that the custom control is in your app and might have to be adjusted when the inner controls change in future versions of OpenUI5. -### webapp/control/ProductRating.?s \(New\) +### webapp/control/ProductRating.ts/.js \(New\) Custom controls are small reuse components that can be created within an application very easily. Due to their nature, they are sometimes also referred to as "notepad” or “on the fly” controls. A custom control is an object that has two special sections \(`metadata` and `renderer`\) and various methods that determine the control's functionality. @@ -95,6 +85,7 @@ The static `renderer` property expects an object that defines how the control is > When developing a custom control, it is crucial to specify the appropriate apiVersion for the control's renderer. This ensures that your control can leverage the latest rendering features and improvements available in the RenderManager. ```ts +// webapp/control/ProductRating.ts import Control from "sap/ui/core/Control"; import RenderManager from "sap/ui/core/RenderManager"; @@ -120,6 +111,7 @@ export default class ProductRating extends Control { ``` ```js +// webapp/control/ProductRating.js sap.ui.define(["sap/ui/core/Control"], function (Control) { "use strict"; @@ -181,6 +173,7 @@ Next, we have the `press` handler for the rating button that submits our rating. We define the `reset` method to be able to revert the state of the control on the UI to its initial state so that the user can again submit a rating. ```ts +// webapp/control/ProductRating.ts import Control from "sap/ui/core/Control"; import RenderManager from "sap/ui/core/RenderManager"; import { MetadataOptions } from "sap/ui/core/Element"; @@ -305,6 +298,7 @@ export default class ProductRating extends Control { ``` ```js +// webapp/control/ProductRating.js sap.ui.define(["sap/ui/core/Control", "sap/m/Label", "sap/m/Button", "sap/m/RatingIndicator"], function (Control, Label, Button, RatingIndicator) { "use strict"; @@ -429,7 +423,7 @@ You can now stop the interface generator again, as no further control API change </details> -### webapp/controller/Detail.controller.?s +### webapp/controller/Detail.controller.ts/.js In the `Detail` controller we implement a new `onRatingChange` event that reads the value of our coustom change event that is fired when a rating has been submitted. This requires to import our new control, as well as the `ProductRating$ChangeEvent` type we just defined to the detail controller. To keep the sample simple we only display a message message instead of sending the rating to the backend. We therefore load the `MessageToast` module from the `sap.m` namespace to our script. In addition we need the `ResourceBundle` module from the `sap/base/i18n` namespace as well as the `ResourceModel` module from the `sap/ui/model/resource` namespace as we want to display the confirmation message we specified in our resource bundle in the message toast. @@ -438,6 +432,7 @@ In the `onRatingChange` the event handler we extract the value of our custom cha In the `onObjectMatched` method, we call the `reset` method to make it possible to submit another rating as soon as the detail view is displayed for a different item. ```ts +// webapp/controller/Detail.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import Route, { Route$PatternMatchedEvent } from "sap/ui/core/routing/Route"; import History from "sap/ui/core/routing/History"; @@ -489,6 +484,7 @@ export default class Detail extends Controller { ``` ```js +// webapp/controller/Detail.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/core/routing/History", "sap/m/MessageToast", "sap/ui/core/UIComponent"], function (Controller, History, MessageToast, UIComponent) { "use strict"; @@ -574,6 +570,7 @@ constructor(id?: string, settings?: $ProductRatingSettings) { super(id, settings To complete the setup of the generated interface, we follow the instructions and add the block between the BEGIN and END line into the `ProductRating` class body in the file `webapp/control/ProductRating.ts`. ```ts +// webapp/control/ProductRating.ts import Control from "sap/ui/core/Control"; import RenderManager from "sap/ui/core/RenderManager"; import { MetadataOptions } from "sap/ui/core/Element"; diff --git a/packages/walkthrough/steps/34/README.md b/packages/walkthrough/steps/34/README.md index b836aaf2b..44c0d2525 100644 --- a/packages/walkthrough/steps/34/README.md +++ b/packages/walkthrough/steps/34/README.md @@ -9,7 +9,7 @@ In this step, we improve the responsiveness of our app. OpenUI5 applications can ### Preview -![](assets/loiocc3f2e0d8ac6471288af6495836c2f07_LowRes.png "A responsive table is hiding some of the columns on small devices") +![A responsive table is hiding some of the columns on small devices](assets/loiocc3f2e0d8ac6471288af6495836c2f07_LowRes.png "A responsive table is hiding some of the columns on small devices") <sup>*A responsive table is hiding some of the columns on small devices*</sup> @@ -19,17 +19,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 34](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 34](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 34](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 34](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-34-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties diff --git a/packages/walkthrough/steps/35/README.md b/packages/walkthrough/steps/35/README.md index c9554a845..f8b0539fc 100644 --- a/packages/walkthrough/steps/35/README.md +++ b/packages/walkthrough/steps/35/README.md @@ -8,7 +8,7 @@ We now configure the visibility and properties of controls based on the device t ### Preview -![](assets/loio0b0d57e04e574d7fbc4b10395e6cb260_LowRes.png "On phone devices, the panel is collapsed to save screen space and a button is hidden") +![On phone devices, the panel is collapsed to save screen space and a button is hidden](assets/loio0b0d57e04e574d7fbc4b10395e6cb260_LowRes.png "On phone devices, the panel is collapsed to save screen space and a button is hidden") <sup>*On phone devices, the panel is collapsed to save screen space and a button is hidden*</sup> @@ -17,20 +17,10 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 35](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 35](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 35](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 35](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-35-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/Component.?s +### webapp/Component.ts/.js In the `app` component we import the `Device` module from the `sap.ui` namespace and initialize the device model in the `init` method. We can simply pass the loaded dependency `Device` to the constructor function of the JSONModel. This will make most properties of the OpenUI5 device API available as a JSON model. The model is then set on the component as a named model so that we can reference it in data binding. @@ -38,6 +28,7 @@ In the `app` component we import the `Device` module from the `sap.ui` namespace > We have to set the binding mode to `OneWay` as the device model is read-only and we want to avoid changing the model accidentally when we bind properties of a control to it. By default, models in OpenUI5 are bidirectional \(`TwoWay`\). When the property changes, the bound model value is updated as well. ```ts +// webapp/Component.ts import UIComponent from "sap/ui/core/UIComponent"; import JSONModel from "sap/ui/model/json/JSONModel"; import Device from "sap/ui/Device"; @@ -76,6 +67,7 @@ export default class Component extends UIComponent { ``` ```js +// webapp/Component.js sap.ui.define(["sap/ui/core/UIComponent", "sap/ui/model/json/JSONModel", "sap/ui/Device"], function (UIComponent, JSONModel, Device) { "use strict"; @@ -157,11 +149,12 @@ The device API of OpenUI5 offers more functionality to detect various device-spe > :info: > The `sap.ui.Device` API detects the device type \(Phone, Tablet, Desktop\) based on the user agent and many other properties of the device. Therefore simply reducing the screen size will not change the device type. To test this feature, you will have to enable device emulation in your browser or open it on a real device. -### webapp/controller/Detail.controller.?s +### webapp/controller/Detail.controller.ts/.js In the `Detail` controller we simply add the view model with our currency definition to display the number properly. It is the same code as in the `InvoiceList` controller file. ```ts +// webapp/controller/Detail.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import Route, { Route$PatternMatchedEvent } from "sap/ui/core/routing/Route"; import History from "sap/ui/core/routing/History"; @@ -192,6 +185,7 @@ export default class Detail extends Controller { ``` ```js +// webapp/controller/Detail.controller.js sap.ui.define(["sap/ui/core/mvc/Controller", "sap/ui/core/routing/History", "sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/ui/core/UIComponent"], function (Controller, History, MessageToast, JSONModel, UIComponent) { "use strict"; diff --git a/packages/walkthrough/steps/36/README.md b/packages/walkthrough/steps/36/README.md index 3d699afda..c4075a976 100644 --- a/packages/walkthrough/steps/36/README.md +++ b/packages/walkthrough/steps/36/README.md @@ -8,7 +8,7 @@ In this step of our Walkthrough tutorial, we adjust the content density based on ### Preview -![](assets/loiof216b131c492448d8a1df25db2b9a26d_LowRes.png "The content density is compact on desktop devices and cozy on touch-enabled devices") +![The content density is compact on desktop devices and cozy on touch-enabled devices](assets/loiof216b131c492448d8a1df25db2b9a26d_LowRes.png "The content density is compact on desktop devices and cozy on touch-enabled devices") <sup>*The content density is compact on devices without a touch screen and cozy on touch-enabled devices*</sup> @@ -17,26 +17,17 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 36](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 36](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 36](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 36](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-36-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** -### webapp/Component.?s +### webapp/Component.ts/.js To prepare the content density feature we add a helper method `getContentDensityClass` to the app component. OpenUI5 controls can be displayed in multiple sizes, for example in a `compact` size that is optimized for desktop and non-touch devices, and in a `cozy` mode that is optimized for touch interaction. The controls look for a specific CSS class in the HTML structure of the application to adjust their size. This helper method queries the `Device` API directly for touch support of the client and returns the CSS class `sapUiSizeCompact` if touch interaction is not supported and `sapUiSizeCozy` for all other cases. We will use it throughout the application coding to set the proper content density CSS class. ```ts +// webapp/Component.ts import UIComponent from "sap/ui/core/UIComponent"; import JSONModel from "sap/ui/model/json/JSONModel"; import Device from "sap/ui/Device"; @@ -62,6 +53,7 @@ export default class Component extends UIComponent { ``` ```js +// webapp/Component.js sap.ui.define(["sap/ui/core/UIComponent", "sap/ui/model/json/JSONModel", "sap/ui/Device"], function (UIComponent, JSONModel, Device) { "use strict"; @@ -84,11 +76,12 @@ sap.ui.define(["sap/ui/core/UIComponent", "sap/ui/model/json/JSONModel", "sap/ui ``` -### webapp/controller/App.controller.?s +### webapp/controller/App.controller.ts/.js We add the `onInit` method to the app controller that is called when the app view is instantiated. There, we query the helper function that we defined on the app component in order to set the corresponding style class on the app view. All controls inside the app view will now automatically adjust to either the compact or the cozy size, as defined by the style. ```ts +// webapp/controller/App.controller.ts import Controller from "sap/ui/core/mvc/Controller"; import Component from "../Component"; @@ -104,6 +97,7 @@ export default class App extends Controller { ``` ```js +// webapp/controller/App.controller.js sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) { "use strict"; diff --git a/packages/walkthrough/steps/37/README.md b/packages/walkthrough/steps/37/README.md index 74c3df495..2ece95fd6 100644 --- a/packages/walkthrough/steps/37/README.md +++ b/packages/walkthrough/steps/37/README.md @@ -15,7 +15,7 @@ One part of the ARIA attribute set are the so-called landmarks. You can compare ### Preview -![](assets/loiob35deda1ebe1433fbf0ff066f6e3fc4b_LowRes.png "Landmarks in our app") +![Landmarks in our app](assets/loiob35deda1ebe1433fbf0ff066f6e3fc4b_LowRes.png "Landmarks in our app") <sup>*Landmarks in our app*</sup> @@ -25,17 +25,7 @@ You can access the live preview by clicking on this link: [🔗 Live Preview of ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 37](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 37](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 37](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 37](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-37-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### webapp/i18n/i18n.properties diff --git a/packages/walkthrough/steps/38/README.md b/packages/walkthrough/steps/38/README.md index 2c6437d38..5541ff1e2 100644 --- a/packages/walkthrough/steps/38/README.md +++ b/packages/walkthrough/steps/38/README.md @@ -9,24 +9,14 @@ In this step we're going to build our application and consume the speed of a bui ### Preview -![](assets/loiofb12cea5ac9b45bb9007aac5a1a8689f_LowRes.png "The UI5 application is built and served") +![The UI5 application is built and served](assets/loiofb12cea5ac9b45bb9007aac5a1a8689f_LowRes.png "The UI5 application is built and served") <sup>*The OpenUI5 application is built and served*</sup> *** ### Coding -<details class="ts-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 38](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38.zip). - -</details> - -<details class="js-only" markdown="1"> - -You can download the solution for this step here: [📥 Download step 38](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38-js.zip). - -</details> +You can download the solution for this step here: <span class="ts-only">[📥 Download step 38](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 38](https://ui5.github.io/tutorials/walkthrough/walkthrough-step-38-js.zip)<span class="lang-suffix"> (JS)</span></span>. *** ### package.json diff --git a/tools/builder/prepare-gh-pages.js b/tools/builder/prepare-gh-pages.js index 7ec90cf11..e0b467ba3 100644 --- a/tools/builder/prepare-gh-pages.js +++ b/tools/builder/prepare-gh-pages.js @@ -135,6 +135,10 @@ function removeTSfromUI5YAML(ui5yaml) { rewriteLinks(join(distTutorialDir, "index.md")); } + if (existsSync(join(tutorialDir, "steps.json"))) { + copyFileSync(join(tutorialDir, "steps.json"), join(distTutorialDir, "steps.json")); + } + console.log(` 👉 Zipping TypeScript sources...`); await Promise.all(steps.map((step) => { return zipDirectory( From 6c6b839bbfb399a97e428172df674376ef2ea1c0 Mon Sep 17 00:00:00 2001 From: Peter Muessig <peter.muessig@sap.com> Date: Tue, 21 Jul 2026 13:27:07 +0200 Subject: [PATCH 2/4] fix: prevent manifest.json corruption on TS/JS switch The odatav4 step 03 and 05 headings read `webapp/manifest.ts/.js`on, a migration leftover where the `.js` inside `.json` was rewritten as the `.ts/.js` toggle token. Switching to TypeScript then rendered `manifest.tson`. The manifest filename is language-independent, so the headings are restored to `webapp/manifest.json`. Also harden replaceFileExtensions so the toggle only fires at a real extension boundary (`/\.ts\/\.js(?![A-Za-z0-9])/`), preventing any mis-tokenized longer extension from silently corrupting a filename. --- assets/js/custom.js | 11 +++++++++-- packages/odatav4/steps/03/README.md | 2 +- packages/odatav4/steps/05/README.md | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/assets/js/custom.js b/assets/js/custom.js index 19eec020c..b9ac21b4e 100644 --- a/assets/js/custom.js +++ b/assets/js/custom.js @@ -179,6 +179,13 @@ function replaceFileExtensions(lang) { const replacement = "<span class='ts-only'>.ts</span><span class='js-only'>.js</span>"; + // The `.ts/.js` toggle token only represents a *complete* file extension. + // Requiring a non-word boundary after `.js` prevents a mis-authored token + // that is really part of a longer extension (e.g. `manifest.ts/.js` + `on`, + // a leftover from tokenizing `.json`) from being swapped into a corrupt + // filename like `manifest.tson` when TypeScript is active. + const tokenPattern = /\.ts\/\.js(?![A-Za-z0-9])/g; + // select all text nodes in the body const walker = document.createTreeWalker( document.body, @@ -191,9 +198,9 @@ function replaceFileExtensions(lang) { // iterate over each text node while (node) { let nextNode = walker.nextNode(); - if (node.nodeValue.includes(".ts/.js")) { + if (tokenPattern.test(node.nodeValue)) { const temp = document.createElement("div"); - temp.innerHTML = node.nodeValue.replace(/\.ts\/\.js/g, replacement); + temp.innerHTML = node.nodeValue.replace(tokenPattern, replacement); const fragment = document.createDocumentFragment(); while (temp.firstChild) { diff --git a/packages/odatav4/steps/03/README.md b/packages/odatav4/steps/03/README.md index fe3f851ea..6161377b5 100644 --- a/packages/odatav4/steps/03/README.md +++ b/packages/odatav4/steps/03/README.md @@ -16,7 +16,7 @@ The OData V4 Model utilizes this information to compute the corresponding SAPUI5 You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html). -## `webapp/manifest.ts/.js`on +## `webapp/manifest.json` ```json { diff --git a/packages/odatav4/steps/05/README.md b/packages/odatav4/steps/05/README.md index 73202630a..a710d3c3a 100644 --- a/packages/odatav4/steps/05/README.md +++ b/packages/odatav4/steps/05/README.md @@ -14,7 +14,7 @@ In this step, we have a closer look at batch groups. Batch groups are used to gr You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html). -## `webapp/manifest.ts/.js`on +## `webapp/manifest.json` ```json ... From 314378a02ad243f7864ac6249996727b64969372 Mon Sep 17 00:00:00 2001 From: Peter Muessig <peter.muessig@sap.com> Date: Tue, 21 Jul 2026 17:30:30 +0200 Subject: [PATCH 3/4] docs(tutorials): add Data Binding tutorial and align Preview/Coding link placement Add the Data Binding tutorial (packages/databinding/) as 15 runnable TypeScript steps aligned to repo conventions: ui5.tutorial.databinding namespace, OpenUI5 1.148.1, manifest v2, index-cdn.html, .ts/.js token with first-line path comments, JS twins for every TS fence, per-step Preview/Coding structure, and the <span> download-solution link form. Also normalize link placement across navigation and odatav4 so that the Live Preview link always sits in the Preview section and the download link in the Coding section (matching walkthrough/quickstart). Regenerate steps.json, extend the root README tutorial list, and update AGENTS.md to describe five tutorials. --- AGENTS.md | 7 +- README.md | 1 + package-lock.json | 240 ++++++++++++++ packages/databinding/README.md | 54 ++++ .../assets/Tutorial_Data_Binding_896048e.png | Bin 0 -> 111804 bytes packages/databinding/steps.json | 141 ++++++++ packages/databinding/steps/01/README.md | 226 +++++++++++++ .../Tutorial_Data_Binding_Step_1_6d391d5.png | Bin 0 -> 6851 bytes packages/databinding/steps/01/package.json | 19 ++ packages/databinding/steps/01/tsconfig.json | 25 ++ packages/databinding/steps/01/ui5.yaml | 24 ++ .../databinding/steps/01/webapp/Component.ts | 11 + .../steps/01/webapp/index-cdn.html | 22 ++ .../databinding/steps/01/webapp/index.html | 22 ++ .../databinding/steps/01/webapp/manifest.json | 38 +++ .../steps/01/webapp/view/App.view.xml | 5 + packages/databinding/steps/02/README.md | 99 ++++++ .../Tutorial_Data_Binding_Step_1_6d391d5.png | Bin 0 -> 6851 bytes packages/databinding/steps/02/package.json | 19 ++ packages/databinding/steps/02/tsconfig.json | 25 ++ packages/databinding/steps/02/ui5.yaml | 24 ++ .../databinding/steps/02/webapp/Component.ts | 11 + .../steps/02/webapp/index-cdn.html | 22 ++ .../databinding/steps/02/webapp/index.html | 22 ++ .../databinding/steps/02/webapp/manifest.json | 44 +++ .../steps/02/webapp/model/data.json | 3 + .../steps/02/webapp/view/App.view.xml | 5 + packages/databinding/steps/03/README.md | 53 +++ .../Tutorial_Data_Binding_Step_1_6d391d5.png | Bin 0 -> 6851 bytes packages/databinding/steps/03/package.json | 19 ++ packages/databinding/steps/03/tsconfig.json | 25 ++ packages/databinding/steps/03/ui5.yaml | 24 ++ .../databinding/steps/03/webapp/Component.ts | 11 + .../steps/03/webapp/index-cdn.html | 22 ++ .../databinding/steps/03/webapp/index.html | 22 ++ .../databinding/steps/03/webapp/manifest.json | 44 +++ .../steps/03/webapp/model/data.json | 3 + .../steps/03/webapp/view/App.view.xml | 5 + packages/databinding/steps/04/README.md | 111 +++++++ ...Tutorial_Data_Binding_Step_4_2_6222561.png | Bin 0 -> 16811 bytes .../Tutorial_Data_Binding_Step_4_61d68f1.png | Bin 0 -> 17241 bytes packages/databinding/steps/04/package.json | 19 ++ packages/databinding/steps/04/tsconfig.json | 25 ++ packages/databinding/steps/04/ui5.yaml | 24 ++ .../databinding/steps/04/webapp/Component.ts | 11 + .../steps/04/webapp/index-cdn.html | 22 ++ .../databinding/steps/04/webapp/index.html | 22 ++ .../databinding/steps/04/webapp/manifest.json | 45 +++ .../steps/04/webapp/model/data.json | 6 + .../steps/04/webapp/view/App.view.xml | 34 ++ packages/databinding/steps/05/README.md | 95 ++++++ .../Tutorial_Data_Binding_Step_4_61d68f1.png | Bin 0 -> 17241 bytes packages/databinding/steps/05/package.json | 19 ++ packages/databinding/steps/05/tsconfig.json | 25 ++ packages/databinding/steps/05/ui5.yaml | 24 ++ .../databinding/steps/05/webapp/Component.ts | 17 + .../steps/05/webapp/index-cdn.html | 22 ++ .../databinding/steps/05/webapp/index.html | 22 ++ .../databinding/steps/05/webapp/manifest.json | 45 +++ .../steps/05/webapp/model/data.json | 6 + .../steps/05/webapp/view/App.view.xml | 34 ++ packages/databinding/steps/06/README.md | 210 ++++++++++++ .../Tutorial_Data_Binding_Step_4_61d68f1.png | Bin 0 -> 17241 bytes packages/databinding/steps/06/package.json | 19 ++ packages/databinding/steps/06/tsconfig.json | 25 ++ packages/databinding/steps/06/ui5.yaml | 24 ++ .../databinding/steps/06/webapp/Component.ts | 11 + .../steps/06/webapp/i18n/i18n.properties | 11 + .../steps/06/webapp/index-cdn.html | 22 ++ .../databinding/steps/06/webapp/index.html | 22 ++ .../databinding/steps/06/webapp/manifest.json | 65 ++++ .../steps/06/webapp/model/data.json | 5 + .../steps/06/webapp/view/App.view.xml | 34 ++ packages/databinding/steps/07/README.md | 65 ++++ .../Tutorial_Data_Binding_Step_7_d96cdf9.png | Bin 0 -> 18359 bytes packages/databinding/steps/07/package.json | 19 ++ packages/databinding/steps/07/tsconfig.json | 25 ++ packages/databinding/steps/07/ui5.yaml | 24 ++ .../databinding/steps/07/webapp/Component.ts | 11 + .../steps/07/webapp/i18n/i18n.properties | 11 + .../steps/07/webapp/i18n/i18n_de.properties | 11 + .../steps/07/webapp/index-cdn.html | 22 ++ .../databinding/steps/07/webapp/index.html | 22 ++ .../databinding/steps/07/webapp/manifest.json | 65 ++++ .../steps/07/webapp/model/data.json | 5 + .../steps/07/webapp/view/App.view.xml | 34 ++ packages/databinding/steps/08/README.md | 135 ++++++++ .../Tutorial_Data_Binding_Step_8_12705f5.png | Bin 0 -> 33890 bytes packages/databinding/steps/08/package.json | 19 ++ packages/databinding/steps/08/tsconfig.json | 25 ++ packages/databinding/steps/08/ui5.yaml | 24 ++ .../databinding/steps/08/webapp/Component.ts | 11 + .../steps/08/webapp/i18n/i18n.properties | 13 + .../steps/08/webapp/i18n/i18n_de.properties | 13 + .../steps/08/webapp/index-cdn.html | 22 ++ .../databinding/steps/08/webapp/index.html | 22 ++ .../databinding/steps/08/webapp/manifest.json | 65 ++++ .../steps/08/webapp/model/data.json | 11 + .../steps/08/webapp/view/App.view.xml | 53 +++ packages/databinding/steps/09/README.md | 172 ++++++++++ .../Tutorial_Data_Binding_Step_9_1161575.png | Bin 0 -> 36217 bytes packages/databinding/steps/09/package.json | 19 ++ packages/databinding/steps/09/tsconfig.json | 25 ++ packages/databinding/steps/09/ui5.yaml | 24 ++ .../databinding/steps/09/webapp/Component.ts | 11 + .../09/webapp/controller/App.controller.ts | 19 ++ .../steps/09/webapp/i18n/i18n.properties | 18 ++ .../steps/09/webapp/i18n/i18n_de.properties | 18 ++ .../steps/09/webapp/index-cdn.html | 22 ++ .../databinding/steps/09/webapp/index.html | 22 ++ .../databinding/steps/09/webapp/manifest.json | 65 ++++ .../steps/09/webapp/model/data.json | 11 + .../steps/09/webapp/view/App.view.xml | 63 ++++ packages/databinding/steps/10/README.md | 144 +++++++++ .../Tutorial_Data_Binding_Step_10_d15f8bc.png | Bin 0 -> 44353 bytes packages/databinding/steps/10/package.json | 19 ++ packages/databinding/steps/10/tsconfig.json | 25 ++ packages/databinding/steps/10/ui5.yaml | 24 ++ .../databinding/steps/10/webapp/Component.ts | 11 + .../10/webapp/controller/App.controller.ts | 19 ++ .../steps/10/webapp/i18n/i18n.properties | 19 ++ .../steps/10/webapp/i18n/i18n_de.properties | 19 ++ .../steps/10/webapp/index-cdn.html | 22 ++ .../databinding/steps/10/webapp/index.html | 22 ++ .../databinding/steps/10/webapp/manifest.json | 65 ++++ .../steps/10/webapp/model/data.json | 13 + .../steps/10/webapp/view/App.view.xml | 84 +++++ packages/databinding/steps/11/README.md | 71 ++++ ...utorial_Data_Binding_Step_11_2_44db88e.png | Bin 0 -> 5666 bytes ...utorial_Data_Binding_Step_11_3_e67207b.png | Bin 0 -> 48558 bytes packages/databinding/steps/11/package.json | 19 ++ packages/databinding/steps/11/tsconfig.json | 25 ++ packages/databinding/steps/11/ui5.yaml | 24 ++ .../databinding/steps/11/webapp/Component.ts | 11 + .../11/webapp/controller/App.controller.ts | 19 ++ .../steps/11/webapp/i18n/i18n.properties | 19 ++ .../steps/11/webapp/i18n/i18n_de.properties | 19 ++ .../steps/11/webapp/index-cdn.html | 22 ++ .../databinding/steps/11/webapp/index.html | 22 ++ .../databinding/steps/11/webapp/manifest.json | 66 ++++ .../steps/11/webapp/model/data.json | 13 + .../steps/11/webapp/view/App.view.xml | 84 +++++ packages/databinding/steps/12/README.md | 305 ++++++++++++++++++ .../Tutorial_Data_Binding_Step_12_1642433.png | Bin 0 -> 96655 bytes packages/databinding/steps/12/package.json | 19 ++ packages/databinding/steps/12/tsconfig.json | 25 ++ packages/databinding/steps/12/ui5.yaml | 24 ++ .../databinding/steps/12/webapp/Component.ts | 11 + .../12/webapp/controller/App.controller.ts | 25 ++ .../steps/12/webapp/i18n/i18n.properties | 24 ++ .../steps/12/webapp/i18n/i18n_de.properties | 24 ++ .../steps/12/webapp/index-cdn.html | 22 ++ .../databinding/steps/12/webapp/index.html | 22 ++ .../databinding/steps/12/webapp/manifest.json | 70 ++++ .../steps/12/webapp/model/Products.json | 64 ++++ .../steps/12/webapp/model/data.json | 13 + .../steps/12/webapp/view/App.view.xml | 121 +++++++ packages/databinding/steps/13/README.md | 212 ++++++++++++ .../Tutorial_Data_Binding_Step_13_872d2ed.png | Bin 0 -> 84291 bytes packages/databinding/steps/13/package.json | 19 ++ packages/databinding/steps/13/tsconfig.json | 25 ++ packages/databinding/steps/13/ui5.yaml | 24 ++ .../databinding/steps/13/webapp/Component.ts | 11 + .../13/webapp/controller/App.controller.ts | 32 ++ .../steps/13/webapp/i18n/i18n.properties | 33 ++ .../steps/13/webapp/i18n/i18n_de.properties | 33 ++ .../steps/13/webapp/index-cdn.html | 22 ++ .../databinding/steps/13/webapp/index.html | 22 ++ .../databinding/steps/13/webapp/manifest.json | 70 ++++ .../steps/13/webapp/model/Products.json | 64 ++++ .../steps/13/webapp/model/data.json | 13 + .../steps/13/webapp/view/App.view.xml | 150 +++++++++ packages/databinding/steps/14/README.md | 149 +++++++++ .../Tutorial_Data_Binding_Step_14_b9fb758.png | Bin 0 -> 97490 bytes packages/databinding/steps/14/package.json | 19 ++ packages/databinding/steps/14/tsconfig.json | 25 ++ packages/databinding/steps/14/ui5.yaml | 24 ++ .../databinding/steps/14/webapp/Component.ts | 11 + .../14/webapp/controller/App.controller.ts | 32 ++ .../steps/14/webapp/i18n/i18n.properties | 33 ++ .../steps/14/webapp/i18n/i18n_de.properties | 33 ++ .../steps/14/webapp/index-cdn.html | 22 ++ .../databinding/steps/14/webapp/index.html | 22 ++ .../databinding/steps/14/webapp/manifest.json | 70 ++++ .../steps/14/webapp/model/Products.json | 64 ++++ .../steps/14/webapp/model/data.json | 14 + .../steps/14/webapp/view/App.view.xml | 151 +++++++++ packages/databinding/steps/15/README.md | 249 ++++++++++++++ .../Tutorial_Data_Binding_Step_15_db27ba8.png | Bin 0 -> 73445 bytes packages/databinding/steps/15/package.json | 19 ++ packages/databinding/steps/15/tsconfig.json | 25 ++ packages/databinding/steps/15/ui5.yaml | 24 ++ .../databinding/steps/15/webapp/Component.ts | 11 + .../15/webapp/controller/App.controller.ts | 59 ++++ .../steps/15/webapp/i18n/i18n.properties | 34 ++ .../steps/15/webapp/i18n/i18n_de.properties | 34 ++ .../steps/15/webapp/index-cdn.html | 22 ++ .../databinding/steps/15/webapp/index.html | 22 ++ .../databinding/steps/15/webapp/manifest.json | 70 ++++ .../steps/15/webapp/model/Products.json | 64 ++++ .../steps/15/webapp/model/data.json | 14 + .../steps/15/webapp/view/App.view.xml | 128 ++++++++ .../webapp/view/ProductExtended.fragment.xml | 21 ++ .../15/webapp/view/ProductSimple.fragment.xml | 13 + packages/navigation/steps/02/README.md | 5 +- packages/navigation/steps/03/README.md | 5 +- packages/navigation/steps/04/README.md | 5 +- packages/navigation/steps/05/README.md | 5 +- packages/navigation/steps/06/README.md | 5 +- packages/navigation/steps/07/README.md | 5 +- packages/navigation/steps/08/README.md | 5 +- packages/navigation/steps/09/README.md | 5 +- packages/navigation/steps/10/README.md | 5 +- packages/navigation/steps/11/README.md | 5 +- packages/navigation/steps/12/README.md | 5 +- packages/navigation/steps/13/README.md | 5 +- packages/navigation/steps/14/README.md | 5 +- packages/navigation/steps/15/README.md | 5 +- packages/navigation/steps/16/README.md | 5 +- packages/navigation/steps/17/README.md | 6 +- packages/odatav4/steps.json | 20 +- packages/odatav4/steps/01/README.md | 2 - packages/odatav4/steps/02/README.md | 6 +- packages/odatav4/steps/03/README.md | 6 +- packages/odatav4/steps/04/README.md | 6 +- packages/odatav4/steps/05/README.md | 6 +- packages/odatav4/steps/06/README.md | 6 +- packages/odatav4/steps/07/README.md | 6 +- packages/odatav4/steps/08/README.md | 6 +- packages/odatav4/steps/09/README.md | 6 +- packages/odatav4/steps/10/README.md | 6 +- packages/odatav4/steps/11/README.md | 6 +- 232 files changed, 7591 insertions(+), 78 deletions(-) create mode 100644 packages/databinding/README.md create mode 100644 packages/databinding/assets/Tutorial_Data_Binding_896048e.png create mode 100644 packages/databinding/steps.json create mode 100644 packages/databinding/steps/01/README.md create mode 100644 packages/databinding/steps/01/assets/Tutorial_Data_Binding_Step_1_6d391d5.png create mode 100644 packages/databinding/steps/01/package.json create mode 100644 packages/databinding/steps/01/tsconfig.json create mode 100644 packages/databinding/steps/01/ui5.yaml create mode 100644 packages/databinding/steps/01/webapp/Component.ts create mode 100644 packages/databinding/steps/01/webapp/index-cdn.html create mode 100644 packages/databinding/steps/01/webapp/index.html create mode 100644 packages/databinding/steps/01/webapp/manifest.json create mode 100644 packages/databinding/steps/01/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/02/README.md create mode 100644 packages/databinding/steps/02/assets/Tutorial_Data_Binding_Step_1_6d391d5.png create mode 100644 packages/databinding/steps/02/package.json create mode 100644 packages/databinding/steps/02/tsconfig.json create mode 100644 packages/databinding/steps/02/ui5.yaml create mode 100644 packages/databinding/steps/02/webapp/Component.ts create mode 100644 packages/databinding/steps/02/webapp/index-cdn.html create mode 100644 packages/databinding/steps/02/webapp/index.html create mode 100644 packages/databinding/steps/02/webapp/manifest.json create mode 100644 packages/databinding/steps/02/webapp/model/data.json create mode 100644 packages/databinding/steps/02/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/03/README.md create mode 100644 packages/databinding/steps/03/assets/Tutorial_Data_Binding_Step_1_6d391d5.png create mode 100644 packages/databinding/steps/03/package.json create mode 100644 packages/databinding/steps/03/tsconfig.json create mode 100644 packages/databinding/steps/03/ui5.yaml create mode 100644 packages/databinding/steps/03/webapp/Component.ts create mode 100644 packages/databinding/steps/03/webapp/index-cdn.html create mode 100644 packages/databinding/steps/03/webapp/index.html create mode 100644 packages/databinding/steps/03/webapp/manifest.json create mode 100644 packages/databinding/steps/03/webapp/model/data.json create mode 100644 packages/databinding/steps/03/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/04/README.md create mode 100644 packages/databinding/steps/04/assets/Tutorial_Data_Binding_Step_4_2_6222561.png create mode 100644 packages/databinding/steps/04/assets/Tutorial_Data_Binding_Step_4_61d68f1.png create mode 100644 packages/databinding/steps/04/package.json create mode 100644 packages/databinding/steps/04/tsconfig.json create mode 100644 packages/databinding/steps/04/ui5.yaml create mode 100644 packages/databinding/steps/04/webapp/Component.ts create mode 100644 packages/databinding/steps/04/webapp/index-cdn.html create mode 100644 packages/databinding/steps/04/webapp/index.html create mode 100644 packages/databinding/steps/04/webapp/manifest.json create mode 100644 packages/databinding/steps/04/webapp/model/data.json create mode 100644 packages/databinding/steps/04/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/05/README.md create mode 100644 packages/databinding/steps/05/assets/Tutorial_Data_Binding_Step_4_61d68f1.png create mode 100644 packages/databinding/steps/05/package.json create mode 100644 packages/databinding/steps/05/tsconfig.json create mode 100644 packages/databinding/steps/05/ui5.yaml create mode 100644 packages/databinding/steps/05/webapp/Component.ts create mode 100644 packages/databinding/steps/05/webapp/index-cdn.html create mode 100644 packages/databinding/steps/05/webapp/index.html create mode 100644 packages/databinding/steps/05/webapp/manifest.json create mode 100644 packages/databinding/steps/05/webapp/model/data.json create mode 100644 packages/databinding/steps/05/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/06/README.md create mode 100644 packages/databinding/steps/06/assets/Tutorial_Data_Binding_Step_4_61d68f1.png create mode 100644 packages/databinding/steps/06/package.json create mode 100644 packages/databinding/steps/06/tsconfig.json create mode 100644 packages/databinding/steps/06/ui5.yaml create mode 100644 packages/databinding/steps/06/webapp/Component.ts create mode 100644 packages/databinding/steps/06/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/06/webapp/index-cdn.html create mode 100644 packages/databinding/steps/06/webapp/index.html create mode 100644 packages/databinding/steps/06/webapp/manifest.json create mode 100644 packages/databinding/steps/06/webapp/model/data.json create mode 100644 packages/databinding/steps/06/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/07/README.md create mode 100644 packages/databinding/steps/07/assets/Tutorial_Data_Binding_Step_7_d96cdf9.png create mode 100644 packages/databinding/steps/07/package.json create mode 100644 packages/databinding/steps/07/tsconfig.json create mode 100644 packages/databinding/steps/07/ui5.yaml create mode 100644 packages/databinding/steps/07/webapp/Component.ts create mode 100644 packages/databinding/steps/07/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/07/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/07/webapp/index-cdn.html create mode 100644 packages/databinding/steps/07/webapp/index.html create mode 100644 packages/databinding/steps/07/webapp/manifest.json create mode 100644 packages/databinding/steps/07/webapp/model/data.json create mode 100644 packages/databinding/steps/07/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/08/README.md create mode 100644 packages/databinding/steps/08/assets/Tutorial_Data_Binding_Step_8_12705f5.png create mode 100644 packages/databinding/steps/08/package.json create mode 100644 packages/databinding/steps/08/tsconfig.json create mode 100644 packages/databinding/steps/08/ui5.yaml create mode 100644 packages/databinding/steps/08/webapp/Component.ts create mode 100644 packages/databinding/steps/08/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/08/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/08/webapp/index-cdn.html create mode 100644 packages/databinding/steps/08/webapp/index.html create mode 100644 packages/databinding/steps/08/webapp/manifest.json create mode 100644 packages/databinding/steps/08/webapp/model/data.json create mode 100644 packages/databinding/steps/08/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/09/README.md create mode 100644 packages/databinding/steps/09/assets/Tutorial_Data_Binding_Step_9_1161575.png create mode 100644 packages/databinding/steps/09/package.json create mode 100644 packages/databinding/steps/09/tsconfig.json create mode 100644 packages/databinding/steps/09/ui5.yaml create mode 100644 packages/databinding/steps/09/webapp/Component.ts create mode 100644 packages/databinding/steps/09/webapp/controller/App.controller.ts create mode 100644 packages/databinding/steps/09/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/09/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/09/webapp/index-cdn.html create mode 100644 packages/databinding/steps/09/webapp/index.html create mode 100644 packages/databinding/steps/09/webapp/manifest.json create mode 100644 packages/databinding/steps/09/webapp/model/data.json create mode 100644 packages/databinding/steps/09/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/10/README.md create mode 100644 packages/databinding/steps/10/assets/Tutorial_Data_Binding_Step_10_d15f8bc.png create mode 100644 packages/databinding/steps/10/package.json create mode 100644 packages/databinding/steps/10/tsconfig.json create mode 100644 packages/databinding/steps/10/ui5.yaml create mode 100644 packages/databinding/steps/10/webapp/Component.ts create mode 100644 packages/databinding/steps/10/webapp/controller/App.controller.ts create mode 100644 packages/databinding/steps/10/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/10/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/10/webapp/index-cdn.html create mode 100644 packages/databinding/steps/10/webapp/index.html create mode 100644 packages/databinding/steps/10/webapp/manifest.json create mode 100644 packages/databinding/steps/10/webapp/model/data.json create mode 100644 packages/databinding/steps/10/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/11/README.md create mode 100644 packages/databinding/steps/11/assets/Tutorial_Data_Binding_Step_11_2_44db88e.png create mode 100644 packages/databinding/steps/11/assets/Tutorial_Data_Binding_Step_11_3_e67207b.png create mode 100644 packages/databinding/steps/11/package.json create mode 100644 packages/databinding/steps/11/tsconfig.json create mode 100644 packages/databinding/steps/11/ui5.yaml create mode 100644 packages/databinding/steps/11/webapp/Component.ts create mode 100644 packages/databinding/steps/11/webapp/controller/App.controller.ts create mode 100644 packages/databinding/steps/11/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/11/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/11/webapp/index-cdn.html create mode 100644 packages/databinding/steps/11/webapp/index.html create mode 100644 packages/databinding/steps/11/webapp/manifest.json create mode 100644 packages/databinding/steps/11/webapp/model/data.json create mode 100644 packages/databinding/steps/11/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/12/README.md create mode 100644 packages/databinding/steps/12/assets/Tutorial_Data_Binding_Step_12_1642433.png create mode 100644 packages/databinding/steps/12/package.json create mode 100644 packages/databinding/steps/12/tsconfig.json create mode 100644 packages/databinding/steps/12/ui5.yaml create mode 100644 packages/databinding/steps/12/webapp/Component.ts create mode 100644 packages/databinding/steps/12/webapp/controller/App.controller.ts create mode 100644 packages/databinding/steps/12/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/12/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/12/webapp/index-cdn.html create mode 100644 packages/databinding/steps/12/webapp/index.html create mode 100644 packages/databinding/steps/12/webapp/manifest.json create mode 100644 packages/databinding/steps/12/webapp/model/Products.json create mode 100644 packages/databinding/steps/12/webapp/model/data.json create mode 100644 packages/databinding/steps/12/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/13/README.md create mode 100644 packages/databinding/steps/13/assets/Tutorial_Data_Binding_Step_13_872d2ed.png create mode 100644 packages/databinding/steps/13/package.json create mode 100644 packages/databinding/steps/13/tsconfig.json create mode 100644 packages/databinding/steps/13/ui5.yaml create mode 100644 packages/databinding/steps/13/webapp/Component.ts create mode 100644 packages/databinding/steps/13/webapp/controller/App.controller.ts create mode 100644 packages/databinding/steps/13/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/13/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/13/webapp/index-cdn.html create mode 100644 packages/databinding/steps/13/webapp/index.html create mode 100644 packages/databinding/steps/13/webapp/manifest.json create mode 100644 packages/databinding/steps/13/webapp/model/Products.json create mode 100644 packages/databinding/steps/13/webapp/model/data.json create mode 100644 packages/databinding/steps/13/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/14/README.md create mode 100644 packages/databinding/steps/14/assets/Tutorial_Data_Binding_Step_14_b9fb758.png create mode 100644 packages/databinding/steps/14/package.json create mode 100644 packages/databinding/steps/14/tsconfig.json create mode 100644 packages/databinding/steps/14/ui5.yaml create mode 100644 packages/databinding/steps/14/webapp/Component.ts create mode 100644 packages/databinding/steps/14/webapp/controller/App.controller.ts create mode 100644 packages/databinding/steps/14/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/14/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/14/webapp/index-cdn.html create mode 100644 packages/databinding/steps/14/webapp/index.html create mode 100644 packages/databinding/steps/14/webapp/manifest.json create mode 100644 packages/databinding/steps/14/webapp/model/Products.json create mode 100644 packages/databinding/steps/14/webapp/model/data.json create mode 100644 packages/databinding/steps/14/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/15/README.md create mode 100644 packages/databinding/steps/15/assets/Tutorial_Data_Binding_Step_15_db27ba8.png create mode 100644 packages/databinding/steps/15/package.json create mode 100644 packages/databinding/steps/15/tsconfig.json create mode 100644 packages/databinding/steps/15/ui5.yaml create mode 100644 packages/databinding/steps/15/webapp/Component.ts create mode 100644 packages/databinding/steps/15/webapp/controller/App.controller.ts create mode 100644 packages/databinding/steps/15/webapp/i18n/i18n.properties create mode 100644 packages/databinding/steps/15/webapp/i18n/i18n_de.properties create mode 100644 packages/databinding/steps/15/webapp/index-cdn.html create mode 100644 packages/databinding/steps/15/webapp/index.html create mode 100644 packages/databinding/steps/15/webapp/manifest.json create mode 100644 packages/databinding/steps/15/webapp/model/Products.json create mode 100644 packages/databinding/steps/15/webapp/model/data.json create mode 100644 packages/databinding/steps/15/webapp/view/App.view.xml create mode 100644 packages/databinding/steps/15/webapp/view/ProductExtended.fragment.xml create mode 100644 packages/databinding/steps/15/webapp/view/ProductSimple.fragment.xml diff --git a/AGENTS.md b/AGENTS.md index a13946bd7..8e3bfbd4f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,11 +1,12 @@ # Repository Guide for AI Agents -This repository ships four OpenUI5 tutorials rendered as a GitHub Pages site with a client-side JavaScript / TypeScript language toggle. Every tutorial step is a self-contained, runnable UI5 app under `packages/<tutorial>/steps/NN/`. This document tells an agent enough about the layout, the toolchain, and the authoring conventions to be useful without re-deriving everything from source. +This repository ships five OpenUI5 tutorials rendered as a GitHub Pages site with a client-side JavaScript / TypeScript language toggle. Every tutorial step is a self-contained, runnable UI5 app under `packages/<tutorial>/steps/NN/`. This document tells an agent enough about the layout, the toolchain, and the authoring conventions to be useful without re-deriving everything from source. ## Layout - `packages/quickstart/` — 3 introductory steps. - `packages/walkthrough/` — 38 steps; the canonical reference for content shape and toolchain. +- `packages/databinding/` — 15 steps on data binding (JSON & resource models, property/two-way/one-way/aggregation/element/expression binding, formatters, data types, validation). - `packages/navigation/` — 17 steps on routing and navigation. - `packages/odatav4/` — 11 steps on OData V4. - `packages/<pkg>/README.md` — the tutorial's overview and step index. @@ -45,7 +46,7 @@ Rely on `npm start -w <workspace>` when you need to see a specific step in a bro ## Tutorial namespace and package naming -All four tutorials share one convention: +All five tutorials share one convention: - App namespace: `ui5.tutorial.<pkg>` (used in `manifest.json` `sap.app.id`, `Controller` names, XML `controllerName`, `index.html` resource-roots, and JSDoc `@namespace`). - Per-step `package.json` name: `ui5.tutorial.<pkg>.stepNN` (zero-padded). @@ -115,7 +116,7 @@ Verify a rippling change with `git grep` before *and* after — the count of mat - One-off scripts go under `_/` (gitignored). Match the pattern of the migration scripts already there (`migrate-*.js`, `promote-*.js`, `add-*.js`) — small Node scripts, no dependencies, walk the tree with `fs.readdirSync`. - Design scripts to be **idempotent**: re-running on already-migrated content should produce zero changes. This lets you split large migrations into review-and-apply passes. -- For content-shape changes that ripple across all four packages, verify with grep before and after (`git grep`) and touch one canary file first (usually [packages/walkthrough/steps/09/README.md](packages/walkthrough/steps/09/README.md) — it exercises every convention). +- For content-shape changes that ripple across all five packages, verify with grep before and after (`git grep`) and touch one canary file first (usually [packages/walkthrough/steps/09/README.md](packages/walkthrough/steps/09/README.md) — it exercises every convention). ## Verification diff --git a/README.md b/README.md index 23f4cec04..764780415 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ We first introduce you to the basic development paradigms like *Model-View-Contr This repository contains following tutorials: - [Quickstart](./packages/quickstart/) - [Walkthrough](./packages/walkthrough/) +- [Data Binding](./packages/databinding/) - [Navigation and Routing](./packages/navigation/) - [OData V4](./packages/odatav4/) diff --git a/package-lock.json b/package-lock.json index 421f60275..89d725bc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20007,6 +20007,66 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/ui5.tutorial.databinding.step01": { + "resolved": "packages/databinding/steps/01", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step02": { + "resolved": "packages/databinding/steps/02", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step03": { + "resolved": "packages/databinding/steps/03", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step04": { + "resolved": "packages/databinding/steps/04", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step05": { + "resolved": "packages/databinding/steps/05", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step06": { + "resolved": "packages/databinding/steps/06", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step07": { + "resolved": "packages/databinding/steps/07", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step08": { + "resolved": "packages/databinding/steps/08", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step09": { + "resolved": "packages/databinding/steps/09", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step10": { + "resolved": "packages/databinding/steps/10", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step11": { + "resolved": "packages/databinding/steps/11", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step12": { + "resolved": "packages/databinding/steps/12", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step13": { + "resolved": "packages/databinding/steps/13", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step14": { + "resolved": "packages/databinding/steps/14", + "link": true + }, + "node_modules/ui5.tutorial.databinding.step15": { + "resolved": "packages/databinding/steps/15", + "link": true + }, "node_modules/ui5.tutorial.navigation.step01": { "resolved": "packages/navigation/steps/01", "link": true @@ -20851,6 +20911,186 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "packages/databinding/steps/01": { + "name": "ui5.tutorial.databinding.step01", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/02": { + "name": "ui5.tutorial.databinding.step02", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/03": { + "name": "ui5.tutorial.databinding.step03", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/04": { + "name": "ui5.tutorial.databinding.step04", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/05": { + "name": "ui5.tutorial.databinding.step05", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/06": { + "name": "ui5.tutorial.databinding.step06", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/07": { + "name": "ui5.tutorial.databinding.step07", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/08": { + "name": "ui5.tutorial.databinding.step08", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/09": { + "name": "ui5.tutorial.databinding.step09", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/10": { + "name": "ui5.tutorial.databinding.step10", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/11": { + "name": "ui5.tutorial.databinding.step11", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/12": { + "name": "ui5.tutorial.databinding.step12", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/13": { + "name": "ui5.tutorial.databinding.step13", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/14": { + "name": "ui5.tutorial.databinding.step14", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, + "packages/databinding/steps/15": { + "name": "ui5.tutorial.databinding.step15", + "version": "1.0.0", + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } + }, "packages/navigation/steps/01": { "name": "ui5.tutorial.navigation.step01", "version": "1.0.0", diff --git a/packages/databinding/README.md b/packages/databinding/README.md new file mode 100644 index 000000000..3e47dd024 --- /dev/null +++ b/packages/databinding/README.md @@ -0,0 +1,54 @@ +<!-- Data Binding Tutorial overview --> + +# Data Binding Tutorial + +In this tutorial, we explain the concepts of data binding in OpenUI5. + +Data binding is used to bind UI elements to data sources. This keeps the data in sync and allows data editing on the UI. + +For data binding, you need a model and a binding instance: The model holds the data and provides methods to set the data or retrieve it from a server. It also provides a method for creating bindings to the data. When you call this method, a binding instance is created, which contains the binding information and provides an event, which is fired whenever the bound data changes. An element can listen to this event and update its visualization according to the new data. + +The UI uses data binding to bind controls to the model which holds the application data, so that the controls are updated automatically whenever application data changes. Data binding is also used in reverse, when changes in the control, for example data entered by the user, cause updates in the underlying application data. This is called two-way binding. + +## Preview + +![Preview of three panels of the OpenUI5 application that is going to be built in this tutorial. Contains three panels with name input fields, address details, and a product list.](assets/Tutorial_Data_Binding_896048e.png "The Data Binding sample app with panels for name, address, and a product list") + +> :tip: +> You don't have to do all tutorial steps sequentially, you can also jump directly to any step you want. Just download the code from the previous step, copy it to your workspace, and ensure that the application runs by calling the `webapp/index.html` file. + +*** + +### In this Tutorial + +The tutorial consists of the following steps. To start, just open the first link — you'll be guided from there. + +- **[Step 1: No Data Binding](./steps/01/README.md)** — In this step, we create a basic application and simply place some text on the screen using a standard sap.m.Text control. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/01/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 2: Creating a Model](./steps/02/README.md)** — In this step, we create a model. It serves as a container for the data your application operates on. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/02/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-02.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-02-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 3: Create Property Binding](./steps/03/README.md)** — Although there is no visible difference, the text on the screen is now derived from model data. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/03/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-03.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-03-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 4: Two-Way Data Binding](./steps/04/README.md)** — We change the user interface to display first and last name fields using sap.m.Input fields and add a check box to enable or disable them. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/04/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-04.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-04-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 5: One-Way Data Binding](./steps/05/README.md)** — Unlike two-way binding, one-way data binding lets data travel in one direction only: from the model to the consumer, but never back. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/05/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-05.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-05-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 6: Resource Models](./steps/06/README.md)** — Business applications often require language-specific (translatable) text. We place all translatable texts into a resource bundle. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/06/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-06.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-06-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 7: \(Optional\) Resource Bundles and Multiple Languages](./steps/07/README.md)** — Resource bundles enable an app to run in multiple languages without changing any code. We create a German version of the app. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/07/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-07.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-07-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 8: Binding Paths: Accessing Properties in Hierarchically Structured Models](./steps/08/README.md)** — We explore how to reference fields in a hierarchically structured model object by adding a second panel with address data. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/08/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-08.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-08-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 9: Formatting Values](./steps/09/README.md)** — We add a link that sends an e-mail to Harry Hawk, using a custom formatter function to convert the model data. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/09/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-09.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-09-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 10: Property Formatting Using Data Types](./steps/10/README.md)** — We apply data types such as Currency to controls to ensure the value displayed on the screen is formatted correctly. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/10/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-10.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-10-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 11: Validation Using `sap/ui/core/Messaging`](./steps/11/README.md)** — We enable validation for the entire app so that validation error messages are passed to Messaging and connected to the offending control. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/11/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-11.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-11-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 12: Aggregation Binding Using Templates](./steps/12/README.md)** — Aggregation binding lets a control bind to a list within the model data. We add a third panel with a list of products. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/12/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-12.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-12-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 13: Element Binding](./steps/13/README.md)** — We use a form with relatively bound controls and bind it to the selected list entity via element binding. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/13/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-13.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-13-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 14: Expression Binding](./steps/14/README.md)** — An expression binding lets you display a calculated value on the screen, derived from values found in a model object. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/14/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-14.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-14-js.zip)<span class="lang-suffix"> (JS)</span></span>) +- **[Step 15: Aggregation Binding Using a Factory Function](./steps/15/README.md)** — We use a factory function to generate different controls based on the data received at runtime. ([🔗 Live Preview](https://ui5.github.io/tutorials/databinding/build/15/index-cdn.html) \| <span class="ts-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-15.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download Solution](https://ui5.github.io/tutorials/databinding/databinding-step-15-js.zip)<span class="lang-suffix"> (JS)</span></span>) + +*** + +## Related Information + +[Data Binding](https://sdk.openui5.org/topic/68b9644a253741e8a4b9e4279a35c247.html "You use data binding to bind UI elements to data sources to keep the data in sync and allow data editing on the UI.") + +[Model View Controller \(MVC\)](https://sdk.openui5.org/topic/91f233476f4d1014b6dd926db0e91070.html "The Model View Controller (MVC) concept is used in OpenUI5 to separate the representation of information from the user interaction. This separation facilitates development and the changing of parts independently.") + +*** + +## License + +Copyright (c) 2026 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](../../LICENSE) file. diff --git a/packages/databinding/assets/Tutorial_Data_Binding_896048e.png b/packages/databinding/assets/Tutorial_Data_Binding_896048e.png new file mode 100644 index 0000000000000000000000000000000000000000..2c020aa6298db411a62c891592d85759f25742ac GIT binary patch literal 111804 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�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 YBlj`_cYy3PoD4W_=^E=)gB_#)1y>-kYybcN literal 0 HcmV?d00001 diff --git a/packages/databinding/steps.json b/packages/databinding/steps.json new file mode 100644 index 000000000..bcee0b845 --- /dev/null +++ b/packages/databinding/steps.json @@ -0,0 +1,141 @@ +{ + "tutorial": "databinding", + "namespace": "ui5.tutorial.databinding", + "steps": [ + { + "n": 1, + "id": "01", + "title": "No Data Binding", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/01/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-01.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-01-js.zip", + "description": "In this step, we create a basic application and simply place some text on the screen using a standard sap.m.Text control. The text in this control is a hard-coded part of the control's definition; therefore, this is not an example of data binding!" + }, + { + "n": 2, + "id": "02", + "title": "Creating a Model", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/02/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-02.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-02-js.zip", + "description": "In this step, we create a model. It serves as a container for the data your application operates on." + }, + { + "n": 3, + "id": "03", + "title": "Create Property Binding", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/03/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-03.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-03-js.zip", + "description": "Although there is no visible difference, the text on the screen is now derived from model data." + }, + { + "n": 4, + "id": "04", + "title": "Two-Way Data Binding", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/04/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-04.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-04-js.zip", + "description": "In the examples we've looked at so far, we've displayed the value of a model property using a read-only field. We'll now change the user interface to display first and last name fields using sap.m.Input fields. We're also adding a check box control to enable or disable both input fields. This setup illustrates a feature known as \"two-way data binding\". As the view now contains more controls, we're also moving the view definition into an XML file." + }, + { + "n": 5, + "id": "05", + "title": "One-Way Data Binding", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/05/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-05.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-05-js.zip", + "description": "Unlike the two-way binding behavior we've seen, one-way data binding is also possible. In this case, data travels in one direction only: from the model, through the binding instance, to the consumer \\(usually the property of a control\\), but never in the other direction. Let's modify the previous example to use one-way data binding. This shows how you can switch off the flow of data from the user interface back to the model if needed." + }, + { + "n": 6, + "id": "06", + "title": "Resource Models", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/06/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-06.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-06-js.zip", + "description": "Business applications often require language-specific \\(translatable\\) text used as labels and descriptions on the user interface." + }, + { + "n": 7, + "id": "07", + "title": "\\(Optional\\) Resource Bundles and Multiple Languages", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/07/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-07.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-07-js.zip", + "description": "Resource bundles exist to enable an app to run in multiple languages without the need to change any code. To demonstrate this feature, let's create a German version of the app – in fact, all we need to do is create a German version of the resource bundle file. In our code, we activate the German locale for the ResourceModel." + }, + { + "n": 8, + "id": "08", + "title": "Binding Paths: Accessing Properties in Hierarchically Structured Models", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/08/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-08.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-08-js.zip", + "description": "In Step 6 , we stated that the fields in a resource model are arranged in a flat structure; in other words, there is no hierarchy of properties. However, this is only true for resource models. The properties within JSON and OData models are usually arranged in a hierarchical structure. So, let's explore how to reference fields in a hierarchically structured model object." + }, + { + "n": 9, + "id": "09", + "title": "Formatting Values", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/09/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-09.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-09-js.zip", + "description": "We'd also like to provide our users with a way of contacting Harry Hawk, so we're adding a link that sends an e-mail to Harry. To do this, we convert our data in the model to match the sap.m.URLHelper.normalizeEmail API. As soon as the user changes the name, the e-mail also changes. We need a custom formatter function for this." + }, + { + "n": 10, + "id": "10", + "title": "Property Formatting Using Data Types", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/10/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-10.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-10-js.zip", + "description": "OpenUI5 offers a set of simple data types, including Boolean, Currency, Date and Float. You can apply these data types to controls to ensure that the value displayed on the screen is formatted correctly. If the field is open for input, this also ensures that the user input meets the requirements of that data type. Let's add a new field called Sales Amount of type Currency." + }, + { + "n": 11, + "id": "11", + "title": "Validation Using `sap/ui/core/Messaging`", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/11/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-11.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-11-js.zip", + "description": "Up to this point, we've created a currency field that formats itself correctly. The currency data type can also validate user input to ensure it meets currency requirements. However, OpenUI5 manages data type validation functions and doesn't have a built-in mechanism for reporting error messages back to the UI. We therefore need a way to report error messages from validation functions back to the user. In this step, we're enabling validation for the entire app with a feature known as \"Messaging\". Once this is set up, any validation error messages based on user input get passed to Messaging, which then connects them to the appropriate view and control that caused the error." + }, + { + "n": 12, + "id": "12", + "title": "Aggregation Binding Using Templates", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/12/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-12.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-12-js.zip", + "description": "Aggregation binding, also known as \"list binding\", lets a control bind to a list within the model data. This binding allows relative binding to the list entries by its child controls." + }, + { + "n": 13, + "id": "13", + "title": "Element Binding", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/13/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-13.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-13-js.zip", + "description": "Now, let's do something with that newly generated list. Typically, you use a list to allow selection of an item and then display the details of that item elsewhere. To accomplish this, we use a form with relatively bound controls and bind it to the selected entity via element binding." + }, + { + "n": 14, + "id": "14", + "title": "Expression Binding", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/14/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-14.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-14-js.zip", + "description": "An expression binding lets you display a calculated value on the screen, which is derived from values found in a model object. This feature allows you to insert simple formatting or calculations directly into the data binding string. In this example, we're changing the color of the price depending on whether it's above or below a certain threshold. The threshold value is stored in the JSON model." + }, + { + "n": 15, + "id": "15", + "title": "Aggregation Binding Using a Factory Function", + "previewUrl": "https://ui5.github.io/tutorials/databinding/build/15/index-cdn.html", + "zipTs": "https://ui5.github.io/tutorials/databinding/databinding-step-15.zip", + "zipJs": "https://ui5.github.io/tutorials/databinding/databinding-step-15-js.zip", + "description": "Instead of using a single hard-coded template control, we now opt for a factory function to generate different controls based on the data received at runtime. This approach is much more flexible and allows for the display of complex or heterogeneous data." + } + ] +} diff --git a/packages/databinding/steps/01/README.md b/packages/databinding/steps/01/README.md new file mode 100644 index 000000000..baaae8fec --- /dev/null +++ b/packages/databinding/steps/01/README.md @@ -0,0 +1,226 @@ + +# Step 1: No Data Binding + +In this step, we create a basic application and simply place some text on the screen using a standard `sap.m.Text` control. The text in this control is a hard-coded part of the control's definition; therefore, this is not an example of data binding! + +## Preview + +![The browser shows the text "Hi, my name is Harry Hawk"](assets/Tutorial_Data_Binding_Step_1_6d391d5.png "The browser shows the text "Hi, my name is Harry Hawk"") + +You can view this step live: [🔗 Live Preview of Step 1](https://ui5.github.io/tutorials/databinding/build/01/index-cdn.html). + +## Setup + +Open a terminal and install UI5 Tooling globally on your machine by executing the following command: + +```sh +npm install --global @ui5/cli +``` + +## Coding + +You can download the solution for this step here: <span class="ts-only">[📥 Download step 1](https://ui5.github.io/tutorials/databinding/databinding-step-01.zip)<span class="lang-suffix"> (TS)</span></span><span class="js-only">[📥 Download step 1](https://ui5.github.io/tutorials/databinding/databinding-step-01-js.zip)<span class="lang-suffix"> (JS)</span></span>. + +### Folder structure for this step + +```text +webapp/ +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +1. Create a folder on your local machine which will contain all the sources of the app we're going to build. We'll refer to this folder as the “app root folder”. + +2. Create a new file called `package.json` which will enable you to execute commands and consume packages from the [npm registry](https://www.npmjs.com/) via the npm command line interface. Enter the following content: + +## package.json \(New\) + +```json +{ + "name": "ui5.tutorial.databinding", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html" + } +} +``` + +3. Create a new folder named `webapp` in the app root folder. It will contain all the sources that become available in the browser later. We'll refer to this folder as the "webapp folder". + +4. Create a new HTML file named `index.html` in your webapp folder and enter the following content: + +## webapp/index.html \(New\) + +```html +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Data Binding Tutorial + + + +
+ + +``` + +5. Create a new file named `manifest.json` in the webapp folder; it's also known as the "app descriptor". All application-specific configuration options which we'll introduce in this tutorial will be added to this file. Enter the following content: + +## webapp/manifest.json \(New\) + +```json +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "Data Binding Tutorial", + "description": "A simple app that explains how to use data binding features of OpenUI5" + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} +``` + +6. Create a new file named `Component.ts/.js` in the webapp folder. Enter the following content: + +## `webapp/Component.ts/.js` \(New\) + +```ts +// webapp/Component.ts +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} +``` + +```js +// webapp/Component.js +sap.ui.define(["sap/ui/core/UIComponent"], function (UIComponent) { + "use strict"; + + return UIComponent.extend("ui5.tutorial.databinding.Component", { + metadata: { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + } + }); +}); +``` + +7. Create a new folder named `view` in the webapp folder. Then, create a new file `App.view.xml` within the `view` folder. We start by placing the `sap.m.Text` control into the XML view. Since the value of the control's text property is hard-coded, it doesn't relate to any data that might exist within a model object. Therefore, data binding is **not** used here. + +## webapp/view/App.view.xml \(New\) + +```xml + + + +``` + +8. Open a terminal in the app root folder and execute `npm i -D @ui5/cli` to install UI5 Tooling in the app root folder. + +9. Execute `ui5 init` in the app root folder. + +10. Execute `ui5 use OpenUI5` + +11. Execute `ui5 add sap.m sap.ui.core sap.ui.layout themelib_sap_horizon` + +12. Configure the tooling extensions we installed from npm in our UI5 Tooling setup, so we can use them in our project: + +## ui5.yaml + +```yaml +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression +``` + +13. Execute `npm start` to start the web server and to open a new browser window hosting your newly created `index.html`. + +*** + +**Next:** [Step 2: Creating a Model](../02/README.md "In this step, we create a model. It serves as a container for the data your application operates on.") + +*** + +**Related Information** + +[Data Binding](https://sdk.openui5.org/topic/68b9644a253741e8a4b9e4279a35c247.html "You use data binding to bind UI elements to data sources to keep the data in sync and allow data editing on the UI.") + +[Model View Controller \(MVC\)](https://sdk.openui5.org/topic/91f233476f4d1014b6dd926db0e91070.html "The Model View Controller (MVC) concept is used in OpenUI5 to separate the representation of information from the user interaction. This separation facilitates development and the changing of parts independently.") diff --git a/packages/databinding/steps/01/assets/Tutorial_Data_Binding_Step_1_6d391d5.png b/packages/databinding/steps/01/assets/Tutorial_Data_Binding_Step_1_6d391d5.png new file mode 100644 index 0000000000000000000000000000000000000000..aaf6180a0c2134cc6675f54337f74e56c1e4373c GIT binary patch literal 6851 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 VchfEWg#G(KQdQDYtd_S7`5&hGant|+ literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/01/package.json b/packages/databinding/steps/01/package.json new file mode 100644 index 000000000..9546aadb1 --- /dev/null +++ b/packages/databinding/steps/01/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step01", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/01/tsconfig.json b/packages/databinding/steps/01/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/01/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/01/ui5.yaml b/packages/databinding/steps/01/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/01/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/01/webapp/Component.ts b/packages/databinding/steps/01/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/01/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/01/webapp/index-cdn.html b/packages/databinding/steps/01/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/01/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/01/webapp/index.html b/packages/databinding/steps/01/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/01/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/01/webapp/manifest.json b/packages/databinding/steps/01/webapp/manifest.json new file mode 100644 index 000000000..463185964 --- /dev/null +++ b/packages/databinding/steps/01/webapp/manifest.json @@ -0,0 +1,38 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "Data Binding Tutorial", + "description": "A simple app that explains how to use data binding features of OpenUI5" + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/01/webapp/view/App.view.xml b/packages/databinding/steps/01/webapp/view/App.view.xml new file mode 100644 index 000000000..470494168 --- /dev/null +++ b/packages/databinding/steps/01/webapp/view/App.view.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/databinding/steps/02/README.md b/packages/databinding/steps/02/README.md new file mode 100644 index 000000000..34a2f277b --- /dev/null +++ b/packages/databinding/steps/02/README.md @@ -0,0 +1,99 @@ + +# Step 2: Creating a Model + +In this step, we create a model. It serves as a container for the data your application operates on. + +You can define the business data within a model using various formats: + +- JavaScript Object Notation \(JSON\) + +- Extensible Markup Language \(XML\) + +- OData + +- Your own custom format \(not covered in this tutorial\) + +> :note: +> There's also a special type of model called a "resource model". This model type is used as a wrapper object around a resource bundle file. The names of such files must end with `.properties`. They're typically used for holding language-specific text. +> +> We'll use this in [Step 6: Resource Models](../06/README.md). + +When you create JSON, XML, and resource models, the data they contain is loaded in a single request \(either from a file stored locally on the client or by requesting it from a Web server\). In other words, after the model's data has been requested, the entire model is known to the application. These models are known as client-side models. Tasks such as filtering and sorting are performed locally on the client. + +An OData model, however, is a server-side model. This means that whenever an application needs data from the model, it must be requested from the server. Such a request almost never returns all the data in the model, typically because this would be far more data than the client application requires. Consequently, tasks such as sorting and filtering should always be delegated to the server. + +In this tutorial, we focus on JSON models since they're the simplest ones to work with. + +## Preview + +![The browser shows the text "Hi, my name is Harry Hawk"](assets/Tutorial_Data_Binding_Step_1_6d391d5.png "The browser shows the text "Hi, my name is Harry Hawk"") + +You can view this step live: [🔗 Live Preview of Step 2](https://ui5.github.io/tutorials/databinding/build/02/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/databinding/databinding-step-02.zip) (TS)[📥 Download step 2](https://ui5.github.io/tutorials/databinding/databinding-step-02-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +1. Create a new folder named `model` in the `webapp` folder. In this folder, create a file called `data.json` with the following content: + +## webapp/model/data.json \(New\) + +```json +{ + "greetingText": "Hi, my name is Harry Hawk" +} +``` + +2. Create a new JSON model in the `manifest.json` and set its path via a URI. This binds the model object to the app component and makes it globally available to all controls used within the application. + +## webapp/manifest.json + +```json +{ + ... + "sap.ui5": { + ... + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + } + }, + ... + } +} +``` + +Generally speaking, a model object holding business data should be bound to the app's `Component.ts/.js` or to the view that displays the data. For an example, see the *Walkthrough* tutorial, [Step 7: JSON Model](../../walkthrough/steps/07/README.md) \(binding to the View\) or [Step 9: Component Configuration](../../walkthrough/steps/09/README.md) \(binding to the Component\). + +The text that is displayed on the UI is still hard-coded and not taken from the model. We'll bind the property `greetingText` to our UI control in the next step. + +> :note: +> You can set models on every control by calling `setModel()`. The model is then propagated to all aggregated child controls \(and their children, and so on …\). All child controls then have access to that model. + +*** + +**Next:** [Step 3: Create Property Binding](../03/README.md "Although there is no visible difference, the text on the screen is now derived from model data.") + +**Previous:** [Step 1: No Data Binding](../01/README.md "In this step, we create a basic application and simply place some text on the screen using a standard sap.m.Text control.") + +*** + +**Related Information** + +[Models](https://sdk.openui5.org/topic/e1b625940c104b558e52f47afe5ddb4f.html "A model in the Model View Controller concept holds the data and provides methods to retrieve the data from the database and to set and update data.") + +[JSON Model](https://sdk.openui5.org/topic/96804e3315ff440aa0a50fd290805116.html#loio96804e3315ff440aa0a50fd290805116 "The JSON model can be used to bind controls to JavaScript object data, which is usually serialized in the JSON format.") diff --git a/packages/databinding/steps/02/assets/Tutorial_Data_Binding_Step_1_6d391d5.png b/packages/databinding/steps/02/assets/Tutorial_Data_Binding_Step_1_6d391d5.png new file mode 100644 index 0000000000000000000000000000000000000000..aaf6180a0c2134cc6675f54337f74e56c1e4373c GIT binary patch literal 6851 zcmeI1^-~ni8^`G`0SW02kC0N1F6l;TjyOc%NNG5vLApaYLO>dhZls%|J-S0U5Co(I z`Qi`X|Kc;V@!EPnI~&jJ%zo0*QYI#NMu3KfMy#r$sE3AzuJ*{8csP$~NNIlBV}|}g zPgx$V8bN>XSWvVzQhleXiN^cLcxdR6_GnoDx;(~bj}Z+GGanrd^D#&NSC^0R|5kLh ze9ZqdTIRowtmRCJkAwJB73BeOvRqj_7lHr?_x?wb=E?H&gh0V9Edf zd-J->@vEX&en&nX@I*BH$H1=2%sV3dgzJl5QKCVaH3#qE7ne`))5&{^@mQD;*i9MD z%*NV!g|>);TqQ)-Kn*%&ZKJOitq633jHV_F*~Y>oJa$S0e$8tv)_XGz2;pabVt8%0 zR`30-R>EgTg7xKfTsGhyHzAQtIf0<=HKk;j&$aX|xZ%!aoI2S=cw}+? zp8HC2Mr}|l_ubY(!&64OzOi>JJ2l0dg;lnfD@@159<@K^!D23p{8H;o++&7SPwyBg z@1(ugFb;mLMY6aouy3xljZXv!%~0jc3QSi6 z2KFnJah*}Ui!J4$UMkGYWTMV9NTH4GB=^&)BtpiWdN((rquE*^zdP_7tt7SGQI!Pb zl3Eq2Vk}G8F62+jUD$%PQJA&|8Q$ybK6-GS+*is#Xv_455A)XMzAJ!}bLlWjcT@Hq zQF{`$n4ZJ^D<%f0mEFMldqW-4YQn=hufDz3O5jTM=Y}>*;nvM{dt1+0&-oosTf^1> zV{s_3pTyH-Ffd-X<#@xp!3lKN0R9y{zvt*m@;C5dmi1uX8&1F>G@l4!0Vx!Q zxvsU4R$_wq7;j$&gjEGS2_VkI@!S;Npn&5ediHXmf2!hS_>BV4KlWSyos20q{MJ)< z(p_VMyrF(^h(EC1acycUlDmn0tn{asFus6+ODi`OM9N4mSs_=8O0jxoHl$5{&4y(i zf=k7~QfH+6b~7p=CZp0=cx?6ep~`na7Q5DiR(1FHUld&6{pwCVW)P#e-$f>5rB2g* zZVhT2O((y)7ICP6+sr+ z5_Vor3OS4NbZ+L=^ove3-bdH??K1ii7j@nb=*G=f^+nT{lmaFTir7eb`qNg2W52}* z6w64iXv``F6Vnx)nA_5)^W6~+@A)de#d_P?Kg`Uh_WRXR$v{nBr;iq@dHhZxQ4#gw zKJ20w5kbuL1lG8q=|FHEmsXC*cVG8?k+t_H)lz>n4C=t6-0JC7$!4SIS8Qz96f2RJ z?^N#}mooh2YW5o_O5j{u^-Qnw3+(`tDAdex3V-pFe*$XU$)Q zKZ%}<5`kvRFl8u#@p_j{Mpx#f+FD2JX;)L7WzbR# z?plTgxbMY`M8w3W%dg*FOK3?*N$Z{pR-1KdF}mC*UmVR?IIR1?a)40a2++!II+=K8 zD;!z$tMj+*%v{#v$>bbK;;ZoRDJZU7Z&11=c~%WODM|$d7GHdh(uxVZm8u?Y8#q2w{p8Pv+* z9crWifvV~n0=DE1mg;Nn>O8`uWqIvaO1nDFdi6iA_8Tc5UA_hVc1&*+HeV8PN5UF+ zcg6VF#nPe!j3PoS?FWxeb_WnrwkmmydbMWuw68AoX$3usI_dQW2URvhZg$rv#q=M_ z@r0)O34}QCL>9h;32mOU27izdFlJfx|DBRYEzk@A6h=d5m4BuT(XBfIaEO`MWbQi@ zK?IB78DxEtAu?m9Xz*8|Rbi1@xR8+FN`_vQ8g?+5XlqTd(0fQowi+~3(6-GZus-49 zV|)EvsR@g&u5$M2FqRY1l0ETvCZG7XKR#UKS={eN-ozXktLO2A}`>DmB!lIppH} zb%~c;;~jhO7-v3PCMBnzPY|_ZdM)_oaO#_F#qlv?6h}m?&o-MremsYvgizQ$aOKvg zpT@V;3X^u}N>jBWWM*$3X?uBfZoARGTD1J!3H*C0?rfy=mtGrK_ZUyywh|%1!X{hN zo9K{(_{18+1AU__UTAEoGli1_YIduS%@s6Wl9|;QB!YrQGdk67iEkCZSFeL%m!Um< zq94Thq3cK7dBiBTw9$+tEvdDPMwHm%*k&wZ0FfiCEj+|0blUlIrp=xr>-RC~%uy?e zyXLODpEjtq;Pq2lp{)E7(-1G# zP>gnAH#@$RfUD2~y~eHi@44P>-&n~+0O=efZ=4{DO{@LVEYi zq|>Bs>wet(;ZkHDzIm=j3QYol8VCDPmC>i(GW-qWb)ZRv`#JIaZp*uTIDXRc{JrhL zLRTYrb{calh(&5EC}Xx?z)odPgSJRx#5%no;!o82v{!@O;%FoOB$?10!{^#Q9E;ZL zmjQEF6Du!OClSZCNAN(s{XJCiRZxLghOwMYq%O9F1KILCN^H1Xlny-Pyf9Fk>UV+@ z<216Rq|jBd?L%8-)M;VTxnNxPBMMib;o@M{BJuZ^IKV##V0}t<^6)!f#O3@Do8hT^ zq?SRcY+!v{JlQCXyz$=2n<&(a*me}cRJ6h#oH1#79n(?u!C|u5$G~B}vS4w6XABGJ zv;sc&BwaDrUh)o5pvF$O=c@i{{Nr8rw)|j{dA)9whtH=OA{)S z!9DRf=yNFTF0w#GZII{yi!d6#zEz9MZg>f)bNwY$X1f3SMoU-`yzJA1Kx$man>sFa zK%T*>ODhE=JBSKBwI~QJhP|v%eco+q(y%pg*m%?s?;a`nHRj{CGTY6GmzCr}0LGqy zq#qr%n-Q@}VPlJa=)v3-tkx;VOR9o^!Be^q?qt|e9+f0Fx?I4c38Y~PJrF=&QPJP% z@V5B~_k**i<9e!(#2jZtk*yjPk_BZfVv16+>&27n4g1(YlBOIFV_*LZmnHcTz*;9U z7pYB&N4ckazB6jY8bOlaD}u`BO@5Wfsrihe=+(sg+TNHWzu@ZAweP3+_1H2<$9LKK z`{FOmVsrWkx7^+?8E>@-y_^7kHL@MpMTEmC@-elO=TkgA%!ft zfNdxu>|Ds9-|R~C*x8H}qm z%j_(NABJO0qge5#T}ucgL0|V|yh;x6(s}KfnR6bQDW0Z}Ds9s(!7se0L~h%f*M!u7 zd{~Qn2)8UVvwS9sc!|te;6mbOJhFAX#2~gDwKi%jq!4+tU7}GBI4K)q)F3-O^|z`q zNjn2l`$u*O%FmKEu~vWhq;D7KC=h1D%R1RPr`a{nr$B>vTVb}=6kzpNlp7vu#8yu% z*(+X|X>;w+RR--x?_hQ{ZH(vnoc6ZN=xsDrE9)=n^rfIgR>w|(FLC~&ghK(`78o?W z%_npEg4DhhXXRDLVGC7rRv&cAEAsU15VZrjZya9^Lm%2^seZf;lpdj)O#g8x^A!jW zAz^}FQxLV|QuhjfgjqG3V@But+(hs4M|R(9$T zDs-C+A~X0N27u_;>6>poeafiOBQ=V&h16PnW8EF8Yl%7%dIhiW8=>)$&dR<+yU(IM zR&?p4Q`n*F&p$O%z(})*sIuU<>z4;Z6ywb2bGCwGsT7Xdz2UcnLZx&L28|#6FBuZQ zPIj?wb86OOb5_E+nkvxxARQVZ8Z;H-=-%K7Bc;KW6X6NQ^vgEEu#nB8foliq$a~p6 zwe+C4-m#!pKa`6b5 zju!wmYL(dFES_BJZXW81K?NOFC}SM$VT?W8=mLGZHYJ&pq6Qke@b@?tn0}r~X}sl= z9SchPZ83|zI^84uJEY_-8W{T-nh!xN;4sJUE~zC+RR_}Icwb{eSoo-HMl*P986-Oq z+pGB>z^YYPTp+s+;*1xOjef^|%X^`l-dD}H6`ix+3sSy4G~2PHWlJw6t>J3C`Zrxo@g|UpNHuOp_lY&D>j&9-v6~BQj_28g-Pk=8+v(_hOQ$V1t zRrbR;OgPecppCUa3w-)&y|m}}EFYo&ZT2Hv18I)4(IbyhjkiG0v6n%P${WTBsl+x; zHZRHZ`C9u-JYZZAwmil&@2QTD2&eY&q3?rpbCZ1@ZevBMRGsTyegCGp4-bSRKD81lR!aNw99qk^274#G1fKBdT{Tr8thryXJrz-~HTGnliQDCmy)J1k-<;!Q zko(7j8>+LIc=;u7KmFIxyIe&0zPeA~$60nmj3aGqodOSxzYJ&_TYUMv~u*9VHgMq48}=43=9A70C-k@$o@*>h)ZZ z6rMniLxRp1LF#uF^C;L}>^`aW711>BOAU$Oj0P z<w#-@CJ9V@LT0ktfQ zS+rfdp)Pf)+$Q~Ag3Opo8gN~v81YQKAYVL+BVy$I1IdH0;cMcbRmOp;6h)_(*vH*l zBPI-1`6Q4PQY?zg+Gfu`2=ljg&xi)zEWnAKNW07uxtgU2qi|a`#_+f^kt;>d7o%9V zD1lzxRFFhloS_XFDx@0abL%YLt!7PBNZtZ<_AzT0z184NC{|#kEWt#_*7U(C8ZJz* z1(%ivNP^gWb{7XB>{~pj?@C!?B1x0$1ifS-0xG-KnAh~Ng1vjh0kVopN+2CW9|khf zDB*|;!wh~FOpglOx1SYe$c&^PKE>m)M0R62B~-q=rsItj=WD<{&v3iN*Hzr_mDg%f zv5KNMi&?^;vqP{t0tF0cE;s4xRK{}WEvz}}U@f@EB+z3W>d54*eJRkDZ@e5s6TW#C z&R<;Q=bGDa^oEgu`TOirOk^S7rgeO)LccH^eK`uV%X3a|JE+F|616yDgdH1t8#YW=^lZSRHc zF=7>FAU=C}sv9mGsSo31Q;y`!!QUi;)t8iSyuG83+0ek`4vX2iM9d7fP zBECm8fk*5ldJbbnCwEm1l0Qi1bb3_t`G>Yf2b7OS)G;X4GDPfS9@YM#K%ZL4qY*Rm ze4Vx;Rntc`i%0aMeGB|&BpEGyhC_4dQSI~}j=^Vm{+sfDtNh==`0x7t=P}q%KdN62 VchfEWg#G(KQdQDYtd_S7`5&hGant|+ literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/02/package.json b/packages/databinding/steps/02/package.json new file mode 100644 index 000000000..e08510fc6 --- /dev/null +++ b/packages/databinding/steps/02/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step02", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/02/tsconfig.json b/packages/databinding/steps/02/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/02/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/02/ui5.yaml b/packages/databinding/steps/02/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/02/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/02/webapp/Component.ts b/packages/databinding/steps/02/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/02/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/02/webapp/index-cdn.html b/packages/databinding/steps/02/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/02/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/02/webapp/index.html b/packages/databinding/steps/02/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/02/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/02/webapp/manifest.json b/packages/databinding/steps/02/webapp/manifest.json new file mode 100644 index 000000000..c2727bd66 --- /dev/null +++ b/packages/databinding/steps/02/webapp/manifest.json @@ -0,0 +1,44 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "Data Binding Tutorial", + "description": "A simple app that explains how to use data binding features of OpenUI5" + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/02/webapp/model/data.json b/packages/databinding/steps/02/webapp/model/data.json new file mode 100644 index 000000000..7b62fba1f --- /dev/null +++ b/packages/databinding/steps/02/webapp/model/data.json @@ -0,0 +1,3 @@ +{ + "greetingText": "Hi, my name is Harry Hawk" +} \ No newline at end of file diff --git a/packages/databinding/steps/02/webapp/view/App.view.xml b/packages/databinding/steps/02/webapp/view/App.view.xml new file mode 100644 index 000000000..470494168 --- /dev/null +++ b/packages/databinding/steps/02/webapp/view/App.view.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/databinding/steps/03/README.md b/packages/databinding/steps/03/README.md new file mode 100644 index 000000000..66644a6b2 --- /dev/null +++ b/packages/databinding/steps/03/README.md @@ -0,0 +1,53 @@ + +# Step 3: Create Property Binding + +Although there is no visible difference, the text on the screen is now derived from model data. + +## Preview + +![The browser shows the text "Hi, my name is Harry Hawk"](assets/Tutorial_Data_Binding_Step_1_6d391d5.png "The browser shows the text "Hi, my name is Harry Hawk"") + +You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io/tutorials/databinding/build/03/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/databinding/databinding-step-03.zip) (TS)[📥 Download step 3](https://ui5.github.io/tutorials/databinding/databinding-step-03-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Assign the `text` property of the `sap.m.Text` control to the value `{/greetingText}`. The curly brackets enclosing a binding path \(binding syntax\) are automatically interpreted as a binding. These binding instances are called property bindings. In this scenario, the control's `text` property is bound to the `greetingText` property at the root of the default model. The slash \(`/`\) at the beginning of the binding path signifies an absolute binding path. + +## webapp/view/App.view.xml + +```xml + + + +``` + +*** + +**Next:** [Step 4: Two-Way Data Binding](../04/README.md "In the examples we've looked at so far, we've displayed the value of a model property using a read-only field.") + +**Previous:** [Step 2: Creating a Model](../02/README.md "In this step, we create a model.") + +*** + +**Related Information** + +[Binding Types](https://sdk.openui5.org/topic/91f0d8ab6f4d1014b6dd926db0e91070.html "Depending on the different use cases, you can use different binding types: Propety binding, context binding, and list binding.") + +[Property Binding](https://sdk.openui5.org/topic/91f0652b6f4d1014b6dd926db0e91070.html "With property binding, you can initialize properties of a control automatically and update them based on the data of the model.") diff --git a/packages/databinding/steps/03/assets/Tutorial_Data_Binding_Step_1_6d391d5.png b/packages/databinding/steps/03/assets/Tutorial_Data_Binding_Step_1_6d391d5.png new file mode 100644 index 0000000000000000000000000000000000000000..aaf6180a0c2134cc6675f54337f74e56c1e4373c GIT binary patch literal 6851 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 VchfEWg#G(KQdQDYtd_S7`5&hGant|+ literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/03/package.json b/packages/databinding/steps/03/package.json new file mode 100644 index 000000000..82943f88f --- /dev/null +++ b/packages/databinding/steps/03/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step03", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/03/tsconfig.json b/packages/databinding/steps/03/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/03/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/03/ui5.yaml b/packages/databinding/steps/03/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/03/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/03/webapp/Component.ts b/packages/databinding/steps/03/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/03/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/03/webapp/index-cdn.html b/packages/databinding/steps/03/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/03/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/03/webapp/index.html b/packages/databinding/steps/03/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/03/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/03/webapp/manifest.json b/packages/databinding/steps/03/webapp/manifest.json new file mode 100644 index 000000000..c2727bd66 --- /dev/null +++ b/packages/databinding/steps/03/webapp/manifest.json @@ -0,0 +1,44 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "Data Binding Tutorial", + "description": "A simple app that explains how to use data binding features of OpenUI5" + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/03/webapp/model/data.json b/packages/databinding/steps/03/webapp/model/data.json new file mode 100644 index 000000000..7b62fba1f --- /dev/null +++ b/packages/databinding/steps/03/webapp/model/data.json @@ -0,0 +1,3 @@ +{ + "greetingText": "Hi, my name is Harry Hawk" +} \ No newline at end of file diff --git a/packages/databinding/steps/03/webapp/view/App.view.xml b/packages/databinding/steps/03/webapp/view/App.view.xml new file mode 100644 index 000000000..ac3213a43 --- /dev/null +++ b/packages/databinding/steps/03/webapp/view/App.view.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/databinding/steps/04/README.md b/packages/databinding/steps/04/README.md new file mode 100644 index 000000000..1e420ec1f --- /dev/null +++ b/packages/databinding/steps/04/README.md @@ -0,0 +1,111 @@ + +# Step 4: Two-Way Data Binding + +In the examples we've looked at so far, we've displayed the value of a model property using a read-only field. We'll now change the user interface to display first and last name fields using `sap.m.Input` fields. We're also adding a check box control to enable or disable both input fields. This setup illustrates a feature known as "two-way data binding". As the view now contains more controls, we're also moving the view definition into an XML file. + +## Preview + +### Two input fields and a checkbox to enable or disable them + +![Two input fields and a checkbox to enable or disable them](assets/Tutorial_Data_Binding_Step_4_61d68f1.png "Two input fields and a checkbox to enable or disable them") + +You can view this step live: [🔗 Live Preview of Step 4](https://ui5.github.io/tutorials/databinding/build/04/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/databinding/databinding-step-04.zip) (TS)[📥 Download step 4](https://ui5.github.io/tutorials/databinding/databinding-step-04-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Replace the content of the `App.view.xml` file with the following content: + +## webapp/view/App.view.xml + +```xml + + + + + + +``` + +> :note: +> Requiring `sap/ui/layout/form/ColumnLayout` is needed because we use the `ColumnLayout` as `layout` for the `sap/ui/layout/form/SimpleForm`. +> The `sap/ui/layout/form/SimpleForm` requires the configured layout, in case it's not done by the consumer but this may cause an additional rendering cycle if rendering starts before the layout finished loading. + +Replace the content of the `data.json` file in the `model` folder with the following content: + +## webapp/model/data.json + +```json +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "panelHeaderText": "Data Binding Basics" +} +``` + +After these changes, refresh the application preview and select or deselect the checkbox. You'll notice that the input fields are automatically enabled or disabled in response to the state of the checkbox. + +![Two input fields and a checked checkbox to enable or disable them](assets/Tutorial_Data_Binding_Step_4_61d68f1.png "Two input fields and a checked checkbox to enable or disable them")![Two input fields and a unchecked checkbox to enable or disable them](assets/Tutorial_Data_Binding_Step_4_2_6222561.png "Two input fields and a unchecked checkbox to enable or disable them") + +It is clear that we haven't written any code to transfer data between the user interface and the model, yet the `Input` controls are enabled or disabled according to the state of the checkbox. This behavior results from the fact that OData models and JSON models implement two-way data binding. For JSON models, two-way binding is the default behavior. For more information, see [Binding Modes](https://sdk.openui5.org/topic/68b9644a253741e8a4b9e4279a35c247.html#loio68b9644a253741e8a4b9e4279a35c247/section_BindingModes). + +Two things are happening here: + +- Data binding allows the property of a control to derive its value from any suitable property in a model. + +- OpenUI5 automatically handles the transport of data from the model to the controls and back from the controls to the model. This is called two-way binding. + +*** + +**Next:** [Step 5: One-Way Data Binding](../05/README.md "Unlike the two-way binding behavior we've seen, one-way data binding is also possible.") + +**Previous:** [Step 3: Create Property Binding](../03/README.md "Although there is no visible difference, the text on the screen is now derived from model data.") + +*** + +**Related Information** + +[Data Binding](https://sdk.openui5.org/topic/68b9644a253741e8a4b9e4279a35c247.html "You use data binding to bind UI elements to data sources to keep the data in sync and allow data editing on the UI.") diff --git a/packages/databinding/steps/04/assets/Tutorial_Data_Binding_Step_4_2_6222561.png b/packages/databinding/steps/04/assets/Tutorial_Data_Binding_Step_4_2_6222561.png new file mode 100644 index 0000000000000000000000000000000000000000..a2c0a67a9e0c257d60b7990c7340d249535dbadf GIT binary patch literal 16811 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

NL1Q`jaS!l4F+ni5l+AHeiasRf+TC(AGz=8{ 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 b@CL)5T=_Ts3qb`K@FyuMCsHY75cvN9rI^2% literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/04/assets/Tutorial_Data_Binding_Step_4_61d68f1.png b/packages/databinding/steps/04/assets/Tutorial_Data_Binding_Step_4_61d68f1.png new file mode 100644 index 0000000000000000000000000000000000000000..d701c3131bb66384a0ecb371013b069723d69c5b GIT binary patch literal 17241 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 literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/04/package.json b/packages/databinding/steps/04/package.json new file mode 100644 index 000000000..aabaff373 --- /dev/null +++ b/packages/databinding/steps/04/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step04", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/04/tsconfig.json b/packages/databinding/steps/04/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/04/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/04/ui5.yaml b/packages/databinding/steps/04/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/04/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/04/webapp/Component.ts b/packages/databinding/steps/04/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/04/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/04/webapp/index-cdn.html b/packages/databinding/steps/04/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/04/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +

+ + diff --git a/packages/databinding/steps/04/webapp/index.html b/packages/databinding/steps/04/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/04/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/04/webapp/manifest.json b/packages/databinding/steps/04/webapp/manifest.json new file mode 100644 index 000000000..5757104d5 --- /dev/null +++ b/packages/databinding/steps/04/webapp/manifest.json @@ -0,0 +1,45 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "Data Binding Tutorial", + "description": "A simple app that explains how to use data binding features of OpenUI5" + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/04/webapp/model/data.json b/packages/databinding/steps/04/webapp/model/data.json new file mode 100644 index 000000000..5a0f07035 --- /dev/null +++ b/packages/databinding/steps/04/webapp/model/data.json @@ -0,0 +1,6 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "panelHeaderText": "Data Binding Basics" +} \ No newline at end of file diff --git a/packages/databinding/steps/04/webapp/view/App.view.xml b/packages/databinding/steps/04/webapp/view/App.view.xml new file mode 100644 index 000000000..d90a308cf --- /dev/null +++ b/packages/databinding/steps/04/webapp/view/App.view.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/packages/databinding/steps/05/README.md b/packages/databinding/steps/05/README.md new file mode 100644 index 000000000..f2e0edab0 --- /dev/null +++ b/packages/databinding/steps/05/README.md @@ -0,0 +1,95 @@ + +# Step 5: One-Way Data Binding + +Unlike the two-way binding behavior we've seen, one-way data binding is also possible. In this case, data travels in one direction only: from the model, through the binding instance, to the consumer \(usually the property of a control\), but never in the other direction. Let's modify the previous example to use one-way data binding. This shows how you can switch off the flow of data from the user interface back to the model if needed. + +## Preview + +### Two input fields and a checkbox + +![Two input fields and a checkbox](assets/Tutorial_Data_Binding_Step_4_61d68f1.png "Two input fields and a checkbox") + +You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io/tutorials/databinding/build/05/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 5](https://ui5.github.io/tutorials/databinding/databinding-step-05.zip) (TS)[📥 Download step 5](https://ui5.github.io/tutorials/databinding/databinding-step-05-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Insert the highlighted code into the `Component.ts/.js` file. The `init` function calls the init function of its parent, retrieves the default model instance bound to the component, and sets the default binding mode to one-way data binding. + +## `webapp/Component.ts/.js` + +```ts +// webapp/Component.ts +import UIComponent from "sap/ui/core/UIComponent"; +import BindingMode from "sap/ui/model/BindingMode"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; + + public init(): void { + super.init(); + this.getModel().setDefaultBindingMode(BindingMode.OneWay); + } +} +``` + +```js +// webapp/Component.js +sap.ui.define(["sap/ui/core/UIComponent", "sap/ui/model/BindingMode"], function (UIComponent, BindingMode) { + "use strict"; + + return UIComponent.extend("ui5.tutorial.databinding.Component", { + metadata: { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }, + init: function () { + UIComponent.prototype.init.call(this); + this.getModel().setDefaultBindingMode(BindingMode.OneWay); + } + }); +}); +``` + +Now, regardless of the state the checkbox is in, the input fields remain open for input, because one-way data binding ensures that data flows only from the model to the UI, but never in the other direction. + +The binding mode \(one-way or two-way\) is set on the model itself. Therefore, unless you specifically alter it, a binding instance will always be created using the model's default binding mode. + +If you wish to alter the binding mode, you've got two options: + +- Alter the model's default binding mode. This is the approach we used above. + +- Specify the data binding mode for a specific binding instance by using the `oBindingInfo.mode` parameter. This change only applies to this data binding instance. Any other binding instances will continue to use the model's default binding mode.For more information, see [API Reference: `sap.ui.base.ManagedObject.bindProperty`](https://sdk.openui5.org/#/api/sap.ui.base.ManagedObject/methods/bindProperty). + +> :note: +> There are two important points to understand about alterations to a model object's data binding mode: +> +> - If you alter the default binding mode of a model \(as in the example above\), **all** binding instances created after that point in time will use the altered binding mode, unless you explicitly say otherwise. +> +> - Altering a model's default binding mode doesn't affect already existing binding instances. + +*** + +**Next:** [Step 6: Resource Models](../06/README.md "Business applications often require language-specific \(translatable\) text used as labels and descriptions on the user interface.") + +**Previous:** [Step 4: Two-Way Data Binding](../04/README.md "In the examples we've looked at so far, we've displayed the value of a model property using a read-only field.") diff --git a/packages/databinding/steps/05/assets/Tutorial_Data_Binding_Step_4_61d68f1.png b/packages/databinding/steps/05/assets/Tutorial_Data_Binding_Step_4_61d68f1.png new file mode 100644 index 0000000000000000000000000000000000000000..d701c3131bb66384a0ecb371013b069723d69c5b GIT binary patch literal 17241 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 literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/05/package.json b/packages/databinding/steps/05/package.json new file mode 100644 index 000000000..232aa477f --- /dev/null +++ b/packages/databinding/steps/05/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step05", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/05/tsconfig.json b/packages/databinding/steps/05/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/05/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/05/ui5.yaml b/packages/databinding/steps/05/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/05/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/05/webapp/Component.ts b/packages/databinding/steps/05/webapp/Component.ts new file mode 100644 index 000000000..035c1884d --- /dev/null +++ b/packages/databinding/steps/05/webapp/Component.ts @@ -0,0 +1,17 @@ +import UIComponent from "sap/ui/core/UIComponent"; +import BindingMode from "sap/ui/model/BindingMode"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; + + public init(): void { + super.init(); + this.getModel().setDefaultBindingMode(BindingMode.OneWay); + } +} diff --git a/packages/databinding/steps/05/webapp/index-cdn.html b/packages/databinding/steps/05/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/05/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/05/webapp/index.html b/packages/databinding/steps/05/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/05/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/05/webapp/manifest.json b/packages/databinding/steps/05/webapp/manifest.json new file mode 100644 index 000000000..5757104d5 --- /dev/null +++ b/packages/databinding/steps/05/webapp/manifest.json @@ -0,0 +1,45 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "Data Binding Tutorial", + "description": "A simple app that explains how to use data binding features of OpenUI5" + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/05/webapp/model/data.json b/packages/databinding/steps/05/webapp/model/data.json new file mode 100644 index 000000000..5a0f07035 --- /dev/null +++ b/packages/databinding/steps/05/webapp/model/data.json @@ -0,0 +1,6 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "panelHeaderText": "Data Binding Basics" +} \ No newline at end of file diff --git a/packages/databinding/steps/05/webapp/view/App.view.xml b/packages/databinding/steps/05/webapp/view/App.view.xml new file mode 100644 index 000000000..d90a308cf --- /dev/null +++ b/packages/databinding/steps/05/webapp/view/App.view.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/packages/databinding/steps/06/README.md b/packages/databinding/steps/06/README.md new file mode 100644 index 000000000..17515fa9a --- /dev/null +++ b/packages/databinding/steps/06/README.md @@ -0,0 +1,210 @@ + +# Step 6: Resource Models + +Business applications often require language-specific \(translatable\) text used as labels and descriptions on the user interface. + +The example we used at the start of this tutorial was quite simplistic as we stored language-specific text directly in a JSON model object. Generally speaking, unless language-specific text comes directly from a back-end system, it's not considered good programming practice to put translatable texts directly into a model. So, let's fix this by placing all translatable texts \(such as field labels\) into a resource bundle. + +## Preview + +### The texts are now derived from a resource model \(No visual change to last step\) + +![The texts are now derived from a resource model (No visual change to last step)](assets/Tutorial_Data_Binding_Step_4_61d68f1.png "The texts are now derived from a resource model (No visual change to last step)") + +You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io/tutorials/databinding/build/06/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/databinding/databinding-step-06.zip) (TS)[📥 Download step 6](https://ui5.github.io/tutorials/databinding/databinding-step-06-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── i18n/ +│ └── i18n.properties +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Create a new entry in the `manifest.json` file under the `models` entry as shown in the coding below. The resource model is set to the component using the model name `i18n`. The data comes from the `i18n.properties` file as specified in the `bundleName` entry in the settings. Since we're creating a resource model, the file name is assumed to have the extension `.properties`; this does not need to be stated explicitly. + +Also add the `i18n` property to the `sap.app` section and modify the `title` and `description` property to use the corresponding texts from the `i18n.properties` as shown below. + +## webapp/manifest.json + +```json +... + "sap.app": { + ... + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + }, + ... + }, + ... + "sap.ui5": { + ... + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + } + }, + ... + } +... +``` + +> :note: +> The configured `supportedLocales` represent the following i18n files present \(see Step 7\): +> +> - `""` - `i18n/i18n.properties` +> - `"de"` - `i18n/i18n_de.properties` +> +> The configured `fallbackLocale` should represent one of these files. According to the fallback chain, the root bundle \(`""`\) is the last fallback. Configuring it explicitly avoids side effects when additional resource files are added. For more information, see [Supported Locales and Fallback Chain](https://sdk.openui5.org/topic/ec753bc539d748f689e3ac814e129563). + +Update the `i18n.properties` and add the code shown below. + +## webapp/i18n/i18n.properties \(New\) + +```properties +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled + +# Screen titles +panelHeaderText=Data Binding Basics +``` + +The `panelHeaderText` property has been moved from the JSON model into the `i18n` resource bundle. Also, the field labels are no longer hard-coded in the XML view. This is because all of these text fields need to be translated. + +Language-specific text stored in resource models obeys the Java convention for internationalization \(i18n\). + +Modify the data binding for the panel header and the labels in `App.view.xml` to include the model name. Note that a "greater than" character separates the model name and the property name. Also, i18n property names **must not** start with a slash character. + +## webapp/view/App.view.xml + +```xml + + + + + + +``` + +Remove the line `panelHeaderText : "Data Binding Basics"` from the model data in the `data.json` file. This text has now been moved to the resource model. + +## webapp/model/data.json + +```json +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true +} +``` + +Remove the `init` function and the import of `sap/ui/model/BindingMode` from `Component.ts/.js` as we do not want to set the one-way binding mode anymore. + +## `webapp/Component.ts/.js` + +```ts +// webapp/Component.ts +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} +``` + +```js +// webapp/Component.js +sap.ui.define(["sap/ui/core/UIComponent"], function (UIComponent) { + "use strict"; + + return UIComponent.extend("ui5.tutorial.databinding.Component", { + metadata: { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + } + }); +}); +``` + +You can use multiple model instances by using different model names. The model name can be set as the second parameter when using the `setModel(oResourceModel,"i18n")` method. The model is then propagated under this name to all aggregated child controls \(and their children, and so on …\). All these controls have access to this model under the name `i18n` as well as to the `JSONModel` \(default model, which has no name\). + +*** + +**Next:** [Step 7: \(Optional\) Resource Bundles and Multiple Languages](../07/README.md "Resource bundles exist to enable an app to run in multiple languages without the need to change any code.") + +**Previous:** [Step 5: One-Way Data Binding](../05/README.md "Unlike the two-way binding behavior we've seen, one-way data binding is also possible.") + +*** + +**Related Information** + +[Resource Model](https://sdk.openui5.org/topic/91f122a36f4d1014b6dd926db0e91070.html "The resource model is used as a wrapper for resource bundles. In data binding you use the resource model instance, for example, to bind texts of a control to language-dependent resource bundle properties.") diff --git a/packages/databinding/steps/06/assets/Tutorial_Data_Binding_Step_4_61d68f1.png b/packages/databinding/steps/06/assets/Tutorial_Data_Binding_Step_4_61d68f1.png new file mode 100644 index 0000000000000000000000000000000000000000..d701c3131bb66384a0ecb371013b069723d69c5b GIT binary patch literal 17241 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 literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/06/package.json b/packages/databinding/steps/06/package.json new file mode 100644 index 000000000..2d6f9f472 --- /dev/null +++ b/packages/databinding/steps/06/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step06", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/06/tsconfig.json b/packages/databinding/steps/06/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/06/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/06/ui5.yaml b/packages/databinding/steps/06/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/06/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/06/webapp/Component.ts b/packages/databinding/steps/06/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/06/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/06/webapp/i18n/i18n.properties b/packages/databinding/steps/06/webapp/i18n/i18n.properties new file mode 100644 index 000000000..761435352 --- /dev/null +++ b/packages/databinding/steps/06/webapp/i18n/i18n.properties @@ -0,0 +1,11 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled + +# Screen titles +panelHeaderText=Data Binding Basics diff --git a/packages/databinding/steps/06/webapp/index-cdn.html b/packages/databinding/steps/06/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/06/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/06/webapp/index.html b/packages/databinding/steps/06/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/06/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/06/webapp/manifest.json b/packages/databinding/steps/06/webapp/manifest.json new file mode 100644 index 000000000..2738faec9 --- /dev/null +++ b/packages/databinding/steps/06/webapp/manifest.json @@ -0,0 +1,65 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/06/webapp/model/data.json b/packages/databinding/steps/06/webapp/model/data.json new file mode 100644 index 000000000..381adf692 --- /dev/null +++ b/packages/databinding/steps/06/webapp/model/data.json @@ -0,0 +1,5 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true +} \ No newline at end of file diff --git a/packages/databinding/steps/06/webapp/view/App.view.xml b/packages/databinding/steps/06/webapp/view/App.view.xml new file mode 100644 index 000000000..8a003efa7 --- /dev/null +++ b/packages/databinding/steps/06/webapp/view/App.view.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/packages/databinding/steps/07/README.md b/packages/databinding/steps/07/README.md new file mode 100644 index 000000000..2ccaf0b56 --- /dev/null +++ b/packages/databinding/steps/07/README.md @@ -0,0 +1,65 @@ + +# Step 7: \(Optional\) Resource Bundles and Multiple Languages + +Resource bundles exist to enable an app to run in multiple languages without the need to change any code. To demonstrate this feature, let's create a German version of the app – in fact, all we need to do is create a German version of the resource bundle file. In our code, we activate the German locale for the ResourceModel. + +## Preview + +### The texts are now adapted for the German locale + +![The texts are now adapted for the German locale](assets/Tutorial_Data_Binding_Step_7_d96cdf9.png "The texts are now adapted for the German locale") + +You can view this step live: [🔗 Live Preview of Step 7](https://ui5.github.io/tutorials/databinding/build/07/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/databinding/databinding-step-07.zip) (TS)[📥 Download step 7](https://ui5.github.io/tutorials/databinding/databinding-step-07-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +## webapp/i18n/i18n\_de.properties \(New\) + +In the `i18n` folder, duplicate the `i18n.properties` file and rename its copy to `i18n`**`_de`**`.properties`. Replace the English text with the German text provided below. The suffix `de` represents the locale for the German language. Since the `de` locale is already set in the `supportedLocales` configuration of the `manifest.json`, it will be taken into account. + +```properties +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert + +# Screen titles +panelHeaderText=Data Binding Grundlagen +``` + +To check the result, append the `sap-language=DE` URL parameter to the URL in your browser, for example `http://localhost:port/index.html?sap-language=DE`. Once you remove this parameter, your app reverts to your browser's default language. + +*** + +**Next:** [Step 8: Binding Paths: Accessing Properties in Hierarchically Structured Models](../08/README.md "Let's explore how to reference fields in a hierarchically structured model object.") + +**Previous:** [Step 6: Resource Models](../06/README.md "Business applications often require language-specific (translatable) text used as labels and descriptions on the user interface.") + +*** + +**Related Information** + +[Localization](https://sdk.openui5.org/topic/91f217c46f4d1014b6dd926db0e91070.html "The framework concepts for text localization in OpenUI5 are aligned with the general concepts of the Java platform.") + diff --git a/packages/databinding/steps/07/assets/Tutorial_Data_Binding_Step_7_d96cdf9.png b/packages/databinding/steps/07/assets/Tutorial_Data_Binding_Step_7_d96cdf9.png new file mode 100644 index 0000000000000000000000000000000000000000..f46f49b169ee6a01a0bb9ce79ecedd73a4283c48 GIT binary patch literal 18359 zcmZ^LWmp{BwkWMUf@^TMAc5fCxVuAecL?qduh{3keeRF@ ze)QK>%WL+kDPxY+p$c-6?~w43prD}MNqrVmf`Wo(20ok!a6rk<&(UMx3)(?RQUt1O zlyD!o5i?SgGM1Hvq6MxIprC`zpkQB{03Uqd0|f<>4GjeYd_%vMWrO}-Ei`jB%)i&1 zuMKV89`~T2grTIwK7DhAKFWaC!|J*2ZM!7a5J46QklSma>yfC>QL7MW|26kadwpGk z>fsdz7R^II2=o11o^umB?t!r1@-)V^-kbJ5Ab|W(c9eQxWMz4Ne)Z7XV>G3@T8^g3 zY(6C=<@VRc;UO#z4i1F7seJ(PT;AJM5hN{)sHG1oo1dT0CN#nse;<~ZR*amUk%4@x zf56(rrx3TqD=!I+~4re|ctWa4tdV3-nLcn`E@ z6cdJ}*Q`0PIXky?X&Y~B)Iy|&rqH7SjmpXKoH4-dn7Nt#GbuL`^lnKBy;*TcQd47O z)TG&h9MIh!0m7;$e=sC2iGP)@08G?@z-?k;lJX4^-lCpU^>wTdDd=D?Gw|4~@kfe8g)70ZL=Va~wRh2hH9SVT6lHM(Ym#PO6nNHpWIFVPg7LvX%tj zQPfYpbTRIFDC7uh(|QUEr|aRAc@lo#7r}GE;&lxAR<;)X!vBywe}c0r+Yu1mCNWk< zbh^*S)G;)bTR5#d<2=D)S0IED8~cUe_Zzru`+TG%WfLjuX73AUPyRDWo-yCP-UyMQ zg2>aO_>^0NGo)|t3M06(JeJbKDdyoyn=TPC(|n19-0*X^NTYcP3<|c^AF;nooMJ;G z7b+pPZ231k&0X2ED$`sl_V!Zf4Gh*QxzvP6>V7NIsvLKkv$2&}aJ~61zxmCOJl$xr z_pl^=p8HlUAvzl8?=({9GFecLe6*Z#HHN)qTK!4yd@`=(Tvf{)tmW(qO!Cu1R-q0D z)R+*9%Jp`z6G8p@ZPAn(Q5|CFZg*Gf5yjKpT*T0VyI4YsD|h^gBu+5(qK_0$#igU403KS_uVQ48`Jg_qQ1=(M4#CUkoP$5_ zO60TWVCe+}FsS6kNm*$nqIe0+_V*hk$C@@K6An%(*Kmnl|2V>?e6Y1kiYeQto4jk3u5{yn$~%x zZyiW+={I&BOJO7O*tbFHWpSI>j;Uz!^QYn`dZ%O2p#(Ao%S#%ibbnqtIt+m(r*_es zWk1*kJrqL0FrEt>gT>L7IXawPgR!=#{Z&uH5|l-KlEk^m4zfO5PG0P3P;rP}K)Z8H z#(Tx35693@=}QsnJCac6{j~YkVd9z9r4_A$K82~Y9znhFFo}^xCS8s4;hKasOEirm z5t$$RfYVCU+?*Hx&8%p6!N(oG2a=|nBd)iPo?h=4n(h3ke3W^fAC4gd5UXBRM?3U& zZoLIjx^?wtbGQ9LiVZy*-k3jDUa z4A;eJV*>+4uGc~*VEJz7sj|@{&Gjpb61Ap}s_jvP zd>^^|KY#nZ7mYvt8$8_@Nf|L-uF7TCfGX33u@&?c@!j7U)`#^aheM|GVp7m@wvJVv ziJ{(Zrfpt+K3U!;ZCkm<=>sk@T3)zX^KWbq8r z+;Aq|*PB9MSqQ1P=K7GC^=nkDL-#&&mvMHv+NG`XRJHTq2&j!ywILjOB;#nhVdD5(Um!Y%mxKX?$o9*^6EsObSRw#m_+YM{k zkoEw3^3F!^2A8+aB9Lk80Wr&qj4GwA==fPu_x)M1KSgz0AD$h}%frCzhE5p*%+BskelfiEo&w7;{Opq{lFk(ip#{17HS8HO-(hjMfxN6%0&6y@c`rxR^IG)QZy zATbBFd~HfhzC6=(1N&i~;;i1k&lyS8+meNGs5f5P-z@hQF5ZzHR}=HK6F_n`*>4KY zbjP9-AD4LB1g+cpk~$wm%5goARH*{{H!){ce*?apOk%^T>%{x>Myw*P%Zs)|y3dP4 zXk{L5QnS^qaP!mT%l4=$m(0>}(#kV}WONqmhN>#}_1%Bg{>HI~3*jcf1HC!gKi;y*ud&@i}56>GLugm<-ijg@B54T**e*w)OW#! z{64FR-pE3>YSwG&LHgAa9j-=?+zv2?fiF*vq^-~Ta2mCy{si#a-%(qW$+>RH9Y)XL zF19_&Q_>dP-p#yu`7CUl+aLP!xTz8NI4SsJOxElp|MObQ4%bbc)&?ED#TL2uNww4( z;-r+^t@Q@+{r#=pMV+?mU0aqfmEtn*r=6%CjPJ5m1PgC77VCq8)eMB>vchTDeBv;M zmJy5(5@kmaSsacoj1^yt#kBYT*-jI^qtqk#h2==YH%h0 z)j3~pCnEOH&Oq>Smo1t=Z)L1_J8}H&G*SKAQJw-%p)O7X3h7+5sQYYI*Eak=EWgIAlvs27kPvM>;Ad3s_4xuB0{^vsh1+>17#FNlK#=(cyi$;~;UV zQZnyLrQL3qG?DH(!6?j#dwf~7w$WGe25u*oO8p2y^-l;vBbNUoK za@ToPgBj&(VV1A>Cr;S>ot@vPG*pK58w11Hn*#=_UQ-N%#vC7#sdH5^h+uqpRsr8~ zfMWeSE~>xDR64AczvWlGp=HIW)rebt@*am1!|f4V`*LmHHaO6$lrNHhy3zhDE5qgw zR$*KV4%QNLRK8#3{<~B!XTQ!9$_&v@@BXQF5{T4rMzXBZh?UiR1rq57xrxG_HWgJz z)K)WXyf+`p@ET-lBa#5O-Ey_-SKqKZNea<{>+@Ffi` ze9cg_VylHiq2a*c%>=~$5qFpeT?!mq4oJF3V&an&@>xFB%R7yZ2s)j=BQgHH;HHGu zi}SC0v%lz!rJjham2gjk8CsW!On1`v)qa#|8qaP0#?N&!Oi1j-*o99Y8YfyCE z+|!r7pxcdEap8Z=mt?+v(cwI-3)RFYJt$eOm{< zrdqk=da-t=rK`0C*mXVn8m|Xb@S0U_WH_&`#HSMiw>TbE*NB0lB3$a-dl`9wfV?fk zrHfNGi7)#o7eQ9YiniJeI8;9_G-@5F#(Pwm?(way&%-zyrq0Q?dxqoET=W~027o|ra_;U#xP>2> zi%DD)F&?8i44@DTnp?#cr*>=B`_;IPU+{q`0d^8rhWs@46n;*t=ar=0I{o1 zb=$Z0&o7fQm3{4uH3ysEMz)~&MbgXX|Bc;koTlnZxdpEN0({YLB{IzcIf2E~%(dn2bs7{Q-HN;^z-Z<;`U zT((@NuFmlAG9I7mACgnO9hB348$-zi+Po+c%z7ixP#T6fw1n^b(LYL~A%7GB^VIp) z)hE?MuB~L>M%sO9v~B+Mqq?->O$6!$DP6^5%kOp19BwS}#+>f&mVuq^KJ=W@q2RzO z-jzp77=g!37U5aXWA3C~rnjxp1TU#}XLFK{K2|fNW3rlfwEo;K9q3)w-A~bY`a60^ zV=*hsyg5aX(5)Ei#Psm=a>K`{0Q4_1`NVZ;XM&o930GC+bH)$80&j*6XPkZO;Py^r zNEJvK^F}BtY&vd)#?UdIO3*4sk2KPdH?$E)*?j0BIt=_A$GSUP8Ehv^p6qAA{B{@* z?v|hmvj;g(L9;L(DO^2c&tSjlvvS!VF%`1cXP5lz#TR)wX%FTCqxW4`G)Vpa^W?MN#$x2Ol_W=^#}w|hQ| zfv%a_o+|&)aD{-v7z&JDuo!sjx>QV!nA;9ThR;cITM8>skWk8aUTNs)_S+^o+xP8Rs4r|tUq<#U)gT9LrX-gxSk+a6E`hBgSHjV zY*Nog4CfxV20m|sCqaJ~i;E!!+QZc2>$y~4^fH2^MrpbjkQtj-&l$};EQ7OLj_0YU zZix-_ndw$V#r#~Jeqz=o&ERSa2?lj{geDwthG;IVKt>{>Ap?Q!bvo7`U3&Wk0}yr3a#L)=%TFQnPw5rejvr*epZ^Guk_hL&;Dlh4jzf-o4WsQZI)- zD{O8?UE-&-Qs!?)Tz9OjmKsGR&|Si8Gtm!5lkV=7X)v&=yt4Il>H~EU_zPKx?Z$ z5+$qqR;9$Papy~pI%RI0q#>+p^jp0xa@gJOR5c>W8aI~@Ns6cfkuQVP<^p)rI#u?O z=}BjhBfq}Lc1GP=on)ycdHZs=(W+CoZz~9{&(9AUuvS_2&(Zo5+q)(r*KHY@4jT*=iVY>YT~~+(V~pgMkh6MV(AN-L6;oBwJnf2 zU;btDy|b%Rt7yU{+BYjJdf-K${}7_4?9z|eF$Y_UXgxI9>f}(9&`gtg`9LmsaX=ET zs2Y?lxx%kjLQhrMjFPfMyz;U9WN{G|jL~B?5TCc;kHiGmDE%nuVtf?DaBfp9#h4e~ z$^Dk($Zcn0Lc$gA*GA>p&&qeC%u!u!cVmJDq9|X~*X%V3PkIU9`c3~bn-#?|^L(5c z*4+UgEVtzH%LtDdyMMU7`6-plc(rylerAgzvh2%X&<2IlNE0A%6u!2Tjq@Mgr5r@)f`bPk!aD9H@Be4voBR7Rs5 zcz}0bO`fb5?3JTOcUb6WOb+OXqrEE64AKTZ$*d!A5A-C-9_-m8GtDs%B>&o*`)d%K zyd@Ns9O?nqv~FWUiN2U1T3JqhJHStagv1bgZKZ)r7k)5Bp-n?Yz$BMRugzD(;xfW< zP@la1x1Ba&Krjd#QVGOE zGZ`iN%6dH@-lSWC#l(aSm z%@mc+Qw2t#`g~1KWjEIobIz!VDZ-K!@!t;8oX_K;*J?&b9qc!kiUa zXa_>Bx76+^gl4c;cT~jjRET9@okOSAtRvi&CAz(;6C;J^P8Gd<)85GRT4gleM1)W^6 zm2^?RS!cj9-VB}$jc?$={jqIUQsvS;ys+QeOQvuZ&;g7dNlG|Yw+2AsvtbG205IL) zI;~CvK;}OxScaBJe|^R16&7H()HjCY!3;+d2haat@Z{Op$(iu~f!!nhcr0hlx&Vw` z0Y_=zEI z$3Qvh^@^6lcdDT6c;ACjui#-By2))b4_PyYUnYLUb?CMVAYOvN@6PF*n!pGH-Kwv! zKQ*)@MK)-sP~g{*3XuI^U>1OX%^hfg%>8(*SIveJgd3=`Lrb=wF%_Vs+wBldNsBkH zGwQSu<2`^T2@hX|p{b$!D6(0Y`;Pdm8qle4VE+Wt-~93g0f`0~VeQZVEe6s3Tg>p^ z*6bm=$MVo`0O=(G())L8e3<{W^|$uHCz@=ic;8KQ1!#w*)~Q4~KYMxMHziA1AS57( z!3ZQV{+*FMruN@r*<9OyTQgMH`yf$6QLI5=+2M8|UcVRsnPmbZ|Mw{ik@x>C*3kQJ z@tgm)hC3VjUslqt#8RQX9?ar0f!|F*jy=bc<{V(qQFZlMPKx2wL%R?*Uigkwj|Q3k z+>&1K*_$)5Y}s9hi9FQw$!5^z)@y*5n};XkriBssZL=TID&l=>&9EaDI6#%gQ;dd& z#yk6w0%(%9huwINHmii0#K${La}crgsIX5WVEas`sY$jUKjN$DDH)+F&qDi$hebR^ zV%3=_INkv`>GxHvZ;zXPhTuSI(K^>JTrf9Z!*5GZ-9$Dq#1plvSCX; zx7(}A)YmcmNXQ7RO>@~k55qYy+PwBMoi7wLS1I;C@lmH1sgj2Mjn}&@fSDuv*Wf(8 z?R1D_XoM3F>?cmh8`Tg#n`2YYxi{5-MJ*>Oj@gz`2{nR0)6-R@UE405%xc1NO~h|p zmv*&FOQz7m7rYUp*fn10&G02vP#4y&{4P^va^<@u%h63fX>txOXEwZZ07<9JiBE6~ zeAK!wS(#)L_Q&Kl`r(=+f~N+1jMh|EQ~;On<}RwPuDPQ!J1DPWO0>LVVxuF#Ms zdq{KVi22h!JLd7d zW+3R8T||DH_tjBb`@+2q(~ljokWDS?Yx>&uzJ4+5O}z>9kmdK(DT?Q-sq%0%h56O) zJkMrGOB{md@yf$rSl#YQ{b?FBPMVi#xBOeBh?>j=G5erL=@aM)^L?Y~_4eu6)G5|MpJ>2vNYGP~ za~AUb==^{>eJT56ohw6X;GZ5g14-O7gC)z{t;~$6j3tfp*%&gJ2HiAN^(r)Y3R6Tl zWz_at7P9lQY#$(PZaWWqp30J>t$n6EE(1Ejq=B3Ne((oVwJNcwS#IPvI$3d)(LtTG z<>C9-*stPyq!r*NXLg%!s8~G=^q#_6p#7A$c~c{fB8_VHXNUvIv>o@{M`{@j9QhS&~2y4oygFHhVQ4k57elZ|IDM0>$aHvs{Di0Lb5E37;gYdbo@9^ zk|T?sEiEo2_PMc(qlLHq>k>OQmsA`?)Bg^5jiPo}pWvoJw3|RmyYM4R;Uc`Q(S{q| zhJ+G5ZcRN_7`}magy=jH1C54`ql{Oxz?bYymATw2|A~*3_M+_A; zmzVr3RZF}*UWN_olN2b#0-@1uX_52Ds%f}H4xJKj{~EMLM}#FLFM?phsi$O}9cq^# zVP?Oc6+kNFYvef2by%Bv0pdfn=az4I12`qb2d=7^2%VN71|-SD_zD9gR+>TmCrHK# zaj{voRyW>kNc|8zWSHGFL-rWxTi-#R1D1KGmb}llf2yq&l*x>Txi+t%Ni(f9*;#3@ zSJ06SPG4^KsSU2|$m538=6W5`=%UnE;Ul1Z3VfLh$JpOuqtM$5QIU;>DY&OBv7pFR zC}65uFMTBY+3E&|JDhYc3T$Z~V4>JutHy&uWxsW4UwxMnap7HmI8EVftmjMI+ zxh3PTOj?;9`)bA04qxh&I667vf*nV8jn{6(p!CiqUE|M1N)xL}4p2Iy6Y75zNd^Bn zKK{W=nWcmAJ8vf4osXS|Fz((uY66o6K>cFeb!hZtf-#Xoj4hfky?^Vd&pWJyjZ5Ob zbND`;F&I_C0-$hY-y+x+VA#mZ6aJW2{m~-EflEe1#d^vql|g zo>d*3BzH8?BGb2IwaajeoItDM8y(HHGpy;mXoDAJ&?o;vJ9Df=r}VeFO0lu**JUS| zvY@ss8|&%y2UNZ3)GJ(9E1_?{zp=P9c)z+%hKP{FKpsDSe*V$i?s+P8SI_kMs}V$! z%_sRbG4Yh)k=aE8n=iNTqxoHhbX@Q4sz*Jt9#H{^20NSqnT3S~*Krx@@%DmAt;Vro zSZ2p>ZjSqH!#cj_!_>Ftmz`qB!F8I$&E4ojmfl1x7No!@Ysu&z4IfLjHja&ZrjSi3 zpBB2nVX>9s?H&#Jkq(ia5pIONBts!=hg9Bxmy#dW_xJrwbi6T0e9qoUL=yV()_z~x zJfdLS^=`fzbbxI4b|Abh1lL7}aEJWLaz!vWX(CwB(E-RJ#9w{xl<8~qvlDRKD3Oq# zHG*=uw8DQwLsP68G{OVFiajE?s z{q@LlArD{f*S9w&_>keCuGnVE9RW3rxN zi7WGhzna0QJwrB+I8E1wY*$RW4Jv=bJ4U+TcMmmufR|sv9F}k;a2P%5Bph``;=(wx z1IMpooYvzpCCTpZ<-XRB3v7B;#yqR;STk&ERwIsj6a9)d_p9Hh=jX%AL1k2dL$fz0 zuCr?daBCT;f;}}U4iUa1seGtbB5Ao+#fc4z`>)R@9qhX~Ch;*(c!i5DtV)@B@cMU~9~}YBFmU-(UX3^u3ayCop(Y>AXttR! znXp?!13|rN)5T)~p*b++Sm8y11E@pPdQFd4n3n)~1GOv0pzrMO;z?-juBHFOo6=uH zuWx`l2!*Ko*XI-$$xIA7k8h6etX|A#3awXcFV{_U*J}{)-dJt8#P#qx#LcP7)d|jy zyugt!USD6GJ49P&piNdl7ktTz0*kX0f< zXf^zykn~Q?kCnC`?emVOfS@(~jP+bVUte25Lh-Er<0fiuE95(2KKp@cS;XL-J%m()OYE%;s4i?q<|9AU8O-Glvg(u``|>liy;KRoYt@-a1Z zi_Youer@?}`@AE_irJzjZL3aTQ#2VFvQHuH;Ae>vVRh~qD>~=p!bU}PS^P_BEI}7x zhI2P0U_jDyEU>P|?xK;|?(=+gX+xpM0XhpE;C7-La1O^{=wyu} zvX&p#47ieW~S`>q5CGl%cJ@)*8>q}M>OgfEIe zkqMI-fv7qA5Q7>c8a?63dXfyN%>DpG##mzFNq{k1dk;&G1+$sUqt5D+^r|zZqoA@F z*K1XvI!fd9A=KP&DY4Y9huv zfgFiojCvC++?{^Zo8QHUcGdo|#N_;GVz5!+bmJ{1(MRf%>amz~{~F(QV*L1_>iY#1 zYa!3?3&*zZ?GZy`AjO;EpCvWwfIU!)@XeR_$j~d12G|qd9z->B%*l(c z0;h&fy8svs=6!|MTg#!cCs*rEu3NLwjf+#O6+FLLp*u7{U}Fzu70A?DB%W*-1a(IY zhXswTuMhH3j|cfMVmMenCsoh}kJ|V;a5XS=jhy1w924WY2@cK>k#|hAIM+AOc8qWF zG>XO-ky}@{&gBv1l8Ux*ZU{F2^0F-3UB)ZPbgRBJofjk0$^+#C2LsWSxw7Qc8n2tq z0zg$YYZbC5MaO#doFIpA0{*(`;?kd<6M!!l6xV!M?KBmHP?&x(`CU{}{-cVHD3%fv z0)a~anKUeWp*dEKHTGK^oTMdQltK3!6WTEOw^<*9UkH9ib+ln)0l=C|7~V_Y}Z2IFpZmK>rXKsH@F?1 zr614x?8$V>NP(TY7_1cuAP1?OJ_1Lmt^;R=gLXuGZoM_NZlARsEtg@ZyfuSrE*;5b zn#xa>5m6%`D=<>b=OJy;%WQ-&56%Na z5-&=i?GWE>*oea^HrDZ4ZHItxC)TU_{Jr-JVz9qNR;**xo_C1QOuw~ZA7iuR1hO67 z81-HqPBn0d{Ds%Q?o?L8t{d_6u{w#O)Dv>M)+Oav2h;O>l=04r;191PgU=D)bWg4) ze;ZdY*0&(r6k*!f(6|xboK;m9vJhY*(WTwJ*%CTcsdooQMu=HZ+C)13OU7c!)k>Z$ zBGd;9bkvMwiQ4G{wQ`Q;9gYH!o83GT&0jW^kM_*lkWF z0{0KRPA2i~>egyR;7xDb4QZhU)DDAo@QJrNwUy-u&@wgy!E_(P>H5v|WkJ{%$oUkYw!jSmrO^J6sKq#od;mep4=eC;>4AF-Ns9iIY zzu)npEkFc=bf;iiS9DCN%a$)tm!lZ1Qt@N*emGgof-<&kyjSyO*=`@)Hv>ZOk(UKD zlwD8`!avw;g6LF*$K*=e(T2(Gj;J}AgxqnC+pc(T@3$Z=Ry_iSX=A4uiAq&lYUZk_ z-4)P4?szsw8N)7gSuCC)*DCxynf;QGws>RHdT`X{kD9hL1lNpIKLGv`ITyhX@=deL%zS`m0ven zOWUKoHdaCv3}wQ^3$#ujGm^i=NU^Xmne4VXEeLw=(6f|hn=9KFU!tL%XE(VpMvDGs zC+)#aEOm9;>Mo+Lq6gln&XPd7%9nmbvI<8!Gbf`k?y8Ei-_Qwb`zaWG4heoRje*m@ zzp<`f!1tt14%e|<_x#2woAd1{<{C1&6K#!<`7GgR>5pbyrx^}tC;OvQP-R(ZA5%Kb+^jTE0;f;WUAZbUWWN0+b}n{<(OFOk#38h{Q(TLC@|?su#a;X|o_? zaycC1qt|uE;>1ryfv+#?o4-c%J}FX9n&*o5*YSl;%vBo^I-R8&D!&nQFWhkAEF*T;YG zNxb^n~QkJwqql+KQp=~YN-9r0ke;toCZ6BM_6C~!_;osQSXm@8GILLwM+)>=5u*8*_)@!Z1g>FaVn!0ix?;AjN7W` zkK1wbodEosJep*hpH~Mzr*~Wz*lnmkJ*8(0Ebqp}zF-q1=@pCk09d?KyA)%MrAH-X z5_1oqB`bWOf><8-0!cp0yHlN_cO2S$G?L;icJg7#m3h5nA6 z*})=(Vk||PLSRLI-5kjN7$NlQ5oMvTlerj~t&Q5J+}7k{G1O2or4d z!*0267(!!~IHTh7V%f$y6r895|^O-sJ|?9#rjR+WJF=B2x>0i-iM}Q4oSr6jSmDD znS8`Rk6YjEWq2v9@$V!{j+N}$rFe_#?s)Apj<6!SAFxm;bs1CacI_aA^;@c() zOX-Qa&A1Wj`8w9-|JF@1?^W-V5kd!JR|JM9{d~#RtdQ@f^|>4K5o(|7>&2nAzMEw@ zT*d5Fw&;=;BSc=r*4BMh69Dp2Qx^Nxeg(*aVBFOm)Uc<>)9bEtrmJ+fVqPz|dWNkY zEos&s9jt5CBWor-UEiy}deCULrqG*95}swn*@z2}VQ;r@-`I7$F0>scTwrei9F1d+9{VG z-?OK+B1o%0Zg+yYPYmTrv`1ACqJ|E~u&u3EqFwbH94eZ z9SxyS36D;tRMhmMfYQ6p#0Iv}**sd=VkXDzvnadG!W<;qk&J9iY<&fuMBLGm-dH{* zz*rU^Yfj>y^x43BY7Fe;`lO^`vK?fhRfm@?HY%&3P)UHhQ&76jWJZ{;ix6qj7uqvs6s(X)THsTw~M&DazVy=-0i)CMvMOCR&|7M(2&g zycGZ4-gje?npWrIh3^6$UF&2&lYc018rKiQD7*l{k8t)EHNBTDIFHZi<~_e_Z@+Em zq6#u26=RR|WH}UU+)2N*UQTvE>xpB>@u*=XiZUm+_0|e6htdu-DP5} ztdTofOlI6RwDp5l28^5zLn=qd_t-MK%Q-~&J!`9<=+v69gWc6Xc*?*XM4R+1RX`k1 zvCob{MDeU#1b!3e&SEt~NTv<;O_^*E<{M>kcE0y;>_vU{CI#0Y2>24eb}8RSEL7y&|F2}Dkd+~ zpm2c2>nmTTTTXAga@Q#qulE+_A>TdW^UI^T%;BcH`!$1e?eoKWtuC*wKfP0Tp0(o2 zwcnODCc!L+b2`YD>AN=0P+>?*gZ9V8i#+_($^gKT=QZNMud71(gOcm2I}g*%X?>OE zlZv^n{sOq@Mnr;8(64`A&d9*wRZlFRw3i|K@cxEo1$R{Ir)<9|iagL7y8QI{XGLC$Zo4;Wto4dPemw@dLA zuwFxY4+P|(ai=6e6Jw%62ak3$5yNV{`i)W2!WZp?wYxRTKvlaEc5r&7S;Zj|Ez{OwH#PZ<1f(^iyh)?6s}~5tsD)~(N@9EK41Z!<UjRb zDGyx?b_|u=hgn!PQ$a5SKs;62x$l5Oooiq~h>Q}S+f)IXMHO&OzsRw`3^Zbt03`jtcc8zb-|E3H zUVX(OU=I;ZiIWB1J(U31f+#S_a89EPK{0S60I)X>&Gt-yHiTh-(||Iqy`c6_hr&0; z@}jT6+nT~Fr19^9vzNB~jEs#x3XpX@&oSLLFdyhtpg)NKt4F8OGlS-%V5>Ujv&BsB zTWkeb2kXn_1qNq-N3O$}Zd2lneVF%g___4aAxA=3qX-Wxvr}iCO{@M)LjN2SbwX6* zgiB0KWE5eAk#Bjz+F?K(5@qJ#AOeUBWEo>R&e!2 zezUYR<*JkYsQL}-3U+%PgGK|Q*?yAo)%-$P8?etm{y-x`p*jN;jC)OAchQB| za8$1P=VfHhBygqWGADZpPQZ}|YWsB0o3`f$`~%LD-Y2rMu0bIFq_g4wJ6VTkJgga2 zc9c5QX54UfwCsb)wo~37IeXczW2c#GuxWSDE+|2LSCH2bJ_GKGY5}M4V72&tM%KU5dr>EEP*ZQ^))8r;{%uEgtM=(pL?Cz>y8c}& zl66_4c-D{@U|5SWsOJJ2I!gr!h-|dFIY;n+s{T^AeYn$8pQ}TzGY9wvz01RcgCcQj z3+Lu!t;-0c=!~`wE*!_3*8=dvX;Gfd{FQVY%sWh|7<6=W^`n7wTzcO_qO;0z>INGv z#BW)*J$hFuJ$c@-;gktq`AQ>{e1rbF!`N#gH2O(Gq5v&VmamKDYgB-tgkul>cNhJ9DsA`roiFQ+~H&Vh`b|r;-+>j90iB2{6ERFpJJ4!T|pS-Qreee4$A zMpU`-Qmk$G*~;3uw_$6}|Jvwd$!^FV6NN$@f*LI)U15yO^feu=j(81WZ@mag17GIAS}Og<4FIpB@wxJl5-f0pL8j*Ys3CFSGcl<6@UC zBv0}EWX1fQ=ZRQxtv#zFflk9KG^!CW7a%q+JEBV+za(jIjz1Gpp)`#9UFM4%%Q02p z4}a&JPS0A)CO`kDjq_t6`wfxegI5KDBn8;GxkT-U%9Ay_C7%PQ({o!P7iyjv{fi&& zlO3t!Eo+)W`sb7#ub%q8{L)PH@}u`ex4o~84C#lfJx1MxLGt3?c*l$58X zgVj@i<=53mk+>b?r|J?D)7Xr|T}reg$HF{@i`|nS6Cx*I$B}Z;V+7E_z{(vFl&nD# zG6VBHhyV#7mVAc(+wV2HjoEfJ+nQEx{C+i!EJmy0>pC+1m88&6jv^Z+QfhE!teA&1 z9J=Xr?R%D%n?#GqY*smEiKPX-0U_iVpSsQoS-8`$6c}yh+hL|OPCZnVDT+g!BLt~W z3p3NNTOKpgH*QPB2nVx`M#HJ`)p3!|z63S{oQivJsfQA9NB@5Ou6nwuTvojLDbWE}Sg(Wn0d`Z|w;&COPW9daPR-07yj37D zwm9JJsKA;wh62|!z_oFt_qfVy2n!1|`u}uDx&3cND*XTG0c3HYQ>|bBOo0OdFwOrR z>i=7jn2NDF;|(O)99AU>_Ux_%lckHZf;luvFy^w)k;Z|HnbHO8g zWX#@&Ry$UUJsriN&6kNbEbcF9c&6A&v(4Xfx&Kb{0uqCqzviAEEJi3VPv%?ax`%?e zAl9;Oc2;Ec+D$!mpM734d~gVAe-(cM@W^T85``k*KXrvpnaKS7Kg(JRT)En)?Q$T9 z>DYnl&b^O(?}^X++yDOJJ}27+yF-Doc7jDmHNn-ct5~}0U*aM8^p9t*cr00RQXag_ z;xfyrDGh;p=Kc66Vizyg{Nl(Nqq~Jw1;ChNNOe0<7bDpI@v{Hnqh~qud3asEc1@E? zxLWKBYR)JGsIj~`|JGANds?_#mD#a)@rL@?*ubPrRR{0Y!1EAQ=n?P3OggL)(x8F_PyZ`;{(CI`3&KKaX%$r1Z( z8F{m+9_+4Oy?7Se+tY76SFtYx9t3h=(&oS$=G)>z&1=fzd3pZ5&`?|7q& z|2<}6Q?+(J{QWz7QQ?I4ziQy|gd(Q{TaOss?-oq>ek|^XSIiN?M@D`T8vV?kF=tMn zW)*f1b-(TwcXWsPS})KtUZsSe5;J(8UHDd4QSVYZS?;0A!gZYH>`58c_P4Ktr}&CI z4lLYc>;8RZgOq5@1kJ(?iLxPkPCfj&s(lU1CxHSD#^lrN+rOSIX86?(%sx-2H#~Jy z^ww&gx6#h-&00_!ev|Wopw3|6c$9bnO!&i?Fx&eoT}R64~7*hcq~-0=>W zSsNdL7AJk&cx4tyQz~fHvr)m_xxf%Q>JA*(dX&7<6trsT6lhUS>VsX@K*x9WH!PJ? z)!KLX5?BvEu=jAvS^u@6R-=)ZLvljumgKJ+rZ`k80L`s@k!tIv8sNscqG9UAzN?3U hQ%Q}$Wln^I|JZlfE-U)ur<}_G1fH&bF6*2UngDf8983TJ literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/07/package.json b/packages/databinding/steps/07/package.json new file mode 100644 index 000000000..3a0810d04 --- /dev/null +++ b/packages/databinding/steps/07/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step07", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/07/tsconfig.json b/packages/databinding/steps/07/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/07/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/07/ui5.yaml b/packages/databinding/steps/07/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/07/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/07/webapp/Component.ts b/packages/databinding/steps/07/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/07/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/07/webapp/i18n/i18n.properties b/packages/databinding/steps/07/webapp/i18n/i18n.properties new file mode 100644 index 000000000..761435352 --- /dev/null +++ b/packages/databinding/steps/07/webapp/i18n/i18n.properties @@ -0,0 +1,11 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled + +# Screen titles +panelHeaderText=Data Binding Basics diff --git a/packages/databinding/steps/07/webapp/i18n/i18n_de.properties b/packages/databinding/steps/07/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..b120815f9 --- /dev/null +++ b/packages/databinding/steps/07/webapp/i18n/i18n_de.properties @@ -0,0 +1,11 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert + +# Screen titles +panelHeaderText=Data Binding Grundlagen diff --git a/packages/databinding/steps/07/webapp/index-cdn.html b/packages/databinding/steps/07/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/07/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/07/webapp/index.html b/packages/databinding/steps/07/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/07/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/07/webapp/manifest.json b/packages/databinding/steps/07/webapp/manifest.json new file mode 100644 index 000000000..2738faec9 --- /dev/null +++ b/packages/databinding/steps/07/webapp/manifest.json @@ -0,0 +1,65 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/07/webapp/model/data.json b/packages/databinding/steps/07/webapp/model/data.json new file mode 100644 index 000000000..381adf692 --- /dev/null +++ b/packages/databinding/steps/07/webapp/model/data.json @@ -0,0 +1,5 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true +} \ No newline at end of file diff --git a/packages/databinding/steps/07/webapp/view/App.view.xml b/packages/databinding/steps/07/webapp/view/App.view.xml new file mode 100644 index 000000000..8a003efa7 --- /dev/null +++ b/packages/databinding/steps/07/webapp/view/App.view.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/packages/databinding/steps/08/README.md b/packages/databinding/steps/08/README.md new file mode 100644 index 000000000..ed5baf970 --- /dev/null +++ b/packages/databinding/steps/08/README.md @@ -0,0 +1,135 @@ + +# Step 8: Binding Paths: Accessing Properties in Hierarchically Structured Models + +In Step 6 , we stated that the fields in a resource model are arranged in a flat structure; in other words, there is no hierarchy of properties. However, this is only true for resource models. The properties within JSON and OData models are usually arranged in a hierarchical structure. So, let's explore how to reference fields in a hierarchically structured model object. + +## Preview + +### A second panel with address data is added + +![A second panel with address data is added](assets/Tutorial_Data_Binding_Step_8_12705f5.png "A second panel with address data is added") + +You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io/tutorials/databinding/build/08/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/databinding/databinding-step-08.zip) (TS)[📥 Download step 8](https://ui5.github.io/tutorials/databinding/databinding-step-08-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +In the `data.json` file, add an additional sub-object named `address`. This object has four properties: `street`, `city`, `zip`, and `country`. + +## webapp/model/data.json + +```json +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + } +} +``` + +Add a new panel to the `App.view.xml` with a new `Label` and `FormattedText` pair of elements. + +The `text` property of the `Label` element is bound to the i18n resource bundle field `address`. + +The `htmlText` property of the `FormattedText` element is bound to four JSON model properties: `/address/street`, `/address/zip`, `/address/city`, and `/address/country`. You can achieve the resulting address format by separating each one of these JSON model property references with a hard-coded newline character. Note that `zip` and `city` are separated by a space. + +## webapp/view/App.view.xml + +```xml + + + ... + + + + + + + + +``` + +Update the `i18n.properties` and `i18n_de.properties` files as shown below. + +## webapp/i18n/i18n.properties + +```properties +... + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details +``` + +## webapp/i18n/i18n\_de.properties + +```properties +... + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails +``` + +> :note: +> The resource bundle files now contain new properties for the address and a new panel header text. Both panel properties are numbered. +> +> In the XML view, inside the curly brackets for the binding path of the `htmlText` element, you'll notice that the first character is a forward slash. This is necessary for binding paths that make absolute references to properties in JSON and OData models, but you must not use it for resource models. After the first forward slash character, the binding path syntax uses the object name and the property names separated by forward slash characters \(`{/address/street}`\). +> +> Remember, all binding path names are case-sensitive. + +*** + +**Next:** [Step 9: Formatting Values](../09/README.md "We'd also like to provide our users with a way of contacting Harry Hawk, so we're adding a link that sends an e-mail to Harry.") + +**Previous:** [Step 7: \(Optional\) Resource Bundles and Multiple Languages](../07/README.md "Resource bundles exist to enable an app to run in multiple languages without the need to change any code.") + +*** + +**Related Information** + +[JSON Model](https://sdk.openui5.org/topic/96804e3315ff440aa0a50fd290805116.html#loio96804e3315ff440aa0a50fd290805116 "The JSON model can be used to bind controls to JavaScript object data, which is usually serialized in the JSON format.") + diff --git a/packages/databinding/steps/08/assets/Tutorial_Data_Binding_Step_8_12705f5.png b/packages/databinding/steps/08/assets/Tutorial_Data_Binding_Step_8_12705f5.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c058bf444fd393ac9128ef4951c9e5bf18554e GIT binary patch literal 33890 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 + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/08/webapp/index.html b/packages/databinding/steps/08/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/08/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/08/webapp/manifest.json b/packages/databinding/steps/08/webapp/manifest.json new file mode 100644 index 000000000..2738faec9 --- /dev/null +++ b/packages/databinding/steps/08/webapp/manifest.json @@ -0,0 +1,65 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/08/webapp/model/data.json b/packages/databinding/steps/08/webapp/model/data.json new file mode 100644 index 000000000..138a22fd4 --- /dev/null +++ b/packages/databinding/steps/08/webapp/model/data.json @@ -0,0 +1,11 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + } +} \ No newline at end of file diff --git a/packages/databinding/steps/08/webapp/view/App.view.xml b/packages/databinding/steps/08/webapp/view/App.view.xml new file mode 100644 index 000000000..e003ff4da --- /dev/null +++ b/packages/databinding/steps/08/webapp/view/App.view.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + diff --git a/packages/databinding/steps/09/README.md b/packages/databinding/steps/09/README.md new file mode 100644 index 000000000..f358eb7a8 --- /dev/null +++ b/packages/databinding/steps/09/README.md @@ -0,0 +1,172 @@ + +# Step 9: Formatting Values + +We'd also like to provide our users with a way of contacting Harry Hawk, so we're adding a link that sends an e-mail to Harry. To do this, we convert our data in the model to match the `sap.m.URLHelper.normalizeEmail` API. As soon as the user changes the name, the e-mail also changes. We need a custom formatter function for this. + +## Preview + +### An e-mail link is added to the address panel + +![An e-mail link is added to the address panel](assets/Tutorial_Data_Binding_Step_9_1161575.png "An e-mail link is added to the address panel") + +You can view this step live: [🔗 Live Preview of Step 9](https://ui5.github.io/tutorials/databinding/build/09/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/databinding/databinding-step-09.zip) (TS)[📥 Download step 9](https://ui5.github.io/tutorials/databinding/databinding-step-09-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── controller/ +│ └── App.controller.ts/.js +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Create a new folder named `controller` within your `webapp` folder as a general location for all controller files for this app. Next, create a new file named `App.controller.ts/.js` with the following content: + +## `webapp/controller/App.controller.ts/.js` \(New\) + +```ts +// webapp/controller/App.controller.ts +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + public async formatMail(firstName: string, lastName: string): Promise { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } +} +``` + +```js +// webapp/controller/App.controller.js +sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/library"], function (Controller, mobileLibrary) { + "use strict"; + + return Controller.extend("ui5.tutorial.databinding.controller.App", { + async formatMail(firstName, lastName) { + const bundle = await this.getView().getModel("i18n").getResourceBundle(); + + return mobileLibrary.URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } + }); +}); +``` + +In our custom formatter, we set the first and last name currently in the model as function parameters. When a user changes the data in the model by entering a different name in the input fields, our formatter will be invoked automatically by the framework. This ensures that the UI stays in sync with the data model. + +In the `formatMail` function, we use the `sap.m.URLHelper.normalizeEmail` function that expects an e-mail address, a mail subject, and a text body. When a user follows the link, their default email client will open with these parameters. For more information, see [API Reference: `sap.m.URLHelper.normalizeEmail`](https://sdk.openui5.org/#/api/sap.m.URLHelper/methods/normalizeEmail). The `mailSubject` resource bundle text contains a placeholder for the recipient's first name \(see below\). Therefore, we provide the name with `[firstName]`. + +> :note: +> For a detailed description of the e-mail link format, see [MDN - Creating hyperlinks: Email links](https://developer.mozilla.org/de/docs/Web/Guide/HTML/Email_links). + +Enhance the `App.view.xml` file as shown below: + +## webapp/view/App.view.xml + +```xml + + ... + + + + ... + + + + + +``` + +For more complex bindings, we can't use the simple binding syntax with the curly braces anymore. The `href` property of the `Link` element now contains an entire object inside the string value. In this case, the object has two properties: + +- `parts` + + This is a JavaScript array in which each element is a string representing a `path` property. The number and order of the elements in this array correspond directly to the number and order of parameters expected by the `formatMail` function. + +- `formatter` + + This is a reference to the function that receives the parameters listed in the `parts` array. Whatever value the formatter function returns becomes the value set for the `href` property. The dot `formatMail`\) at the beginning of the formatter tells OpenUI5 to look for a `formatMail` function on the controller instance of the view. If you don't use the dot, the function will be resolved by looking into the global namespace. + +> :note: +> When using formatter functions, the binding automatically switches to "one-way". Therefore, you can’t use a formatter function for "two-way" scenarios. However, you can use data types \(which we explain in the following steps\). + +Add the `# E-mail` section to the `i18n.properties` and `i18n_de.properties` files as shown below. + +## webapp/i18n/i18n.properties + +```properties +... + +# E-mail +sendEmail=Send Mail +mailSubject=Hi {0}! +mailBody=How are you? +``` + +## webapp/i18n/i18n\_de.properties + +```properties +... + +# E-mail +sendEmail=E-mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? +``` + +*** + +**Next:** [Step 10: Property Formatting Using Data Types](../10/README.md "OpenUI5 offers a set of simple data types, including Boolean, Currency, Date and Float.") + +**Previous:** [Step 8: Binding Paths: Accessing Properties in Hierarchically Structured Models](../08/README.md "In Step 6, we stated that the fields in a resource model are arranged in a flat structure; in other words, there is no hierarchy of properties.") + +*** + +**Related Information** + +[Formatting, Parsing, and Validating Data](https://sdk.openui5.org/topic/07e4b920f5734fd78fdaa236f26236d8.html "Data that is presented on the UI often has to be converted so that is human readable and fits to the locale of the user. On the other hand, data entered by the user has to be parsed and validated to be understood by the data source. For this purpose, you use formatters and data types.") diff --git a/packages/databinding/steps/09/assets/Tutorial_Data_Binding_Step_9_1161575.png b/packages/databinding/steps/09/assets/Tutorial_Data_Binding_Step_9_1161575.png new file mode 100644 index 0000000000000000000000000000000000000000..e184af3b6d5275f246407a5652eb4bfb391559f6 GIT binary patch literal 36217 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 literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/09/package.json b/packages/databinding/steps/09/package.json new file mode 100644 index 000000000..e468ebe81 --- /dev/null +++ b/packages/databinding/steps/09/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step09", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/09/tsconfig.json b/packages/databinding/steps/09/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/09/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/09/ui5.yaml b/packages/databinding/steps/09/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/09/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/09/webapp/Component.ts b/packages/databinding/steps/09/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/09/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/09/webapp/controller/App.controller.ts b/packages/databinding/steps/09/webapp/controller/App.controller.ts new file mode 100644 index 000000000..36a95910b --- /dev/null +++ b/packages/databinding/steps/09/webapp/controller/App.controller.ts @@ -0,0 +1,19 @@ +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + public async formatMail(firstName: string, lastName: string): Promise { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } +} diff --git a/packages/databinding/steps/09/webapp/i18n/i18n.properties b/packages/databinding/steps/09/webapp/i18n/i18n.properties new file mode 100644 index 000000000..18975b0b1 --- /dev/null +++ b/packages/databinding/steps/09/webapp/i18n/i18n.properties @@ -0,0 +1,18 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled +address=Address + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details + +# Email +sendEmail=Send EMail +mailSubject=Hi {0}! +mailBody=How are you? diff --git a/packages/databinding/steps/09/webapp/i18n/i18n_de.properties b/packages/databinding/steps/09/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..df07d86e8 --- /dev/null +++ b/packages/databinding/steps/09/webapp/i18n/i18n_de.properties @@ -0,0 +1,18 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails + +# Email +sendEmail=E-Mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? diff --git a/packages/databinding/steps/09/webapp/index-cdn.html b/packages/databinding/steps/09/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/09/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/09/webapp/index.html b/packages/databinding/steps/09/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/09/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/09/webapp/manifest.json b/packages/databinding/steps/09/webapp/manifest.json new file mode 100644 index 000000000..2738faec9 --- /dev/null +++ b/packages/databinding/steps/09/webapp/manifest.json @@ -0,0 +1,65 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/09/webapp/model/data.json b/packages/databinding/steps/09/webapp/model/data.json new file mode 100644 index 000000000..138a22fd4 --- /dev/null +++ b/packages/databinding/steps/09/webapp/model/data.json @@ -0,0 +1,11 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + } +} \ No newline at end of file diff --git a/packages/databinding/steps/09/webapp/view/App.view.xml b/packages/databinding/steps/09/webapp/view/App.view.xml new file mode 100644 index 000000000..48c8f4017 --- /dev/null +++ b/packages/databinding/steps/09/webapp/view/App.view.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + diff --git a/packages/databinding/steps/10/README.md b/packages/databinding/steps/10/README.md new file mode 100644 index 000000000..913e3faa6 --- /dev/null +++ b/packages/databinding/steps/10/README.md @@ -0,0 +1,144 @@ + +# Step 10: Property Formatting Using Data Types + +OpenUI5 offers a set of simple data types, including `Boolean`, `Currency`, `Date` and `Float`. You can apply these data types to controls to ensure that the value displayed on the screen is formatted correctly. If the field is open for input, this also ensures that the user input meets the requirements of that data type. Let's add a new field called *Sales Amount* of type `Currency`. + +## Preview + +### An input field for a currency amount is added to the second panel + +![An input field for a currency amount is added to the second panel](assets/Tutorial_Data_Binding_Step_10_d15f8bc.png "An input field for a currency amount is added to the second panel") + +You can view this step live: [🔗 Live Preview of Step 10](https://ui5.github.io/tutorials/databinding/build/10/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/databinding/databinding-step-10.zip) (TS)[📥 Download step 10](https://ui5.github.io/tutorials/databinding/databinding-step-10-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── controller/ +│ └── App.controller.ts/.js +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Add two new JSON model properties, `salesAmount` and `currencyCode`, to the `data.json` file. + +## webapp/model/data.json + +```json +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "currencyCode": "EUR" +} +``` + +Add a `sap.ui.layout.HorizontalLayout` to the content of the second `sap.m.Panel` within `App.view.xml` file. Move the existing `sap.ui.layout.VerticalLayout` to the default aggregation of the new `sap.ui.layout.HorizontalLayout`. Finally, add a second `sap.ui.layout.VerticalLayout`, containing a `sap.m.Label` and a `sap.m.Input` control, to the `sap.ui.layout.HorizontalLayout`. + +## webapp/view/App.view.xml + +```xml + + ... + + + + + ... + + + + + + + +``` + +We've created a new pair of `Label` and `Input` elements for the `salesAmount` model property. The description property of the `Input` element is bound to the `currencyCode` model property. The value property of the `Input` element is bound to the model properties `salesAmount` and `currencyCode`. The `{showMeasure: false}` parameter switches off the display of the currency symbol within the input field itself. This isn't necessary because the currency symbol is displayed using the `Input` element's description property. + +Add the highlighted texts to the `properties` files. Remember, you need to enter special characters \(non-Latin-1\) using Unicode escape characters. + +## webapp/i18n/i18n.properties + +```properties +... +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Enabled +address=Address +salesAmount=Sales Amount +... +``` + +## webapp/i18n/i18n\_de.properties + +```properties +... +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse +salesAmount=Verk\u00e4ufe bis zum heutigen Datum +... +``` + +*** + +**Next:** [Step 11: Validation Using `sap/ui/core/Messaging`](../11/README.md "Up to this point, we've created a currency field that formats itself correctly. The currency data type can also validate user input to ensure it meets currency requirements.") + +**Previous:** [Step 9: Formatting Values](../09/README.md "We'd also like to provide our users with a way of contacting Harry Hawk, so we're adding a link that sends an e-mail to Harry.") + +*** + +**Related Information** + +[Formatting, Parsing, and Validating Data](https://sdk.openui5.org/topic/07e4b920f5734fd78fdaa236f26236d8.html "Data that is presented on the UI often has to be converted so that is human readable and fits to the locale of the user. On the other hand, data entered by the user has to be parsed and validated to be understood by the data source. For this purpose, you use formatters and data types.") diff --git a/packages/databinding/steps/10/assets/Tutorial_Data_Binding_Step_10_d15f8bc.png b/packages/databinding/steps/10/assets/Tutorial_Data_Binding_Step_10_d15f8bc.png new file mode 100644 index 0000000000000000000000000000000000000000..220e18c429889622bafcf48ab490f60c358b7ff9 GIT binary patch literal 44353 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 B#UKCx literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/10/package.json b/packages/databinding/steps/10/package.json new file mode 100644 index 000000000..a73a073f2 --- /dev/null +++ b/packages/databinding/steps/10/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step10", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/10/tsconfig.json b/packages/databinding/steps/10/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/10/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/10/ui5.yaml b/packages/databinding/steps/10/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/10/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/10/webapp/Component.ts b/packages/databinding/steps/10/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/10/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/10/webapp/controller/App.controller.ts b/packages/databinding/steps/10/webapp/controller/App.controller.ts new file mode 100644 index 000000000..36a95910b --- /dev/null +++ b/packages/databinding/steps/10/webapp/controller/App.controller.ts @@ -0,0 +1,19 @@ +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + public async formatMail(firstName: string, lastName: string): Promise { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } +} diff --git a/packages/databinding/steps/10/webapp/i18n/i18n.properties b/packages/databinding/steps/10/webapp/i18n/i18n.properties new file mode 100644 index 000000000..bd88296b2 --- /dev/null +++ b/packages/databinding/steps/10/webapp/i18n/i18n.properties @@ -0,0 +1,19 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled +address=Address +salesAmount=Sales Amount + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details + +# Email +sendEmail=Send EMail +mailSubject=Hi {0}! +mailBody=How are you? diff --git a/packages/databinding/steps/10/webapp/i18n/i18n_de.properties b/packages/databinding/steps/10/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..21988f6c1 --- /dev/null +++ b/packages/databinding/steps/10/webapp/i18n/i18n_de.properties @@ -0,0 +1,19 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse +salesAmount=Verk\u00e4ufe bis zum heutigen Datum + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails + +# Email +sendEmail=E-Mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? diff --git a/packages/databinding/steps/10/webapp/index-cdn.html b/packages/databinding/steps/10/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/10/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/10/webapp/index.html b/packages/databinding/steps/10/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/10/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/10/webapp/manifest.json b/packages/databinding/steps/10/webapp/manifest.json new file mode 100644 index 000000000..2738faec9 --- /dev/null +++ b/packages/databinding/steps/10/webapp/manifest.json @@ -0,0 +1,65 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/10/webapp/model/data.json b/packages/databinding/steps/10/webapp/model/data.json new file mode 100644 index 000000000..da676ea6b --- /dev/null +++ b/packages/databinding/steps/10/webapp/model/data.json @@ -0,0 +1,13 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "currencyCode": "EUR" +} \ No newline at end of file diff --git a/packages/databinding/steps/10/webapp/view/App.view.xml b/packages/databinding/steps/10/webapp/view/App.view.xml new file mode 100644 index 000000000..639403174 --- /dev/null +++ b/packages/databinding/steps/10/webapp/view/App.view.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + diff --git a/packages/databinding/steps/11/README.md b/packages/databinding/steps/11/README.md new file mode 100644 index 000000000..8f7ebb708 --- /dev/null +++ b/packages/databinding/steps/11/README.md @@ -0,0 +1,71 @@ + +# Step 11: Validation Using `sap/ui/core/Messaging` + +Up to this point, we've created a currency field that formats itself correctly. The *currency* data type can also validate user input to ensure it meets currency requirements. However, OpenUI5 manages data type validation functions and doesn't have a built-in mechanism for reporting error messages back to the UI. We therefore need a way to report error messages from validation functions back to the user. In this step, we're enabling validation for the entire app with a feature known as "Messaging". Once this is set up, any validation error messages based on user input get passed to `Messaging`, which then connects them to the appropriate view and control that caused the error. + +## Preview + +### An error message is displayed upon entering text into the currency amount input field + +![An error message is displayed upon entering text into the currency amount input field](assets/Tutorial_Data_Binding_Step_11_3_e67207b.png "An error message is displayed upon entering text into the currency amount input field") + +You can view this step live: [🔗 Live Preview of Step 11](https://ui5.github.io/tutorials/databinding/build/11/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/databinding/databinding-step-11.zip) (TS)[📥 Download step 11](https://ui5.github.io/tutorials/databinding/databinding-step-11-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── controller/ +│ └── App.controller.ts/.js +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +## webapp/manifest.json + +To generally enable validation in the app, add the `handleValidation` property into the `sap.ui5` section of the `manifest.json` file as shown below. + +```json +... + "sap.ui5": { + "handleValidation": true, + ... + } +... +``` + +Now, try entering a non-numeric value into the *Sales Amount* field and either press [Enter\] or move the focus to a different UI control. This action triggers either the `onenter` or `onchange` event. OpenUI5 then executes the validation function for the `sap.ui.model.type.Currency` data type. + +With validation handling enabled in the manifest, any validation error messages will be picked up by `Messaging`. It checks its list of registered objects and passes the error message back to the correct view for display. + +You'll notice that the field in error has a red border: + +![Input field showing a validation error](assets/Tutorial_Data_Binding_Step_11_2_44db88e.png "Input field showing a validation error") + +However, the error message only displays when that particular field is in focus: + +![Input field showing a validation error](assets/Tutorial_Data_Binding_Step_11_3_e67207b.png "Input field showing a validation error") + +*** + +**Next:** [Step 12: Aggregation Binding Using Templates](../12/README.md "Aggregation binding, also known as list binding, lets a control bind to a list within the model data.") + +**Previous:** [Step 10: Property Formatting Using Data Types](../10/README.md "OpenUI5 offers a set of simple data types, including Boolean, Currency, Date and Float.") + +*** + +**Related Information** + +[Error, Warning, and Info Messages](https://sdk.openui5.org/topic/62b1481d3e084cb49dd30956d183c6a0.html "OpenUI5 provides a central place for storing and managing info, warning, and error messages.") diff --git a/packages/databinding/steps/11/assets/Tutorial_Data_Binding_Step_11_2_44db88e.png b/packages/databinding/steps/11/assets/Tutorial_Data_Binding_Step_11_2_44db88e.png new file mode 100644 index 0000000000000000000000000000000000000000..0bad8bfbe2705751eb1ef42dbd16b7ef312b4aa8 GIT binary patch literal 5666 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
^5K9lAVfBfQPk^qYPR4e^51NP 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 literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/11/package.json b/packages/databinding/steps/11/package.json new file mode 100644 index 000000000..1cd5ae47b --- /dev/null +++ b/packages/databinding/steps/11/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step11", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/11/tsconfig.json b/packages/databinding/steps/11/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/11/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/11/ui5.yaml b/packages/databinding/steps/11/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/11/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/11/webapp/Component.ts b/packages/databinding/steps/11/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/11/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/11/webapp/controller/App.controller.ts b/packages/databinding/steps/11/webapp/controller/App.controller.ts new file mode 100644 index 000000000..36a95910b --- /dev/null +++ b/packages/databinding/steps/11/webapp/controller/App.controller.ts @@ -0,0 +1,19 @@ +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + public async formatMail(firstName: string, lastName: string): Promise { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } +} diff --git a/packages/databinding/steps/11/webapp/i18n/i18n.properties b/packages/databinding/steps/11/webapp/i18n/i18n.properties new file mode 100644 index 000000000..bd88296b2 --- /dev/null +++ b/packages/databinding/steps/11/webapp/i18n/i18n.properties @@ -0,0 +1,19 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled +address=Address +salesAmount=Sales Amount + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details + +# Email +sendEmail=Send EMail +mailSubject=Hi {0}! +mailBody=How are you? diff --git a/packages/databinding/steps/11/webapp/i18n/i18n_de.properties b/packages/databinding/steps/11/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..21988f6c1 --- /dev/null +++ b/packages/databinding/steps/11/webapp/i18n/i18n_de.properties @@ -0,0 +1,19 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse +salesAmount=Verk\u00e4ufe bis zum heutigen Datum + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails + +# Email +sendEmail=E-Mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? diff --git a/packages/databinding/steps/11/webapp/index-cdn.html b/packages/databinding/steps/11/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/11/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/11/webapp/index.html b/packages/databinding/steps/11/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/11/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/11/webapp/manifest.json b/packages/databinding/steps/11/webapp/manifest.json new file mode 100644 index 000000000..41eefc4e4 --- /dev/null +++ b/packages/databinding/steps/11/webapp/manifest.json @@ -0,0 +1,66 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "handleValidation": true, + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/11/webapp/model/data.json b/packages/databinding/steps/11/webapp/model/data.json new file mode 100644 index 000000000..da676ea6b --- /dev/null +++ b/packages/databinding/steps/11/webapp/model/data.json @@ -0,0 +1,13 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "currencyCode": "EUR" +} \ No newline at end of file diff --git a/packages/databinding/steps/11/webapp/view/App.view.xml b/packages/databinding/steps/11/webapp/view/App.view.xml new file mode 100644 index 000000000..639403174 --- /dev/null +++ b/packages/databinding/steps/11/webapp/view/App.view.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + diff --git a/packages/databinding/steps/12/README.md b/packages/databinding/steps/12/README.md new file mode 100644 index 000000000..2aa1bfd04 --- /dev/null +++ b/packages/databinding/steps/12/README.md @@ -0,0 +1,305 @@ + +# Step 12: Aggregation Binding Using Templates + +Aggregation binding, also known as "list binding", lets a control bind to a list within the model data. This binding allows relative binding to the list entries by its child controls. + +The system automatically creates as many child controls as are needed to display the data in the model using one of two approaches: + +- It clones a template control as many times as necessary to display the data. + +- It uses a factory function to generate the correct control for each bound list entry, based on the data received at runtime. + +## Preview + +### A third panel with a list of products is displayed + +![A third panel with a list of products is displayed](assets/Tutorial_Data_Binding_Step_12_1642433.png "A third panel with a list of products is displayed") + +You can view this step live: [🔗 Live Preview of Step 12](https://ui5.github.io/tutorials/databinding/build/12/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 12](https://ui5.github.io/tutorials/databinding/databinding-step-12.zip) (TS)[📥 Download step 12](https://ui5.github.io/tutorials/databinding/databinding-step-12-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── controller/ +│ └── App.controller.ts/.js +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ ├── Products.json +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Add a new entry named `products` to the `models` entry under `sap.ui5` in the `manifest.json` file: + +## webapp/manifest.json + +```json +{ + ... + "sap.ui5": { + ... + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "products" : { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/Products.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + } + }, + ... + } + ... +} +``` + +Create a new file named `Products.json` in the `model` folder. Enter the data for the products: + +## webapp/model/Products.json \(New\) + +```json +{ + "Products": [ + { + "ProductID": 1, + "ProductName": "Chai", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "10 boxes x 20 bags", + "UnitPrice": "18.0000", + "UnitsInStock": 39, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": false + }, + { + "ProductID": 2, + "ProductName": "Chang", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": "19.0000", + "UnitsInStock": 17, + "UnitsOnOrder": 40, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 3, + "ProductName": "Aniseed Syrup", + "SupplierID": 1, + "CategoryID": 2, + "QuantityPerUnit": "12 - 550 ml bottles", + "UnitPrice": "10.0000", + "UnitsInStock": 13, + "UnitsOnOrder": 70, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 4, + "ProductName": "Chef Anton's Cajun Seasoning", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "48 - 6 oz jars", + "UnitPrice": "22.0000", + "UnitsInStock": 53, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": false + }, + { + "ProductID": 5, + "ProductName": "Chef Anton's Gumbo Mix", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "36 boxes", + "UnitPrice": "21.3500", + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": true + } + ] +} +``` + +In the `App.view.xml` file, add a new panel with an `sap.m.List` control containing the `sap.m.ObjectListItem` template control as shown below. Note that the template control is only present once in the XML view. It's automatically cloned for each entry in the products' JSON model. + +## webapp/view/App.view.xml + +```xml + + ... + + + + + + + + + + + + + +``` + +Also, add another formatter to the `App.controller.ts/.js` file to calculate the value of the stock of each product. + +## `webapp/controller/App.controller.ts/.js` + +```ts +// webapp/controller/App.controller.ts +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; +import Currency from "sap/ui/model/type/Currency"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + public async formatMail(firstName: string, lastName: string): Promise { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } + + public formatStockValue(unitPrice: number, stockLevel: number, currencyCode: string): string { + const currency: Currency = new Currency(); + return currency.formatValue([unitPrice * stockLevel, currencyCode], "string"); + } +} +``` + +```js +// webapp/controller/App.controller.js +sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/library", "sap/ui/model/type/Currency"], function (Controller, mobileLibrary, Currency) { + "use strict"; + + return Controller.extend("ui5.tutorial.databinding.controller.App", { + async formatMail(firstName, lastName) { + const bundle = await this.getView().getModel("i18n").getResourceBundle(); + + return mobileLibrary.URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + }, + + formatStockValue(unitPrice, stockLevel, currencyCode) { + const currency = new Currency(); + return currency.formatValue([unitPrice * stockLevel, currencyCode], "string"); + } + }); +}); +``` + +Lastly, add the missing texts to the `i18n.properties` and `i18n_de.properties` files. These texts are used in the newly added UI elements. + +## webapp/i18n/i18n.properties + +```properties +... +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details +panel3HeaderText=Aggregation Binding + +... + +# Product list +productListTitle=Product List +stockValue=Current Stock Value +``` + +## webapp/i18n/i18n\_de.properties + +```properties +... +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails +panel3HeaderText=Aggregation Binding + +... + +# Product list +productListTitle=Artikelliste +stockValue=Lagerbestand Wert +``` + +*** + +**Next:** [Step 13: Element Binding](../13/README.md "Now, let's do something with that newly generated list.") + +**Previous:** [Step 11: Validation Using `sap/ui/core/Messaging`](../11/README.md "Up to this point, we've created a currency field that formats itself correctly.") + +*** + +**Related Information** + +[List Binding \(Aggregation Binding\)](https://sdk.openui5.org/topic/91f057786f4d1014b6dd926db0e91070.html "List binding (or aggregation binding) is used to automatically create child controls according to model data.") diff --git a/packages/databinding/steps/12/assets/Tutorial_Data_Binding_Step_12_1642433.png b/packages/databinding/steps/12/assets/Tutorial_Data_Binding_Step_12_1642433.png new file mode 100644 index 0000000000000000000000000000000000000000..2e6f27c35f68ebd4483846b99d69cc08df7c8ef3 GIT binary patch literal 96655 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
+ + diff --git a/packages/databinding/steps/12/webapp/index.html b/packages/databinding/steps/12/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/12/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/12/webapp/manifest.json b/packages/databinding/steps/12/webapp/manifest.json new file mode 100644 index 000000000..3852b19e3 --- /dev/null +++ b/packages/databinding/steps/12/webapp/manifest.json @@ -0,0 +1,70 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "handleValidation": true, + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "products": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/Products.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/12/webapp/model/Products.json b/packages/databinding/steps/12/webapp/model/Products.json new file mode 100644 index 000000000..2b47ed742 --- /dev/null +++ b/packages/databinding/steps/12/webapp/model/Products.json @@ -0,0 +1,64 @@ +{ + "Products": [ + { + "ProductID": 1, + "ProductName": "Chai", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "10 boxes x 20 bags", + "UnitPrice": "18.0000", + "UnitsInStock": 39, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": false + }, + { + "ProductID": 2, + "ProductName": "Chang", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": "19.0000", + "UnitsInStock": 17, + "UnitsOnOrder": 40, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 3, + "ProductName": "Aniseed Syrup", + "SupplierID": 1, + "CategoryID": 2, + "QuantityPerUnit": "12 - 550 ml bottles", + "UnitPrice": "10.0000", + "UnitsInStock": 13, + "UnitsOnOrder": 70, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 4, + "ProductName": "Chef Anton's Cajun Seasoning", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "48 - 6 oz jars", + "UnitPrice": "22.0000", + "UnitsInStock": 53, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": false + }, + { + "ProductID": 5, + "ProductName": "Chef Anton's Gumbo Mix", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "36 boxes", + "UnitPrice": "21.3500", + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": true + } + ] +} \ No newline at end of file diff --git a/packages/databinding/steps/12/webapp/model/data.json b/packages/databinding/steps/12/webapp/model/data.json new file mode 100644 index 000000000..da676ea6b --- /dev/null +++ b/packages/databinding/steps/12/webapp/model/data.json @@ -0,0 +1,13 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "currencyCode": "EUR" +} \ No newline at end of file diff --git a/packages/databinding/steps/12/webapp/view/App.view.xml b/packages/databinding/steps/12/webapp/view/App.view.xml new file mode 100644 index 000000000..338bd67e0 --- /dev/null +++ b/packages/databinding/steps/12/webapp/view/App.view.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/databinding/steps/13/README.md b/packages/databinding/steps/13/README.md new file mode 100644 index 000000000..17870b04f --- /dev/null +++ b/packages/databinding/steps/13/README.md @@ -0,0 +1,212 @@ + +# Step 13: Element Binding + +Now, let's do something with that newly generated list. Typically, you use a list to allow selection of an item and then display the details of that item elsewhere. To accomplish this, we use a form with relatively bound controls and bind it to the selected entity via element binding. + +## Preview + +### A fourth panel with details for a selected product is displayed + +![A fourth panel with details for a selected product is displayed](assets/Tutorial_Data_Binding_Step_13_872d2ed.png "A fourth panel with details for a selected product is displayed") + +You can view this step live: [🔗 Live Preview of Step 13](https://ui5.github.io/tutorials/databinding/build/13/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 13](https://ui5.github.io/tutorials/databinding/databinding-step-13.zip) (TS)[📥 Download step 13](https://ui5.github.io/tutorials/databinding/databinding-step-13-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── controller/ +│ └── App.controller.ts/.js +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ ├── Products.json +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +In the `App.view.xml` file, add a `press` event handler to the items in the list. Below the panel with the list, add a new panel with an `sap.m.SimpleForm`. To populate the form with data, we bind the entire panel to the path of the element you clicked in the list. + +## webapp/view/App.view.xml + +```xml +... + + ... + + + + + + ... + + + + + + + + + + +``` + +In the controller, add a new function `onItemSelected`, which binds the newly created panel to the correct item whenever it's pressed. + +## `webapp/controller/App.controller.ts/.js` + +```ts +// webapp/controller/App.controller.ts +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import { ListItemBase$DetailPressEvent } from "sap/m/ListItemBase"; +import ObjectListItem from "sap/m/ObjectListItem"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; +import Currency from "sap/ui/model/type/Currency"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + ... + + public onItemSelected(event: ListItemBase$DetailPressEvent): void { + const bindingPath: string = (event.getSource() as ObjectListItem).getBindingContext("products").getPath(); + this.byId("productDetailsPanel")?.bindElement({ path: bindingPath, model: "products" }); + } +} +``` + +```js +// webapp/controller/App.controller.js +sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/library", "sap/ui/model/type/Currency"], function (Controller, mobileLibrary, Currency) { + "use strict"; + + return Controller.extend("ui5.tutorial.databinding.controller.App", { + ... + + onItemSelected(event) { + const bindingPath = event.getSource().getBindingContext("products").getPath(); + this.byId("productDetailsPanel")?.bindElement({ path: bindingPath, model: "products" }); + } + }); +}); +``` + +Lastly, add the new texts to the `i18n.properties` and `i18n_de.properties` files. + +## webapp/i18n/i18n.properties + +```properties +... +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details +panel3HeaderText=Aggregation Binding +panel4HeaderText=Product Details + +... + +# Product Details +ProductID=Product ID +ProductName=Product Name +QuantityPerUnit=Quantity per Unit +UnitPrice=Unit Price +UnitsInStock=Number of Units in Stock +Discontinued=Discontinued + +``` + +## webapp/i18n/i18n\_de.properties + +```properties +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails +panel3HeaderText=Aggregation Binding +panel4HeaderText=Produktdetails + +... + +# Product Details +ProductID=Produkt-ID +ProductName=Produktname +QuantityPerUnit=Menge pro Einheit +UnitPrice=Preis pro Einheit +UnitsInStock=Lagerbestand +Discontinued=Eingestellt +``` + +Now, you can click on an element in the list and view its details in the panel below. You can even edit these details, and the changes are directly reflected in the list because we use two-way binding. + +> :note: +> Element bindings can also be relative to their parent context. + +*** + +**Next:** [Step 14: Expression Binding](../14/README.md "An expression binding lets you display a calculated value on the screen, which is derived from values found in a model object.") + +**Previous:** [Step 12: Aggregation Binding Using Templates](../12/README.md "Aggregation binding, also known as list binding, lets a control bind to a list within the model data.") + +*** + +**Related Information** + +[Context Binding \(Element Binding\)](https://sdk.openui5.org/topic/91f05e8b6f4d1014b6dd926db0e91070.html "Context binding (or element binding) allows you to bind elements to a specific object in the model data, which will create a binding context and allow relative binding within the control and all of its children. This is especially helpful in list-detail scenarios.") diff --git a/packages/databinding/steps/13/assets/Tutorial_Data_Binding_Step_13_872d2ed.png b/packages/databinding/steps/13/assets/Tutorial_Data_Binding_Step_13_872d2ed.png new file mode 100644 index 0000000000000000000000000000000000000000..33c1216c036373eeb0b5d399237793e06ad4552c GIT binary patch literal 84291 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

%hq3LxJFg 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 P06&VdYBFWd-vs^-ALhOA literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/13/package.json b/packages/databinding/steps/13/package.json new file mode 100644 index 000000000..981def262 --- /dev/null +++ b/packages/databinding/steps/13/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step13", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/13/tsconfig.json b/packages/databinding/steps/13/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/13/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/13/ui5.yaml b/packages/databinding/steps/13/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/13/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/13/webapp/Component.ts b/packages/databinding/steps/13/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/13/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/13/webapp/controller/App.controller.ts b/packages/databinding/steps/13/webapp/controller/App.controller.ts new file mode 100644 index 000000000..97c2a9cb8 --- /dev/null +++ b/packages/databinding/steps/13/webapp/controller/App.controller.ts @@ -0,0 +1,32 @@ +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import { ListItemBase$DetailPressEvent } from "sap/m/ListItemBase"; +import ObjectListItem from "sap/m/ObjectListItem"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; +import Currency from "sap/ui/model/type/Currency"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + public async formatMail(firstName: string, lastName: string): Promise { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } + + public formatStockValue(unitPrice: number, stockLevel: number, currencyCode: string): string { + const currency: Currency = new Currency(); + return currency.formatValue([unitPrice * stockLevel, currencyCode], "string"); + } + + public onItemSelected(event: ListItemBase$DetailPressEvent): void { + const bindingPath: string = (event.getSource() as ObjectListItem).getBindingContext("products").getPath(); + this.byId("productDetailsPanel")?.bindElement({ path: bindingPath, model: "products" }); + } +} diff --git a/packages/databinding/steps/13/webapp/i18n/i18n.properties b/packages/databinding/steps/13/webapp/i18n/i18n.properties new file mode 100644 index 000000000..da1880d42 --- /dev/null +++ b/packages/databinding/steps/13/webapp/i18n/i18n.properties @@ -0,0 +1,33 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled +address=Address +salesAmount=Sales Amount + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details +panel3HeaderText=Aggregation Binding +panel4HeaderText=Product Details + +# Email +sendEmail=Send EMail +mailSubject=Hi {0}! +mailBody=How are you? + +# Product list +productListTitle=Product List +stockValue=Current Stock Value + +# Product Details +ProductID=Product ID +ProductName=Product Name +QuantityPerUnit=Quantity per Unit +UnitPrice=Unit Price +UnitsInStock=Number of Units in Stock +Discontinued=Discontinued diff --git a/packages/databinding/steps/13/webapp/i18n/i18n_de.properties b/packages/databinding/steps/13/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..73b5af5d6 --- /dev/null +++ b/packages/databinding/steps/13/webapp/i18n/i18n_de.properties @@ -0,0 +1,33 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse +salesAmount=Verk\u00e4ufe bis zum heutigen Datum + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails +panel3HeaderText=Aggregation Binding +panel4HeaderText=Produktdetails + +# Email +sendEmail=E-Mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? + +# Product list +productListTitle=Artikelliste +stockValue=Lagerbestand Wert + +# Product Details +ProductID=Produkt-ID +ProductName=Produktname +QuantityPerUnit=Menge pro Einheit +UnitPrice=Preis pro Einheit +UnitsInStock=Lagerbestand +Discontinued=Eingestellt diff --git a/packages/databinding/steps/13/webapp/index-cdn.html b/packages/databinding/steps/13/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/13/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +

+ + diff --git a/packages/databinding/steps/13/webapp/index.html b/packages/databinding/steps/13/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/13/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
+ + diff --git a/packages/databinding/steps/13/webapp/manifest.json b/packages/databinding/steps/13/webapp/manifest.json new file mode 100644 index 000000000..3852b19e3 --- /dev/null +++ b/packages/databinding/steps/13/webapp/manifest.json @@ -0,0 +1,70 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "handleValidation": true, + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "products": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/Products.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/13/webapp/model/Products.json b/packages/databinding/steps/13/webapp/model/Products.json new file mode 100644 index 000000000..2b47ed742 --- /dev/null +++ b/packages/databinding/steps/13/webapp/model/Products.json @@ -0,0 +1,64 @@ +{ + "Products": [ + { + "ProductID": 1, + "ProductName": "Chai", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "10 boxes x 20 bags", + "UnitPrice": "18.0000", + "UnitsInStock": 39, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": false + }, + { + "ProductID": 2, + "ProductName": "Chang", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": "19.0000", + "UnitsInStock": 17, + "UnitsOnOrder": 40, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 3, + "ProductName": "Aniseed Syrup", + "SupplierID": 1, + "CategoryID": 2, + "QuantityPerUnit": "12 - 550 ml bottles", + "UnitPrice": "10.0000", + "UnitsInStock": 13, + "UnitsOnOrder": 70, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 4, + "ProductName": "Chef Anton's Cajun Seasoning", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "48 - 6 oz jars", + "UnitPrice": "22.0000", + "UnitsInStock": 53, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": false + }, + { + "ProductID": 5, + "ProductName": "Chef Anton's Gumbo Mix", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "36 boxes", + "UnitPrice": "21.3500", + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": true + } + ] +} \ No newline at end of file diff --git a/packages/databinding/steps/13/webapp/model/data.json b/packages/databinding/steps/13/webapp/model/data.json new file mode 100644 index 000000000..da676ea6b --- /dev/null +++ b/packages/databinding/steps/13/webapp/model/data.json @@ -0,0 +1,13 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "currencyCode": "EUR" +} \ No newline at end of file diff --git a/packages/databinding/steps/13/webapp/view/App.view.xml b/packages/databinding/steps/13/webapp/view/App.view.xml new file mode 100644 index 000000000..d3296cafb --- /dev/null +++ b/packages/databinding/steps/13/webapp/view/App.view.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/databinding/steps/14/README.md b/packages/databinding/steps/14/README.md new file mode 100644 index 000000000..8c2cc0fae --- /dev/null +++ b/packages/databinding/steps/14/README.md @@ -0,0 +1,149 @@ + +# Step 14: Expression Binding + +An expression binding lets you display a calculated value on the screen, which is derived from values found in a model object. This feature allows you to insert simple formatting or calculations directly into the data binding string. In this example, we're changing the color of the price depending on whether it's above or below a certain threshold. The threshold value is stored in the JSON model. + +## Preview + +### Prices are color-coded depending on a selected threshold + +![Prices are color-coded depending on a selected threshold](assets/Tutorial_Data_Binding_Step_14_b9fb758.png "Prices are color-coded depending on a selected threshold") + +You can view this step live: [🔗 Live Preview of Step 14](https://ui5.github.io/tutorials/databinding/build/14/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 14](https://ui5.github.io/tutorials/databinding/databinding-step-14.zip) (TS)[📥 Download step 14](https://ui5.github.io/tutorials/databinding/databinding-step-14-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── controller/ +│ └── App.controller.ts/.js +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ ├── Products.json +│ └── data.json +├── view/ +│ └── App.view.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Add a new property called `priceThreshold` with a value of 20 to the `data.json` file. + +## webapp/model/data.json + +```json +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "priceThreshold": 20, + "currencyCode": "EUR" +} +``` + +In the `App.view.xml` file, add a new `numberState` property to the `ObjectListItem` element within the `List`. The value of this property is an expression that gets evaluated for each item. The expression compares each invoice value against the price threshold and returns a number state based on the result. + +## webapp/view/App.view.xml + +```xml +... + + ... + + + + + + ... + + + + + + ... + +... +``` + +By binding an expression to the `numberState` property, the error status \(color\) of the price field changes depending on the invoice value. + +Consider the following two expressions: + +- `numberState="{= ${products>UnitPrice} > ${/priceThreshold} ? 'Error' : 'Success' }"` + +- `numberState="{= ${products>UnitPrice} <= ${/priceThreshold} ? 'Success' : 'Error' }"` + +Can you see why one of these expressions works, and the other doesn't? + +Logically, both expressions are identical. However, the first one works, and the second one doesn't: it only produces an empty screen and an "Invalid XML" message in the browser's console. So, what's happening here? + +To understand the situation, you need to know how XML files are parsed. + +When an XML file is parsed, certain characters have a special \(high-priority\) meaning to the XML parser. When these characters are encountered, they're **always** interpreted to be part of the XML definition itself, and not as part of any other content within the XML document. + +The XML parser always interprets one of these high-priority characters \(in this case, a less-than \(`<`\) character\) as the start of a new XML tag. This happens regardless of any other meaning that character might have within the context of the expression. This is known as a **syntax collision**. + +In our case, the collision occurs between the syntax of XML and the syntax of the JavaScript-like expression language used by OpenUI5. + +Therefore, this statement fails because the less-than character is interpreted as the start of an XML tag: `numberState="{= ${products>UnitPrice} <= ${/priceThreshold} ? 'Success' : 'Error' }"` + +You can avoid this problem in one of two ways: + +- Reverse the logic of the condition \(use "greater than or equal to" instead of "less than"\) + +- Use the escaped value for the less-than character: `numberState="{= ${products>UnitPrice} <= ${/priceThreshold} ? 'Success' : 'Error' }"` + +Since using an escaped character is not so easy to read, we recommend reversing the logic of the condition and using a greater-than character instead. + +The ampersand \(`&`\) character also has a high-priority meaning to the XML parser. This character always means "The start of an escaped character". So, if you'd like to use the Boolean `AND` operator \(`&&`\) in a condition, you must escape both ampersand characters \(`&&`\). + +*** + +**Next:** [Step 15: Aggregation Binding Using a Factory Function](../15/README.md "Instead of using a single hard-coded template control, we now opt for a factory function to generate different controls based on the data received at runtime.") + +**Previous:** [Step 13: Element Binding](../13/README.md "Now, let's do something with that newly generated list.") + +*** + +**Related Information** + +[Expression Binding](https://sdk.openui5.org/topic/daf6852a04b44d118963968a1239d2c0.html "Expression binding is an enhancement of the OpenUI5 binding syntax, which allows for providing expressions instead of custom formatter functions.") diff --git a/packages/databinding/steps/14/assets/Tutorial_Data_Binding_Step_14_b9fb758.png b/packages/databinding/steps/14/assets/Tutorial_Data_Binding_Step_14_b9fb758.png new file mode 100644 index 0000000000000000000000000000000000000000..f07dea3fa4602e2f6f010445aacdc38ce220b4ba GIT binary patch literal 97490 zcmeFZ1y>zS6EzBiBuH>~cMBfe3GVLh!Ciy9!@=EMPjCpq-QC^Yg5UGp^@im830Kyd zvlx1MrmCxZx~q2Wo=`cNuW&HfFkoO{aN=UZ3SeLmsbFC5IH5j(N{CFi3P69r9TdI_ zf>n;<9)SK4HdYfik(LId2AxBJfd`v|LH<_-^uq@IK(}UtgS`X&1^=%s8{*GZh}7(N zf6m`={#UWSTXF~tj2}!~SU|}Y{5a#iXQDDe-|LZHPsl`G3yyh$95j*~5+i@7aL*~c zE1eQ5l7EaGWPVFa3)hE;$eNnbyH~EQs1`f+A4pqKYZoidXIJTa=~tdvkKVsmeDH#c z_mffeNx+c_-wxj+sh<~|(R~|Y-y#1VKM}LXw?F#+Q?eceDrzNC;YhbyKtR@0^#sm?hmT81NC3L?^N;8`9mrUSK5M6EOhFcU zWp7jeE596Cje9aGDr)oim=rM`Iy7`n+ICmX%z&tpo_=#*?5NNvcgDdKD`S zP&62Li3H-}p1gD^}$%u^d6YK3;g>aJwC|n$B37fAfq!YXlc?H zeyiGl+q%pPnf;pm&S9yNnuW3R=6qt$*|#<7AF~mjL8%R@?V7w-MN2J?7q>LA9>2u? zt3$O9#JKOcf0H@ra=pp!2bwCPzXRY&h_J!^vt$nRdr3=^m;0D=nZM(JkR8,R6? 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%0A5*msgT9Ra96Vi< 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

v3<=J3 zq_&zLJvXd?+hRYX+V&V9Fw(l)r2D4^%I>8yp4T7uIh{T0$m~vfQbL;J`koa*wf20> zYO&h92E7+4rjT=PCEhCOMbZqu^Hv4PWFnaD+_BaI)nRwZ!q0&01doqA1`mKvdp?0p^v>!8* z{A=Z}L-zoqacBXh^v3CN&>iY@rASJHLf^({l80!F`cJtsNQ>Xx|W z)3?*GVDFyq>F~s&9}lxIbJMmBZ}%N}aib1eDULJv+c8P#z6MV@{h&(d%UGh5W+J&k zijaV`t31vp+8zB*=mN-8bhrKyz9ho0+?-{C3K1%Dsm;6MJmHDEapGjjD>C62pk`=9 z|JMT3#t`G0s?;9P?%XA+$6aS-4x24Ey+h^)}%W&rqDDCPAa1pod`e6nRj`tQOI!R~}L(d{b2_i`Y&?hwMZi z8W@30F1OQ=StaIz$e&9g~OTt->-BQC%){oojv~LW8xMo zQJGfbFQKMBC7}Q~LiLB49%bZZQ%S=lJUqL2kMGYG$(G`}m%}GODSqqQb%-B+YTw~`~ly8 zm?NOo%RAhllapj_c@YhSWqlfQ>W>#=#lo05fQ3R z^#|O%r_;5x;xx3P1K@{L5D z<=w}zy`0#$ftQ1DyL_i+9P%J!NTZP63`T_6EjU@DIGg~>8TDxP-9E;KACL@r62uf$ zoSOgU^|_dQVEUuH@^TX+R`biy*cltM8skP1k`<~N)4>VbxeZwU8QZhVoBj z1@AvbHf*NHW#?1>X6ykil+Jh7*8iPaDV103%sYhT3SCmigV?}So-xmJ`}0*16I>6q zB5>QqywO3%-TFi|4T$b6P-me@ii4L|b9gYBiapJh->^YTcSlpn^4iAmV&UO1&_eeZ z=jDf=cn!k@E9ADp|K2iBe9twkiO$y3kVX9Aca1^F>U}?NFUAe&9Gsi4mas>nDSm5< zWPp7PNI!{w zs)51UzC`ivY8^E_3W`$z?|b>AKT8A99kM@HnIF@uL^0s3s+&;3fKo8?>gb%;Z(Y7_ z>c|Q*p#zoNK%IBK61q$=ms~t`nFEBrY@H-~`74FYL>1+IiNDwd*o2<~Xi4!0EwJzK zQBwa7A-OUK@c5c8Ww}WI^LY9Ow3b^p2E?@g{zX##77fDC{q0}|)C+-w*#A8O`0)SY zgUpwZU7)goI`ARAjRZDEf9@7wvlEBWDI5Lp)c3yP1fkOI;SG&Cx#I40aV&EHz&x65 z*vbH4U&2kieA)7lQvjMb8+!4yi7dKQ7}iM6#;Z&V6CVi&p_LINS@vW#3a{lO!ewOg zIFC_9Vx+M~YF0=|bxw~o{JJms=Umz>B7CU+mBM1}7Ya1*sI~-AiI|FuclF1&%Eth1 z3+amX=g~m#lNb}GD7{qezQtyqz=2lO&#xj<<50vO8CORuphtTQ_Hm^#q|g1B6RyiDw0xHQ5$3 zE&5ZIvt)#aRvHAXG+bKkWLZPJ00JSfC?sSj9q1@kVT%x*ltc;r0*2S!Twf<&T1tZ! zm(>i7cjz7-P73MhvTC#ZFkB<2V0`oYYrZhk!2Ha~MqCjc`$)7$-b&UX}jH2JZYJf(l=Cw_V*&iF}diNzd>HC1R z(U;KB>szOK8|HRR5VJm~hQ|0&7s*48FNo1n+s-by>FVGsO_?FDjRdEZq-?aa%=Ewl z#U2Gcsq+1#-*T?4rD45;lYPu@->1`>iOk*RMfC`GK?d~NPvWk~c3yH=O<%aaf- zuQppTO|6r;VcqOu#L>S`GRc)?)LskRJ5Dm)9!si1x_g~oF`R|gMl^{64&f>Q6cL>f z*8o1#cx`}7a=e+}>upM>R(vdwwy_iW9%7RFu9m&~aes@3#bizx0ReHuad0hfWPnQG zfh0+Gq|>*a;ij0Da~WW5j>e6Cp%d$^fgN;Fbx3rsXXeC$n9ywYjd;3 z%kiM}X4em7=UndoK^&nWz zgAc?^{EqQ&r+u@FE&V8I+iq{$e>h25%1H_%o?d;gZ6&0lUXd^*XklEp8?-`NrZB(i&#v~(`LM3b{a?+P5l?Bi}DY`8xVNvnE$qyQVna-+DJH= z&I;BX@*B~IFx#HIC?cSDW@kG@D>dw7X~|#?NA)vBbs}=(R8qH|%OS1QqVz8hv|4fy z6c&bX!LG3iv6leM3fz6#>RJT=8Eza0kz)3Jlx4j9?$1$)o0zP@ z=phyh$Hp?4-)qxwsAW_qNWr@S2lhra{#Z?H>x^WmaWn(sZ9+PIyR3GsgKgur)$7=OPRGS>t=<=GUs`VT&s9w|UJsV=1F6af|k#d)=A>!<6`ILPC-FaDz7HE-*m^x zUb~=#BD&^?&wxJotIH?=vT-QgF;N@5`A&5hWrpcV0IyfRA_mV_pYR<9-!E0wR{SAi+mnml_W=$&z#E1US?hMpN9glXTg6c z&}>*|Yq68;#3pUz7O0iuxeNi?_DR`QY~mEzCjSF*+7j*(nTCz0HDM{P!V;{#-2%3b z@cO-}f$-3~=TjfZ57a3_IZ#{djUV9ZosahCch_at4y;U_dDS`HMv=dYDa z*)>1mnMTV`b>_TfC_7)r(@&OAn6m4=LKdqn@qV ziloF=W2?xUx(XW5;a|R=yoj=6|gxAK<+_N!hA6ySRFVuw%i8JZ{4L_a4zH z(*_4>{qZ!^0Yhu#e_S&*9|51q8@p4trslz_x!;cbjnsDvJH&Zm-Iki!T&;3n<2oEqQ@GGANZf> z_h;NQzg!07mcOnpAR;sYLftYP={Ns{k$sc_=>BnVe>~y97{G&(6`1!l62&XhKfeTs ziOVSjGr=-G|Jm1pI35S^$C&#^zx?kcXMpgzI;ZUqe)OM7PXG4xu#5D0{rjQ>e6T>M zoAa=#zKR;HKu?dPE@fmy-#(%PSYP3e@Qzz}*y+Th6(`WcP1;8fe#bkVB0n0M0v6X3 z5RDd_V$xLENo|LCPdCmI(9qHGuDhEea2tGld0yT~h=_q6V>BG?fUZ-S&sLz#Fz?}9 z|6&>$6Egk?`#$A8{ zG4!n!PWt`9UyZ6)c;RmEf~xEvP$S z*X~+DA1g9M1Zy@*x1>DGM!v`Fl2UGwJ+at&<;Arzf>~*`rw4>T*hEYUG(QZAUGGf9 zBZWje&C)UQMr7*8C{)Sojd|4NHL|m_dk4kLvt4pl@DYZHJk#m6L^?LQdJdz7Tb2?X zG0?F{fQ?M3Pz@jTCE1hqAzBXl2&_DbC))uy~(`R_4FW!md%+;lVnD5DD=RmA($fiNO+_shH^} zyttvJr@gz>(l8FKhLx^>9v;5&T0y0k{&sygf{ekDEC)}LP0v*;=jgOPoO&cb!`dnb zE=vAV)_6q}m+3hiDTP|gJMKygBJyzi=Rp=pMKfid^%UH--~k`%LOaWaeT!n z+-t$(6LWDgCp$=MKfrq*U56sM_B%Q*Y;X zpr|mu&?qs$nQby}MZ1||*>jjbRGh82%D`!b&{t`oK#)wX5)vnE;a}-~x;yXLfApO&7&Qi)xmVSy znlaEEc3*2rz7Umo$AY5>3vsr zvno~s4qNd@4|s{EC8d0;QL5cS-?mCw){Y~;wDfK_5;SZ_Iswcd&fB-9zB?EQIrkyg zDs2uv%q0q12d3U9W}})_cDYeoGVB;mw^BP<#ngcpsdA`se=?)d*44;9H15P?dRFpAmK&GLiB)fR;Xa z64|c6w>0R!_N00*zxs=M=6v>a?iy8gL?`MXX*ZTdW2AmZ0!lO(dYnO4?5b-00M(jDW~W}L7?12Oc>H~8O^NB4VfwO;D<|6L&I8cPezhZX}u_(p}bNc z4Nl(|e0 z8XT3*$X{%@#bKwhhp}&j0B~S*a4huR5E-#rA;n-v&=}$Aq*w(My;r8v%mU9R5x4i)6ossc~;BYL%V7QqLa9NUagVFxa|8`>$Y)jHcDrQaDvWiYaRcU@|@zJX@87|ud> zmAn9tntyS+qa<5WYW@LXex%QmNWM^A$iDU!HI<-4ijN-E)KovOdb&RDDe+g?F%RQso1~?K2 zi0ZPcsG7f>pKWUhX)@+g%Z<;66y@W_h7h+n6(-v^tK0x$ggS-#(6Ah~>0g9`( zwoB1Bb!=)0J}DbM2tkJtSN%@EETNpqq;7H?+uQ3}q@n>z@+!O5m#5RdSQfCgGi4S= zSMmi;no3SnOF4g^#KWEL2(wy~RcUol>-_@Pc^5PfHvCVGrQkQY87S|ov=Iq-B(z`F zZAcOctNcn-#L?`la{3PLkTO4Me7o=wEAag!ctoI?S;^y?`CetCL3dgE?zYgD(e&ER z^}6#5puv$&KJ$wB6d&E)$90CRyPt(UsAeq zg`#aOww^B@2*4UoV^)Of^pAcziw{&ZSz82wE?e`ipKpgeycj@Gg1ZHiX`h zsaDP(Zha5a(|9X2E3YMPO>8$scy22V^Tx*!=dA3^&0{zxEsnbp>94?Y$0%P%aR520 zE+K}e=Lh)E(iGHV;nAmZHSZ%e$fm>W9{c3!37l?M#@JEPA#ZC8NTU4%YKMJvqei zt2Od!mDxCHZub`PK+MjVgmF?uU+M@3uB-@SHkW+ZWp$2=O8IG3J%B6h0NhR#|UR1~ti9o)AdAA@c%!Y=?m17EK z^eeiA>+09^07rCP^eWy&Wx8bNke5v%PpR*;xBMZ*QD=9Zt&VNPQn#IKBou?t^oT>z z*CsYhK2l?3(KiO z&1`G3BKU3=k|D%Age7s9%686e4yJ_O*+SU2> zvEDFx6BscVZ07hxu^IZOE2=qGtMyVnCdbpfZBhlhxBPDqQhEY8=`|FOCA#xY9Cy~G zl(IJWW>Q39*GRi>$76roA}oj5v{yW?Ruk!_wyj_*Lg8=e9C4lJ&=Ps!>ZuIQ^$0rw zHcy!H&o=p6ebj^GDKKpO)zz4zA5;^+ECZJyt|-pO{TuVMRI;Zls7sR<*73-((!M5#N z!#tjq^&$g{53eHgdt?MuJJ()14eWC%++Qbhw7qxC@5}pwd|<;5=?ZR?u=Um<;Xs>B z4;3VQcq`|-^zCCVvw*&0zlqahw^}?!*Yl{7iS+b+T2&Mub0-9}ut^93-t+v2xcKQt zM-s|{ex3Ze5uO5C#zVYk`M&zix0~qI`EFfC7Z5`@6`Om|SFHSSb}-5YWx_4T?X!ey zx@2_jgS}CN--{M;$hPGPM4$}Pj{V>Yj)dtOQ4*_zmpSV zHHGI(mh;b03r-%OnvsoIbtg5HN7t+)ZD^N;M+rYqAvC6WHum z<}7D}W}j6Q@nJq5Si@zr6)E)sBG5y4x?jLk3_*-!F*Ia;QvxBQg8qTX)g6bIZoMoK zL10})<_^tGmszSr)|0b4b@ooPQnpj(Ij;b&h4pCF z?uA`b^jAFr>Y>te9N_WEuR^Dl_HANl<+4bL|KM2|QyZ+llHcm}VJ;G(iz+Y=yT5d9 zk;A^7t8~ph^L?jcLq_`}DhM*Nz|nj_n$|f$J+11S8Zq?d^nENd?jb#mzx$-w zN8Z(3OD4WZ34;t0eLT>cJ=%4`Cl)%?d@Vq)*&Po9zjOo32>We5f-<2&_RD#{L!6MM zPSFQqQ~$8i#wGe5azU0@dqVJ6F*n)T?#W+5-*!mI$5=!8SD70N%E|Y>a#olb3WcUA zscW2B7{tGuON~N~wdp0fgLcGhuCppL`WTa+ni7TV%f_+~y#bMUb+fkoCE51*v7bj? zJJ4)LGR#V#EURt`Nxu-MGYIudqL1%q=zg94ab}43fn9POu_t0n@$&$qc_yA0@sna?+JpcRbRFY$Xx>Zcc6<=7fh))v=hceL-%ysKnX?8iV3 zWg%~bHy&zH82S>T*o7MD;+^dlF6Dcwe<_kH8j3b+U*Ytwzq4ZgiYElqBm2~heclU` z-afRH5WWUBY5Y_^9ohs+WMMvTYIlg-N)E9U#x8F+qGQ=9IWTXe`&%H&_fewbIFc=> zC2n!Qds9ENF@3S#74%`Ql>B~yALio>mEa&rNC_dWz=hz;@^r>K$a%7!5ky+4x|d(J zl=;fUq}?4$0rXxl?X=A|r5&)*Zf3TCD0z$k=K>txOjW?Led%0)ASDUudR8*W5F!yQ z1T1|>aSvw8jTUlun z-qVVy`zZzWM`n}*@SJ0hn`%ll_BT?ro>R-UYMn!0d{#P%85N6C>>nmoKC^S@94

  • Y2sT2q6v7jj>M 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 TI;$LzkzV@RCR)`Rb`k#rB2V!~ literal 0 HcmV?d00001 diff --git a/packages/databinding/steps/14/package.json b/packages/databinding/steps/14/package.json new file mode 100644 index 000000000..90998b0be --- /dev/null +++ b/packages/databinding/steps/14/package.json @@ -0,0 +1,19 @@ +{ + "name": "ui5.tutorial.databinding.step14", + "private": true, + "version": "1.0.0", + "author": "SAP SE", + "description": "UI5 Demo App - Data Binding Tutorial", + "scripts": { + "start": "ui5 serve -o index.html", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@openui5/types": "^1.148.1", + "@ui5/cli": "^4.0.53", + "typescript": "^6.0.3", + "ui5-middleware-livereload": "^3.3.1", + "ui5-middleware-serveframework": "^3.8.1", + "ui5-tooling-transpile": "^3.11.0" + } +} diff --git a/packages/databinding/steps/14/tsconfig.json b/packages/databinding/steps/14/tsconfig.json new file mode 100644 index 000000000..0b7fa1ae7 --- /dev/null +++ b/packages/databinding/steps/14/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2023", + "types": [ + "@openui5/types" + ], + "skipLibCheck": true, + "allowJs": true, + "strictPropertyInitialization": false, + "rootDir": "./webapp", + "paths": { + "ui5/tutorial/databinding/*": [ + "./webapp/*" + ] + }, + "strict": false, + "strictNullChecks": false + }, + "exclude": [ + "./webapp/test/e2e/**/*" + ], + "include": [ + "./webapp/**/*" + ] +} diff --git a/packages/databinding/steps/14/ui5.yaml b/packages/databinding/steps/14/ui5.yaml new file mode 100644 index 000000000..af70be177 --- /dev/null +++ b/packages/databinding/steps/14/ui5.yaml @@ -0,0 +1,24 @@ +specVersion: "4.0" +metadata: + name: "ui5.tutorial.databinding" +type: application +framework: + name: OpenUI5 + version: "1.148.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.layout + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion +server: + customMiddleware: + - name: ui5-tooling-transpile-middleware + afterMiddleware: compression + - name: ui5-middleware-serveframework + afterMiddleware: compression + - name: ui5-middleware-livereload + afterMiddleware: compression diff --git a/packages/databinding/steps/14/webapp/Component.ts b/packages/databinding/steps/14/webapp/Component.ts new file mode 100644 index 000000000..3fc494bca --- /dev/null +++ b/packages/databinding/steps/14/webapp/Component.ts @@ -0,0 +1,11 @@ +import UIComponent from "sap/ui/core/UIComponent"; + +/** + * @namespace ui5.tutorial.databinding + */ +export default class Component extends UIComponent { + public static metadata = { + interfaces: ["sap.ui.core.IAsyncContentCreation"], + manifest: "json" + }; +} diff --git a/packages/databinding/steps/14/webapp/controller/App.controller.ts b/packages/databinding/steps/14/webapp/controller/App.controller.ts new file mode 100644 index 000000000..97c2a9cb8 --- /dev/null +++ b/packages/databinding/steps/14/webapp/controller/App.controller.ts @@ -0,0 +1,32 @@ +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import { ListItemBase$DetailPressEvent } from "sap/m/ListItemBase"; +import ObjectListItem from "sap/m/ObjectListItem"; +import Controller from "sap/ui/core/mvc/Controller"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; +import Currency from "sap/ui/model/type/Currency"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + public async formatMail(firstName: string, lastName: string): Promise { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } + + public formatStockValue(unitPrice: number, stockLevel: number, currencyCode: string): string { + const currency: Currency = new Currency(); + return currency.formatValue([unitPrice * stockLevel, currencyCode], "string"); + } + + public onItemSelected(event: ListItemBase$DetailPressEvent): void { + const bindingPath: string = (event.getSource() as ObjectListItem).getBindingContext("products").getPath(); + this.byId("productDetailsPanel")?.bindElement({ path: bindingPath, model: "products" }); + } +} diff --git a/packages/databinding/steps/14/webapp/i18n/i18n.properties b/packages/databinding/steps/14/webapp/i18n/i18n.properties new file mode 100644 index 000000000..da1880d42 --- /dev/null +++ b/packages/databinding/steps/14/webapp/i18n/i18n.properties @@ -0,0 +1,33 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled +address=Address +salesAmount=Sales Amount + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details +panel3HeaderText=Aggregation Binding +panel4HeaderText=Product Details + +# Email +sendEmail=Send EMail +mailSubject=Hi {0}! +mailBody=How are you? + +# Product list +productListTitle=Product List +stockValue=Current Stock Value + +# Product Details +ProductID=Product ID +ProductName=Product Name +QuantityPerUnit=Quantity per Unit +UnitPrice=Unit Price +UnitsInStock=Number of Units in Stock +Discontinued=Discontinued diff --git a/packages/databinding/steps/14/webapp/i18n/i18n_de.properties b/packages/databinding/steps/14/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..73b5af5d6 --- /dev/null +++ b/packages/databinding/steps/14/webapp/i18n/i18n_de.properties @@ -0,0 +1,33 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse +salesAmount=Verk\u00e4ufe bis zum heutigen Datum + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails +panel3HeaderText=Aggregation Binding +panel4HeaderText=Produktdetails + +# Email +sendEmail=E-Mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? + +# Product list +productListTitle=Artikelliste +stockValue=Lagerbestand Wert + +# Product Details +ProductID=Produkt-ID +ProductName=Produktname +QuantityPerUnit=Menge pro Einheit +UnitPrice=Preis pro Einheit +UnitsInStock=Lagerbestand +Discontinued=Eingestellt diff --git a/packages/databinding/steps/14/webapp/index-cdn.html b/packages/databinding/steps/14/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/14/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
    + + diff --git a/packages/databinding/steps/14/webapp/index.html b/packages/databinding/steps/14/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/14/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
    + + diff --git a/packages/databinding/steps/14/webapp/manifest.json b/packages/databinding/steps/14/webapp/manifest.json new file mode 100644 index 000000000..3852b19e3 --- /dev/null +++ b/packages/databinding/steps/14/webapp/manifest.json @@ -0,0 +1,70 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "handleValidation": true, + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "products": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/Products.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/14/webapp/model/Products.json b/packages/databinding/steps/14/webapp/model/Products.json new file mode 100644 index 000000000..2b47ed742 --- /dev/null +++ b/packages/databinding/steps/14/webapp/model/Products.json @@ -0,0 +1,64 @@ +{ + "Products": [ + { + "ProductID": 1, + "ProductName": "Chai", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "10 boxes x 20 bags", + "UnitPrice": "18.0000", + "UnitsInStock": 39, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": false + }, + { + "ProductID": 2, + "ProductName": "Chang", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": "19.0000", + "UnitsInStock": 17, + "UnitsOnOrder": 40, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 3, + "ProductName": "Aniseed Syrup", + "SupplierID": 1, + "CategoryID": 2, + "QuantityPerUnit": "12 - 550 ml bottles", + "UnitPrice": "10.0000", + "UnitsInStock": 13, + "UnitsOnOrder": 70, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 4, + "ProductName": "Chef Anton's Cajun Seasoning", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "48 - 6 oz jars", + "UnitPrice": "22.0000", + "UnitsInStock": 53, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": false + }, + { + "ProductID": 5, + "ProductName": "Chef Anton's Gumbo Mix", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "36 boxes", + "UnitPrice": "21.3500", + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": true + } + ] +} \ No newline at end of file diff --git a/packages/databinding/steps/14/webapp/model/data.json b/packages/databinding/steps/14/webapp/model/data.json new file mode 100644 index 000000000..9682b2cd2 --- /dev/null +++ b/packages/databinding/steps/14/webapp/model/data.json @@ -0,0 +1,14 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "priceThreshold": 20, + "currencyCode": "EUR" +} \ No newline at end of file diff --git a/packages/databinding/steps/14/webapp/view/App.view.xml b/packages/databinding/steps/14/webapp/view/App.view.xml new file mode 100644 index 000000000..664c62380 --- /dev/null +++ b/packages/databinding/steps/14/webapp/view/App.view.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/databinding/steps/15/README.md b/packages/databinding/steps/15/README.md new file mode 100644 index 000000000..12a36f2d2 --- /dev/null +++ b/packages/databinding/steps/15/README.md @@ -0,0 +1,249 @@ + +# Step 15: Aggregation Binding Using a Factory Function + +Instead of using a single hard-coded template control, we now opt for a factory function to generate different controls based on the data received at runtime. This approach is much more flexible and allows for the display of complex or heterogeneous data. + +## Preview + +### A different type of list item is displayed for a discontinued product + +![A different type of list item is displayed for a discontinued product](assets/Tutorial_Data_Binding_Step_15_db27ba8.png "A different type of list item is displayed for a discontinued product") + +You can view this step live: [🔗 Live Preview of Step 15](https://ui5.github.io/tutorials/databinding/build/15/index-cdn.html). + +## Coding + +You can download the solution for this step here: [📥 Download step 15](https://ui5.github.io/tutorials/databinding/databinding-step-15.zip) (TS)[📥 Download step 15](https://ui5.github.io/tutorials/databinding/databinding-step-15-js.zip) (JS). + +### Folder structure for this step + +```text +webapp/ +├── controller/ +│ └── App.controller.ts/.js +├── i18n/ +│ ├── i18n.properties +│ └── i18n_de.properties +├── model/ +│ ├── Products.json +│ └── data.json +├── view/ +│ ├── App.view.xml +│ ├── ProductExtended.fragment.xml +│ └── ProductSimple.fragment.xml +├── Component.ts/.js +├── index.html +└── manifest.json +``` + +Create a `ProductSimple.fragment.xml` file in the `view` folder. Here, define an `sap.m.StandardListItem` that is used when the stock level is zero and the product is discontinued. In this simple use case, you only need to define a warning icon and a "Product Discontinued" message in the `info` property. + +## webapp/view/ProductSimple.fragment.xml \(New\) + +```xml + + + + +``` + +Create a new `ProductExtended.fragment.xml` file in the `view` folder. In this extended use case, you create an `ObjectListItem` to display more product details. The properties are bound to the fields of the current data binding context. This allows the use of types, formatters, and all handlers defined in the assigned controller. However, you can't define more complex logic declaratively in XML. Therefore, we add a single `sap.m.ObjectAttribute` in a factory function of the controller using JavaScript, which displays an "Out of Stock" message when the stock level is zero. + +## webapp/view/ProductExtended.fragment.xml \(New\) + +```xml + + + + +``` + +In the `App.view.xml` file, add an XML namespace for `sap.ui.core`. Then, remove the `items` aggregation from the `sap.m.List` XML element. Add an `id` attribute to the `sap.m.List` and include the factory function in the items' binding definition. Lastly, add the two newly created fragments as dependents to the `sap.m.List`. + +## webapp/view/App.view.xml + +```xml + + ... + + + + + + + + + ... + +``` + +The `sap.m.List` that previously held the product list is now just a named, but otherwise empty placeholder. Without a factory function to populate it, this `List` would always remain empty. As the fragments are declared as dependents, they also inherit the controller of the view. This means that the `onItemSelect` function of the `App.controller.ts/.js` can still be used in the `ProductExtended.fragment.xml`. + +In the `App.controller.ts/.js` file, add a new import for the `sap.m.ObjectAttribute` class and create a new function called `productListFactory`. This factory function returns a control for the associated binding context, similar to the XML templates we've defined in the previous steps. The controls returned by this factory function must suit the items aggregation of the `sap.m.List` object. In this case, it returns either an `sap.m.StandardListItem` or an `sap.m.ObjectListItem` based on the data stored in the context of the item to be created. + +## `webapp/controller/App.controller.ts/.js` + +```ts +// webapp/controller/App.controller.ts +import ResourceBundle from "sap/base/i18n/ResourceBundle"; +import { URLHelper } from "sap/m/library"; +import { ListItemBase$DetailPressEvent } from "sap/m/ListItemBase"; +import ObjectAttribute from "sap/m/ObjectAttribute"; +import ObjectListItem from "sap/m/ObjectListItem"; +import StandardListItem from "sap/m/StandardListItem"; +import Control from "sap/ui/core/Control"; +import Controller from "sap/ui/core/mvc/Controller"; +import Context from "sap/ui/model/Context"; +import ResourceModel from "sap/ui/model/resource/ResourceModel"; +import Currency from "sap/ui/model/type/Currency"; + +/** + * @namespace ui5.tutorial.databinding.controller + */ +export default class App extends Controller { + ... + + public productListFactory(id: string, context: Context): Control { + let uiControl; + // Decide based on the data which dependent to clone + if (context.getProperty("UnitsInStock") === 0 && context.getProperty("Discontinued")) { + // The item is discontinued, so use a StandardListItem + uiControl = (this.byId("productSimple") as StandardListItem).clone(id); + } else { + // The item is available, so we will create an ObjectListItem + uiControl = (this.byId("productExtended") as ObjectListItem).clone(id); + + // The item is temporarily out of stock, so we will add a status + if (context.getProperty("UnitsInStock") < 1) { + uiControl.addAttribute(new ObjectAttribute({ + text : { + path: "i18n>outOfStock" + } + })); + } + } + + return uiControl; + } +} +``` + +```js +// webapp/controller/App.controller.js +sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/library", "sap/m/ObjectAttribute", "sap/ui/model/type/Currency"], function (Controller, mobileLibrary, ObjectAttribute, Currency) { + "use strict"; + + return Controller.extend("ui5.tutorial.databinding.controller.App", { + ... + + productListFactory(id, context) { + let uiControl; + // Decide based on the data which dependent to clone + if (context.getProperty("UnitsInStock") === 0 && context.getProperty("Discontinued")) { + // The item is discontinued, so use a StandardListItem + uiControl = this.byId("productSimple").clone(id); + } else { + // The item is available, so we will create an ObjectListItem + uiControl = this.byId("productExtended").clone(id); + + // The item is temporarily out of stock, so we will add a status + if (context.getProperty("UnitsInStock") < 1) { + uiControl.addAttribute(new ObjectAttribute({ + text : { + path: "i18n>outOfStock" + } + })); + } + } + + return uiControl; + } + }); +}); +``` + +The function decides which type of control to return by checking the current stock level and whether the product is discontinued. For both options, it loads and clones the respective XML fragment, which allows the view logic to be defined dynamically. If the stock level is zero and the product is discontinued, the `ProductSimple` XML fragment is used. Otherwise, the `ProductExtended` XML fragment is used. + +For each item of the list, the corresponding control is cloned. This method creates a fresh copy of a control that can be bound to the context of the list item. Remember, in a factory function you are responsible for the life cycle of the control you create. + +If the product is not discontinued but the stock level is zero, we're temporarily out of stock. In this case, a single `sap.m.ObjectAttribute` is added to the cloned control. The "Out of Stock" message is bound to the `sap.m.ObjectAttribute`'s `text` property using JavaScript. Like declarative definitions in the XML view or fragments, you can bind properties using data binding syntax. Here, the text is bound to an entry in the resource bundle. Since the `sap.m.ObjectAttribute` is a child of the list item, it has access to all assigned models and the current binding context. + +Finally, the function returns the control that is then displayed inside the list. + +Lastly, add the new texts to the `i18n.properties` and `i18n_de.properties` files. + +## webapp/i18n/i18n.properties + +```properties +... +# Product Details +... +outOfStock=Out of Stock +``` + +## webapp/i18n/i18n\_de.properties + +```properties +... +# Product Details +... +outOfStock=Nicht vorr\u00e4tig +``` + +Congratulations! You've completed the Data Binding tutorial. + +*** + +**Previous:** [Step 14: Expression Binding](../14/README.md "An expression binding lets you display a calculated value on the screen, which is derived from values found in a model object.") + +*** + +**Related Information** + +[List Binding \(Aggregation Binding\)](https://sdk.openui5.org/topic/91f057786f4d1014b6dd926db0e91070.html "List binding (or aggregation binding) is used to automatically create child controls according to model data.") + +[XML Fragments](https://sdk.openui5.org/topic/2c677b574ea2486a8d5f5414d15e21c5.html "XML fragments are similar to XML view, but have no tag as root element. Instead, there is an OpenUI5 control.") + +[Using Factory Functions](https://sdk.openui5.org/topic/335848ac1174435c901baaa55f6d7819.html) diff --git a/packages/databinding/steps/15/assets/Tutorial_Data_Binding_Step_15_db27ba8.png b/packages/databinding/steps/15/assets/Tutorial_Data_Binding_Step_15_db27ba8.png new file mode 100644 index 0000000000000000000000000000000000000000..c19c3e3deb2a979eafce2158aac22565f4f25305 GIT binary patch literal 73445 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 P0SG)@{an^LB{Ts5 { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } + + public formatStockValue(unitPrice: number, stockLevel: number, currencyCode: string): string { + const currency: Currency = new Currency(); + return currency.formatValue([unitPrice * stockLevel, currencyCode], "string"); + } + + public onItemSelected(event: ListItemBase$DetailPressEvent): void { + const bindingPath: string = (event.getSource() as ObjectListItem).getBindingContext("products").getPath(); + this.byId("productDetailsPanel")?.bindElement({ path: bindingPath, model: "products" }); + } + + public productListFactory(id: string, context: Context): Control { + let uiControl; + // Decide based on the data which dependent to clone + if (context.getProperty("UnitsInStock") === 0 && context.getProperty("Discontinued")) { + // The item is discontinued, so use a StandardListItem + uiControl = (this.byId("productSimple") as StandardListItem).clone(id); + } else { + // The item is available, so we will create an ObjectListItem + uiControl = (this.byId("productExtended") as ObjectListItem).clone(id); + + // The item is temporarily out of stock, so we will add a status + if (context.getProperty("UnitsInStock") < 1) { + uiControl.addAttribute(new ObjectAttribute({ + text : { + path: "i18n>outOfStock" + } + })); + } + } + + return uiControl; + } +} diff --git a/packages/databinding/steps/15/webapp/i18n/i18n.properties b/packages/databinding/steps/15/webapp/i18n/i18n.properties new file mode 100644 index 000000000..d5dfa8b7f --- /dev/null +++ b/packages/databinding/steps/15/webapp/i18n/i18n.properties @@ -0,0 +1,34 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled +address=Address +salesAmount=Sales Amount + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details +panel3HeaderText=Aggregation Binding +panel4HeaderText=Product Details + +# Email +sendEmail=Send EMail +mailSubject=Hi {0}! +mailBody=How are you? + +# Product list +productListTitle=Product List +stockValue=Current Stock Value + +# Product Details +ProductID=Product ID +ProductName=Product Name +QuantityPerUnit=Quantity per Unit +UnitPrice=Unit Price +UnitsInStock=Number of Units in Stock +Discontinued=Discontinued +outOfStock=Out of Stock diff --git a/packages/databinding/steps/15/webapp/i18n/i18n_de.properties b/packages/databinding/steps/15/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..c9d2da542 --- /dev/null +++ b/packages/databinding/steps/15/webapp/i18n/i18n_de.properties @@ -0,0 +1,34 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse +salesAmount=Verk\u00e4ufe bis zum heutigen Datum + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails +panel3HeaderText=Aggregation Binding +panel4HeaderText=Produktdetails + +# Email +sendEmail=E-Mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? + +# Product list +productListTitle=Artikelliste +stockValue=Lagerbestand Wert + +# Product Details +ProductID=Produkt-ID +ProductName=Produktname +QuantityPerUnit=Menge pro Einheit +UnitPrice=Preis pro Einheit +UnitsInStock=Lagerbestand +Discontinued=Eingestellt +outOfStock=Nicht vorr\u00e4tig diff --git a/packages/databinding/steps/15/webapp/index-cdn.html b/packages/databinding/steps/15/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/15/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
    + + diff --git a/packages/databinding/steps/15/webapp/index.html b/packages/databinding/steps/15/webapp/index.html new file mode 100644 index 000000000..42edae4b3 --- /dev/null +++ b/packages/databinding/steps/15/webapp/index.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +
    + + diff --git a/packages/databinding/steps/15/webapp/manifest.json b/packages/databinding/steps/15/webapp/manifest.json new file mode 100644 index 000000000..3852b19e3 --- /dev/null +++ b/packages/databinding/steps/15/webapp/manifest.json @@ -0,0 +1,70 @@ +{ + "_version": "2.8.0", + "sap.app": { + "id": "ui5.tutorial.databinding", + "type": "application", + "applicationVersion": { + "version": "1.0.0" + }, + "title": "{{appTitle}}", + "description": "{{appDescription}}", + "i18n": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "" + } + }, + "sap.ui": { + "technology": "UI5", + "deviceTypes": { + "desktop": true, + "tablet": true, + "phone": true + } + }, + "sap.ui5": { + "handleValidation": true, + "dependencies": { + "minUI5Version": "1.148", + "libs": { + "sap.m": {}, + "sap.ui.core": {}, + "sap.ui.layout": {} + } + }, + "contentDensities": { + "compact": true, + "cozy": true + }, + "models": { + "": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/data.json" + }, + "products": { + "type": "sap.ui.model.json.JSONModel", + "uri": "./model/Products.json" + }, + "i18n": { + "type": "sap.ui.model.resource.ResourceModel", + "settings": { + "bundleName": "ui5.tutorial.databinding.i18n.i18n", + "supportedLocales": [ + "", + "de" + ], + "fallbackLocale": "", + "async": true + } + } + }, + "rootView": { + "viewName": "ui5.tutorial.databinding.view.App", + "type": "XML", + "id": "app" + } + } +} diff --git a/packages/databinding/steps/15/webapp/model/Products.json b/packages/databinding/steps/15/webapp/model/Products.json new file mode 100644 index 000000000..8df5acf34 --- /dev/null +++ b/packages/databinding/steps/15/webapp/model/Products.json @@ -0,0 +1,64 @@ +{ + "Products": [ + { + "ProductID": 1, + "ProductName": "Chai", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "10 boxes x 20 bags", + "UnitPrice": "18.0000", + "UnitsInStock": 39, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": false + }, + { + "ProductID": 2, + "ProductName": "Chang", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": "19.0000", + "UnitsInStock": 17, + "UnitsOnOrder": 40, + "ReorderLevel": 25, + "Discontinued": true + }, + { + "ProductID": 3, + "ProductName": "Aniseed Syrup", + "SupplierID": 1, + "CategoryID": 2, + "QuantityPerUnit": "12 - 550 ml bottles", + "UnitPrice": "10.0000", + "UnitsInStock": 0, + "UnitsOnOrder": 70, + "ReorderLevel": 25, + "Discontinued": false + }, + { + "ProductID": 4, + "ProductName": "Chef Anton's Cajun Seasoning", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "48 - 6 oz jars", + "UnitPrice": "22.0000", + "UnitsInStock": 53, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": false + }, + { + "ProductID": 5, + "ProductName": "Chef Anton's Gumbo Mix", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "36 boxes", + "UnitPrice": "21.3500", + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": true + } + ] +} \ No newline at end of file diff --git a/packages/databinding/steps/15/webapp/model/data.json b/packages/databinding/steps/15/webapp/model/data.json new file mode 100644 index 000000000..9682b2cd2 --- /dev/null +++ b/packages/databinding/steps/15/webapp/model/data.json @@ -0,0 +1,14 @@ +{ + "firstName": "Harry", + "lastName": "Hawk", + "enabled": true, + "address": { + "street": "Dietmar-Hopp-Allee 16", + "city": "Walldorf", + "zip": "69190", + "country": "Germany" + }, + "salesAmount": 12345.6789, + "priceThreshold": 20, + "currencyCode": "EUR" +} \ No newline at end of file diff --git a/packages/databinding/steps/15/webapp/view/App.view.xml b/packages/databinding/steps/15/webapp/view/App.view.xml new file mode 100644 index 000000000..971008de7 --- /dev/null +++ b/packages/databinding/steps/15/webapp/view/App.view.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/databinding/steps/15/webapp/view/ProductExtended.fragment.xml b/packages/databinding/steps/15/webapp/view/ProductExtended.fragment.xml new file mode 100644 index 000000000..37e5613c6 --- /dev/null +++ b/packages/databinding/steps/15/webapp/view/ProductExtended.fragment.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/packages/databinding/steps/15/webapp/view/ProductSimple.fragment.xml b/packages/databinding/steps/15/webapp/view/ProductSimple.fragment.xml new file mode 100644 index 000000000..adbd5c104 --- /dev/null +++ b/packages/databinding/steps/15/webapp/view/ProductSimple.fragment.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/packages/navigation/steps/02/README.md b/packages/navigation/steps/02/README.md index 2d814a4a8..c390a28e2 100644 --- a/packages/navigation/steps/02/README.md +++ b/packages/navigation/steps/02/README.md @@ -8,9 +8,11 @@ In this step we will modify the app and introduce routing. Instead of having the ![Views are wired together using the router](assets/Tutorial_Navigation_and_Routing_Step_02a.png "Views are wired together using the router") +You can view this step live: [🔗 Live Preview of Step 2](https://ui5.github.io/tutorials/navigation/build/02/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 2](https://ui5.github.io/tutorials/navigation/build/02/index-cdn.html). +You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/navigation/navigation-step-02.zip) (TS)[📥 Download step 2](https://ui5.github.io/tutorials/navigation/navigation-step-02-js.zip) (JS). ### Folder structure for this step @@ -235,7 +237,6 @@ Create a file `Home.controller.ts` in the `webapp/controller` folder. The contro *** - *** **Next:** [Step 3: Catch Invalid Hashes](../03/README.md) diff --git a/packages/navigation/steps/03/README.md b/packages/navigation/steps/03/README.md index 9e2c62e67..3f8395045 100644 --- a/packages/navigation/steps/03/README.md +++ b/packages/navigation/steps/03/README.md @@ -8,9 +8,11 @@ Sometimes it is important to display an indication that the requested resource w ![Not Found page](assets/Tutorial_Navigation_and_Routing_Step_03.png "Not Found page") +You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io/tutorials/navigation/build/03/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io/tutorials/navigation/build/03/index-cdn.html). +You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/navigation/navigation-step-03.zip) (TS)[📥 Download step 3](https://ui5.github.io/tutorials/navigation/navigation-step-03-js.zip) (JS). ### Folder structure for this step @@ -158,7 +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) diff --git a/packages/navigation/steps/04/README.md b/packages/navigation/steps/04/README.md index d533610fa..65d9c78c9 100644 --- a/packages/navigation/steps/04/README.md +++ b/packages/navigation/steps/04/README.md @@ -8,6 +8,8 @@ When we are on the *Not Found* page because of an invalid hash, we want to get b ![Not Found page with Back button](assets/Tutorial_Navigation_and_Routing_Step_04a.png) +You can view this step live: [🔗 Live Preview of Step 4](https://ui5.github.io/tutorials/navigation/build/04/index-cdn.html). + ### Folder structure for this step ```text @@ -37,7 +39,7 @@ webapp/ ## Coding -You can view this step live: [🔗 Live Preview of Step 4](https://ui5.github.io/tutorials/navigation/build/04/index-cdn.html). +You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/navigation/navigation-step-04.zip) (TS)[📥 Download step 4](https://ui5.github.io/tutorials/navigation/navigation-step-04-js.zip) (JS). ## webapp/view/NotFound.view.xml @@ -229,7 +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) diff --git a/packages/navigation/steps/05/README.md b/packages/navigation/steps/05/README.md index 0f74497e3..9c12f758b 100644 --- a/packages/navigation/steps/05/README.md +++ b/packages/navigation/steps/05/README.md @@ -12,9 +12,11 @@ Fortunately, we can extend our app and offer an easy solution. There are some us ![The new Home page with a navigation button](assets/Tutorial_Navigation_and_Routing_Step_05.png "The new Home page with a navigation button") +You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io/tutorials/navigation/build/05/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io/tutorials/navigation/build/05/index-cdn.html). +You can download the solution for this step here: [📥 Download step 5](https://ui5.github.io/tutorials/navigation/navigation-step-05.zip) (TS)[📥 Download step 5](https://ui5.github.io/tutorials/navigation/navigation-step-05-js.zip) (JS). ## webapp/view/Home.view.xml @@ -216,7 +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) diff --git a/packages/navigation/steps/06/README.md b/packages/navigation/steps/06/README.md index ece8810f5..c478e0c53 100644 --- a/packages/navigation/steps/06/README.md +++ b/packages/navigation/steps/06/README.md @@ -12,9 +12,11 @@ In this step, we'll create a second button on the home page, with which we can n ![Employee list with Back button](assets/Tutorial_Navigation_and_Routing_Step_06b.png "Employee list with Back button") +You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io/tutorials/navigation/build/06/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io/tutorials/navigation/build/06/index-cdn.html). +You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/navigation/navigation-step-06.zip) (TS)[📥 Download step 6](https://ui5.github.io/tutorials/navigation/navigation-step-06-js.zip) (JS). ### Folder structure for this step @@ -263,7 +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) diff --git a/packages/navigation/steps/07/README.md b/packages/navigation/steps/07/README.md index 424574606..d1f60cfda 100644 --- a/packages/navigation/steps/07/README.md +++ b/packages/navigation/steps/07/README.md @@ -18,9 +18,11 @@ The detail page has to read the ID of the employee from the URL to fetch and dis ![Not Found page for an invalid EmployeeID](assets/Tutorial_Navigation_and_Routing_Step_07c.png "Not Found page for an invalid EmployeeID") +You can view this step live: [🔗 Live Preview of Step 7](https://ui5.github.io/tutorials/navigation/build/07/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 7](https://ui5.github.io/tutorials/navigation/build/07/index-cdn.html). +You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/navigation/navigation-step-07.zip) (TS)[📥 Download step 7](https://ui5.github.io/tutorials/navigation/navigation-step-07-js.zip) (JS). ### Folder structure for this step @@ -392,7 +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) diff --git a/packages/navigation/steps/08/README.md b/packages/navigation/steps/08/README.md index d2de77545..17248d524 100644 --- a/packages/navigation/steps/08/README.md +++ b/packages/navigation/steps/08/README.md @@ -16,9 +16,11 @@ In this step, we want to illustrate how to navigate to a page with a custom tran ![Not Found page for resume](assets/Tutorial_Navigation_and_Routing_Step_08c.png "Not Found page for resume") +You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io/tutorials/navigation/build/08/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io/tutorials/navigation/build/08/index-cdn.html). +You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/navigation/navigation-step-08.zip) (TS)[📥 Download step 8](https://ui5.github.io/tutorials/navigation/navigation-step-08-js.zip) (JS). ### Folder structure for this step @@ -427,7 +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) diff --git a/packages/navigation/steps/09/README.md b/packages/navigation/steps/09/README.md index 29af24e4c..69ddcdc06 100644 --- a/packages/navigation/steps/09/README.md +++ b/packages/navigation/steps/09/README.md @@ -10,9 +10,11 @@ In this step, we implement a bookmarking feature by enabling deep linking to tab ![Deep link to allow bookmarkable tabs](assets/Tutorial_Navigation_and_Routing_Step_09.png "Deep link to allow bookmarkable tabs") +You can view this step live: [🔗 Live Preview of Step 9](https://ui5.github.io/tutorials/navigation/build/09/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 9](https://ui5.github.io/tutorials/navigation/build/09/index-cdn.html). +You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/navigation/navigation-step-09.zip) (TS)[📥 Download step 9](https://ui5.github.io/tutorials/navigation/navigation-step-09-js.zip) (JS). ## webapp/manifest.json @@ -286,7 +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) diff --git a/packages/navigation/steps/10/README.md b/packages/navigation/steps/10/README.md index da1c41e75..d8c16d984 100644 --- a/packages/navigation/steps/10/README.md +++ b/packages/navigation/steps/10/README.md @@ -8,9 +8,11 @@ In the previous steps, we have implemented a `Resume` view that uses tabs to dis ![Tabs with lazy loading](assets/Tutorial_Navigation_and_Routing_Step_10a.png "Tabs with lazy loading") +You can view this step live: [🔗 Live Preview of Step 10](https://ui5.github.io/tutorials/navigation/build/10/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 10](https://ui5.github.io/tutorials/navigation/build/10/index-cdn.html). +You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/navigation/navigation-step-10.zip) (TS)[📥 Download step 10](https://ui5.github.io/tutorials/navigation/navigation-step-10-js.zip) (JS). ### Folder Structure for this Step @@ -294,7 +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) diff --git a/packages/navigation/steps/11/README.md b/packages/navigation/steps/11/README.md index e186eeeed..ad275e703 100644 --- a/packages/navigation/steps/11/README.md +++ b/packages/navigation/steps/11/README.md @@ -16,9 +16,11 @@ In this step, we will add a new button to the home page to illustrate the usage ![Sort options for the Employee Overview](assets/Tutorial_Navigation_and_Routing_Step_11c.png "Sort options for the Employee Overview") +You can view this step live: [🔗 Live Preview of Step 11](https://ui5.github.io/tutorials/navigation/build/11/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 11](https://ui5.github.io/tutorials/navigation/build/11/index-cdn.html). +You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/navigation/navigation-step-11.zip) (TS)[📥 Download step 11](https://ui5.github.io/tutorials/navigation/navigation-step-11-js.zip) (JS). ### Folder Structure for this Step @@ -587,7 +589,6 @@ Add the new texts to the `properties` file. *** - *** **Next:** [Step 12: Make a Search Bookmarkable](../12/README.md) diff --git a/packages/navigation/steps/12/README.md b/packages/navigation/steps/12/README.md index 55cae08d5..f86358cf3 100644 --- a/packages/navigation/steps/12/README.md +++ b/packages/navigation/steps/12/README.md @@ -8,9 +8,11 @@ In this step we will make the search bookmarkable. This allows users to search f ![Search and sorting bookmarkable](assets/Tutorial_Navigation_and_Routing_Step_12.png "Search and sorting bookmarkable") +You can view this step live: [🔗 Live Preview of Step 12](https://ui5.github.io/tutorials/navigation/build/12/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 12](https://ui5.github.io/tutorials/navigation/build/12/index-cdn.html). +You can download the solution for this step here: [📥 Download step 12](https://ui5.github.io/tutorials/navigation/navigation-step-12.zip) (TS)[📥 Download step 12](https://ui5.github.io/tutorials/navigation/navigation-step-12-js.zip) (JS). ## webapp/manifest.json @@ -282,7 +284,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) diff --git a/packages/navigation/steps/13/README.md b/packages/navigation/steps/13/README.md index cb57d6aca..5fd526559 100644 --- a/packages/navigation/steps/13/README.md +++ b/packages/navigation/steps/13/README.md @@ -8,9 +8,11 @@ In this step, we will create a button at the top of the table which will change ![Bookmarkable search and sorting](assets/Tutorial_Navigation_and_Routing_Step_13.png "Bookmarkable search and sorting") +You can view this step live: [🔗 Live Preview of Step 13](https://ui5.github.io/tutorials/navigation/build/13/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 13](https://ui5.github.io/tutorials/navigation/build/13/index-cdn.html). +You can download the solution for this step here: [📥 Download step 13](https://ui5.github.io/tutorials/navigation/navigation-step-13.zip) (TS)[📥 Download step 13](https://ui5.github.io/tutorials/navigation/navigation-step-13-js.zip) (JS). ## `webapp/controller/employee/overview/EmployeeOverviewContent.controller.ts/.js` @@ -220,7 +222,6 @@ When changing the table’s sorting options, you will see that the hash updates *** - *** **Next:** [Step 14: Make Dialogs Bookmarkable](../14/README.md) diff --git a/packages/navigation/steps/14/README.md b/packages/navigation/steps/14/README.md index f77989a87..8426b8fdf 100644 --- a/packages/navigation/steps/14/README.md +++ b/packages/navigation/steps/14/README.md @@ -8,9 +8,11 @@ In this step, we want to allow bookmarking of the dialog box that is opened when ![Bookmark for a dialog](assets/Tutorial_Navigation_and_Routing_Step_14.png "Bookmark for a dialog") +You can view this step live: [🔗 Live Preview of Step 14](https://ui5.github.io/tutorials/navigation/build/14/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 14](https://ui5.github.io/tutorials/navigation/build/14/index-cdn.html). +You can download the solution for this step here: [📥 Download step 14](https://ui5.github.io/tutorials/navigation/navigation-step-14.zip) (TS)[📥 Download step 14](https://ui5.github.io/tutorials/navigation/navigation-step-14-js.zip) (JS). ## /controller/employee/overview/EmployeeOverviewContent.controller.ts @@ -248,7 +250,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) diff --git a/packages/navigation/steps/15/README.md b/packages/navigation/steps/15/README.md index 5862c0801..b48d040de 100644 --- a/packages/navigation/steps/15/README.md +++ b/packages/navigation/steps/15/README.md @@ -8,9 +8,11 @@ The *Employees* table displays employee data. However, the resumes of the employ ![Navigation to an existing route from a table item](assets/Tutorial_Navigation_and_Routing_Step_15.png "Navigation to an existing route from a table item") +You can view this step live: [🔗 Live Preview of Step 15](https://ui5.github.io/tutorials/navigation/build/15/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 15](https://ui5.github.io/tutorials/navigation/build/15/index-cdn.html). +You can download the solution for this step here: [📥 Download step 15](https://ui5.github.io/tutorials/navigation/navigation-step-15.zip) (TS)[📥 Download step 15](https://ui5.github.io/tutorials/navigation/navigation-step-15-js.zip) (JS). ## webapp/view/employee/overview/EmployeeOverviewContent.view.xml @@ -245,7 +247,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) diff --git a/packages/navigation/steps/16/README.md b/packages/navigation/steps/16/README.md index ee7d2567b..620994555 100644 --- a/packages/navigation/steps/16/README.md +++ b/packages/navigation/steps/16/README.md @@ -8,9 +8,11 @@ So far we have created many useful routes in our app. In the very early steps we ![Console output for invalid hashes when listening to bypassed events](assets/Tutorial_Navigation_and_Routing_Step_16.png "Console output for invalid hashes when listening to bypassed events") +You can view this step live: [🔗 Live Preview of Step 16](https://ui5.github.io/tutorials/navigation/build/16/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 16](https://ui5.github.io/tutorials/navigation/build/16/index-cdn.html). +You can download the solution for this step here: [📥 Download step 16](https://ui5.github.io/tutorials/navigation/navigation-step-16.zip) (TS)[📥 Download step 16](https://ui5.github.io/tutorials/navigation/navigation-step-16-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` @@ -80,7 +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) diff --git a/packages/navigation/steps/17/README.md b/packages/navigation/steps/17/README.md index 386af3f82..5e4b7d68f 100644 --- a/packages/navigation/steps/17/README.md +++ b/packages/navigation/steps/17/README.md @@ -8,9 +8,11 @@ In the previous step, we have listened for bypassed events to detect possible te ![Console output for routes matched by listening to routeMatched events](assets/Tutorial_Navigation_and_Routing_Step_17.png "Console output for routes matched by listening to routeMatched events") +You can view this step live: [🔗 Live Preview of Step 17](https://ui5.github.io/tutorials/navigation/build/17/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 17](https://ui5.github.io/tutorials/navigation/build/17/index-cdn.html). +You can download the solution for this step here: [📥 Download step 17](https://ui5.github.io/tutorials/navigation/navigation-step-17.zip) (TS)[📥 Download step 17](https://ui5.github.io/tutorials/navigation/navigation-step-17-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` @@ -42,7 +44,6 @@ export default class App extends BaseController { Log.info(`Sorry, but the hash '${hash}' is invalid.`, "The resource was not found."); }); - router.attachRouteMatched((event: Router$RouteMatchedEvent) => { const sRouteName = event.getParameter("name"); // do something, i.e. send usage statistics to backend @@ -89,7 +90,6 @@ We extend the `App` controller again and listen to the `routeMatched` event. The Now you can access, for example, `webapp/index.html#/employees` while you have the console of the browser open. As you can see, there is a message logged for each navigation step that you do within the app. - *** **Previous:** [Step 16: Handle Invalid Hashes by Listening to Bypassed Events](../16/README.md) diff --git a/packages/odatav4/steps.json b/packages/odatav4/steps.json index e329fb52a..ac10ddad4 100644 --- a/packages/odatav4/steps.json +++ b/packages/odatav4/steps.json @@ -18,7 +18,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/02/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 2 (TS)📥 Download step 2 (JS)." + "description": "In this step, we see how the Table that is bound to the People entity set initially requests its data, and how the data can be refreshed. We use the Console tab in the browser developer tools to monitor the communication between the browser and the server. We see the initial request as well as the requests for refreshing the data." }, { "n": 3, @@ -27,7 +27,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 3 (TS)📥 Download step 3 (JS)." + "description": "In this step, we use the automatic data type detection of the OData V4 model to parse, validate, and format user entries. The service metadata contains type information for the properties of each entity." }, { "n": 4, @@ -36,7 +36,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/04/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 4 (TS)📥 Download step 4 (JS)." + "description": "In this step, we add features to filter, sort, and count the user data by using the OData V4 model API to apply OData system query options $filter, $orderby, and $count." }, { "n": 5, @@ -45,7 +45,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 5 (TS)📥 Download step 5 (JS)." + "description": "In this step, we have a closer look at batch groups. Batch groups are used to group multiple requests into one server request to improve the overall performance." }, { "n": 6, @@ -54,7 +54,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/06/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 6 (TS)📥 Download step 6 (JS)." + "description": "In this step, we will make it possible to create and edit \\(update\\) user data from the user interface and send the data to the back end." }, { "n": 7, @@ -63,7 +63,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/07/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 7 (TS)📥 Download step 7 (JS)." + "description": "In this step, we make it possible to delete user data." }, { "n": 8, @@ -72,7 +72,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/08/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 8 (TS)📥 Download step 8 (JS)." + "description": "Our OData service provides one OData operation: the ResetDataSource action. In this step, we add a button that resets all data changes we made during the tutorial to their original state using this action." }, { "n": 9, @@ -81,7 +81,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/09/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 9 (TS)📥 Download step 9 (JS)." + "description": "In this step we add a detail area with additional information." }, { "n": 10, @@ -90,7 +90,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/10/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 10 (TS)📥 Download step 10 (JS)." + "description": "In this step we avoid unnecessary back-end requests by preventing the destruction of data shown in the detail area when sorting or filtering the list." }, { "n": 11, @@ -99,7 +99,7 @@ "previewUrl": "https://ui5.github.io/tutorials/odatav4/build/11/index-cdn.html", "zipTs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip", "zipJs": "https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip", - "description": "You can download the solution for this step here: 📥 Download step 11 (TS)📥 Download step 11 (JS)." + "description": "In this step we add a table with additional information to the detail area." } ] } diff --git a/packages/odatav4/steps/01/README.md b/packages/odatav4/steps/01/README.md index 4be075def..a4628e0b7 100644 --- a/packages/odatav4/steps/01/README.md +++ b/packages/odatav4/steps/01/README.md @@ -10,8 +10,6 @@ The structure and data model created in this step will be used throughout this t ![Initial app with a simple table](assets/Tutorial_OData_V4_Step1_Preview_9d0182f.png "Initial app with a simple table") -You can download the solution for this step here: [📥 Download step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01.zip) (TS)[📥 Download step 1](https://ui5.github.io/tutorials/odatav4/odatav4-step-01-js.zip) (JS). - *** ## Setup diff --git a/packages/odatav4/steps/02/README.md b/packages/odatav4/steps/02/README.md index 616a6d215..cd0cb3d48 100644 --- a/packages/odatav4/steps/02/README.md +++ b/packages/odatav4/steps/02/README.md @@ -1,7 +1,5 @@ # Step 2: Data Access and Client-Server Communication -You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip) (TS)[📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip) (JS). - In this step, we see how the `Table` that is bound to the `People` entity set initially requests its data, and how the data can be refreshed. We use the *Console* tab in the browser developer tools to monitor the communication between the browser and the server. We see the initial request as well as the requests for refreshing the data. ## Preview @@ -12,9 +10,11 @@ In this step, we see how the `Table` that is bound to the `People` entity set in 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). + ## Coding -You can view this step live: [🔗 Live Preview of Step 2](https://ui5.github.io/tutorials/odatav4/build/02/index-cdn.html). +You can download the solution for this step here: [📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02.zip) (TS)[📥 Download step 2](https://ui5.github.io/tutorials/odatav4/odatav4-step-02-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` diff --git a/packages/odatav4/steps/03/README.md b/packages/odatav4/steps/03/README.md index 6161377b5..03ea1254d 100644 --- a/packages/odatav4/steps/03/README.md +++ b/packages/odatav4/steps/03/README.md @@ -1,7 +1,5 @@ # Step 3: Automatic Data Type Detection -You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip) (TS)[📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip) (JS). - In this step, we use the automatic data type detection of the OData V4 model to parse, validate, and format user entries. The service metadata contains type information for the properties of each entity. The OData V4 Model utilizes this information to compute the corresponding SAPUI5 type, including constraints, and sets this type to the SAPUI5 property binding for the entity property. For example, for `` the SAPUI5 type `Int64` is used, which corresponds to the OData type `Edm.Int64`. @@ -12,9 +10,11 @@ The OData V4 Model utilizes this information to compute the corresponding SAPUI5 ![Input does not match the underlying data type](assets/Tutorial_OData_V4_Step_3_8320fcf.png "Input does not match the underlying data type") +You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 3](https://ui5.github.io/tutorials/odatav4/build/03/index-cdn.html). +You can download the solution for this step here: [📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03.zip) (TS)[📥 Download step 3](https://ui5.github.io/tutorials/odatav4/odatav4-step-03-js.zip) (JS). ## `webapp/manifest.json` diff --git a/packages/odatav4/steps/04/README.md b/packages/odatav4/steps/04/README.md index ddd691d24..5b342f64a 100644 --- a/packages/odatav4/steps/04/README.md +++ b/packages/odatav4/steps/04/README.md @@ -1,7 +1,5 @@ # Step 4: Filtering, Sorting, and Counting -You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip) (TS)[📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip) (JS). - In this step, we add features to filter, sort, and count the user data by using the OData V4 model API to apply OData system query options `$filter`, `$orderby`, and `$count`. ## Preview @@ -12,9 +10,11 @@ In this step, we add features to filter, sort, and count the user data by using 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). + ## Coding -You can view this step live: [🔗 Live Preview of Step 4](https://ui5.github.io/tutorials/odatav4/build/04/index-cdn.html). +You can download the solution for this step here: [📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04.zip) (TS)[📥 Download step 4](https://ui5.github.io/tutorials/odatav4/odatav4-step-04-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` diff --git a/packages/odatav4/steps/05/README.md b/packages/odatav4/steps/05/README.md index a710d3c3a..92cc5725b 100644 --- a/packages/odatav4/steps/05/README.md +++ b/packages/odatav4/steps/05/README.md @@ -1,7 +1,5 @@ # Step 5: Batch Groups -You can download the solution for this step here: [📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip) (TS)[📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip) (JS). - In this step, we have a closer look at batch groups. Batch groups are used to group multiple requests into one server request to improve the overall performance. ## Preview @@ -10,9 +8,11 @@ In this step, we have a closer look at batch groups. Batch groups are used to gr ![No visual change compared to the last step](assets/Tutorial_OData_V4_Step_4_3ac4fcc.png "No visual change compared to the last step") +You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 5](https://ui5.github.io/tutorials/odatav4/build/05/index-cdn.html). +You can download the solution for this step here: [📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05.zip) (TS)[📥 Download step 5](https://ui5.github.io/tutorials/odatav4/odatav4-step-05-js.zip) (JS). ## `webapp/manifest.json` diff --git a/packages/odatav4/steps/06/README.md b/packages/odatav4/steps/06/README.md index a67c084aa..f88954840 100644 --- a/packages/odatav4/steps/06/README.md +++ b/packages/odatav4/steps/06/README.md @@ -1,7 +1,5 @@ # Step 6: Create and Edit -You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip) (TS)[📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip) (JS). - In this step, we will make it possible to create and edit \(update\) user data from the user interface and send the data to the back end. ## Preview @@ -10,9 +8,11 @@ In this step, we will make it possible to create and edit \(update\) user data f ![Data can now be edited and added.](assets/Tutorial_OData_V4_Step_6_baf7417.png "Data can now be edited and added.") +You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io/tutorials/odatav4/build/06/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 6](https://ui5.github.io/tutorials/odatav4/build/06/index-cdn.html). +You can download the solution for this step here: [📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06.zip) (TS)[📥 Download step 6](https://ui5.github.io/tutorials/odatav4/odatav4-step-06-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` diff --git a/packages/odatav4/steps/07/README.md b/packages/odatav4/steps/07/README.md index b44cd0ec3..b21a02779 100644 --- a/packages/odatav4/steps/07/README.md +++ b/packages/odatav4/steps/07/README.md @@ -1,7 +1,5 @@ # Step 7: Delete -You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip) (TS)[📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip) (JS). - In this step, we make it possible to delete user data. ## Preview @@ -10,9 +8,11 @@ In this step, we make it possible to delete user data. ![A new Delete User button is added](assets/Tutorial_OData_V4_Step_7_32509f4.png "A new Delete User button is added") +You can view this step live: [🔗 Live Preview of Step 7](https://ui5.github.io/tutorials/odatav4/build/07/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 7](https://ui5.github.io/tutorials/odatav4/build/07/index-cdn.html). +You can download the solution for this step here: [📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07.zip) (TS)[📥 Download step 7](https://ui5.github.io/tutorials/odatav4/odatav4-step-07-js.zip) (JS). ## `webapp/App.controller.ts/.js` diff --git a/packages/odatav4/steps/08/README.md b/packages/odatav4/steps/08/README.md index d35ed1fe5..f116dbe96 100644 --- a/packages/odatav4/steps/08/README.md +++ b/packages/odatav4/steps/08/README.md @@ -1,7 +1,5 @@ # Step 8: OData Operations -You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip) (TS)[📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip) (JS). - Our OData service provides one OData operation: the `ResetDataSource` action. In this step, we add a button that resets all data changes we made during the tutorial to their original state using this action. ## Preview @@ -10,9 +8,11 @@ Our OData service provides one OData operation: the `ResetDataSource` action. In ![A Restart Tutorial button is added](assets/Tutorial_OData_V4_Step_8_e518deb.png "A Restart Tutorial button is added") +You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io/tutorials/odatav4/build/08/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 8](https://ui5.github.io/tutorials/odatav4/build/08/index-cdn.html). +You can download the solution for this step here: [📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08.zip) (TS)[📥 Download step 8](https://ui5.github.io/tutorials/odatav4/odatav4-step-08-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` diff --git a/packages/odatav4/steps/09/README.md b/packages/odatav4/steps/09/README.md index a1d8b46f6..e375c691f 100644 --- a/packages/odatav4/steps/09/README.md +++ b/packages/odatav4/steps/09/README.md @@ -1,7 +1,5 @@ # Step 9: List-Detail Scenario -You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip) (TS)[📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip) (JS). - In this step we add a detail area with additional information. ## Preview @@ -10,9 +8,11 @@ In this step we add a detail area with additional information. ![A list of users with an added detail area](assets/Tut_OD4_Step_9_6e9025b.png "A detail area containing information about the selected user is added") +You can view this step live: [🔗 Live Preview of Step 9](https://ui5.github.io/tutorials/odatav4/build/09/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 9](https://ui5.github.io/tutorials/odatav4/build/09/index-cdn.html). +You can download the solution for this step here: [📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09.zip) (TS)[📥 Download step 9](https://ui5.github.io/tutorials/odatav4/odatav4-step-09-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` diff --git a/packages/odatav4/steps/10/README.md b/packages/odatav4/steps/10/README.md index e980c9c91..f54ee9e23 100644 --- a/packages/odatav4/steps/10/README.md +++ b/packages/odatav4/steps/10/README.md @@ -1,7 +1,5 @@ # Step 10: Enable Data Reuse -You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip) (TS)[📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip) (JS). - In this step we avoid unnecessary back-end requests by preventing the destruction of data shown in the detail area when sorting or filtering the list. ## Preview @@ -10,9 +8,11 @@ In this step we avoid unnecessary back-end requests by preventing the destructio ![A list of users with an added detail area](assets/Tut_OD4_Step_9_6e9025b.png "No visual change compared to the last step") +You can view this step live: [🔗 Live Preview of Step 10](https://ui5.github.io/tutorials/odatav4/build/10/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 10](https://ui5.github.io/tutorials/odatav4/build/10/index-cdn.html). +You can download the solution for this step here: [📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10.zip) (TS)[📥 Download step 10](https://ui5.github.io/tutorials/odatav4/odatav4-step-10-js.zip) (JS). ## `webapp/controller/App.controller.ts/.js` diff --git a/packages/odatav4/steps/11/README.md b/packages/odatav4/steps/11/README.md index 162829905..ab0602a00 100644 --- a/packages/odatav4/steps/11/README.md +++ b/packages/odatav4/steps/11/README.md @@ -1,7 +1,5 @@ # Step 11: Add Table with :n Navigation to Detail Area -You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip) (TS)[📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip) (JS). - In this step we add a table with additional information to the detail area. ## Preview @@ -10,9 +8,11 @@ In this step we add a table with additional information to the detail area. ![A table containing information about friends of the selected user is added](assets/Tut_OD4_Step_11_45abd62.png "A table containing information about friends of the selected user is added") +You can view this step live: [🔗 Live Preview of Step 11](https://ui5.github.io/tutorials/odatav4/build/11/index-cdn.html). + ## Coding -You can view this step live: [🔗 Live Preview of Step 11](https://ui5.github.io/tutorials/odatav4/build/11/index-cdn.html). +You can download the solution for this step here: [📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11.zip) (TS)[📥 Download step 11](https://ui5.github.io/tutorials/odatav4/odatav4-step-11-js.zip) (JS). ## webapp/view/App.view.xml From 6d7a4eb516d132c7145c37922702b77693c21937 Mon Sep 17 00:00:00 2001 From: Peter Muessig Date: Wed, 22 Jul 2026 16:01:17 +0200 Subject: [PATCH 4/4] docs(tutorials): use OpenUI5 wording and SDK doc links Align all tutorials to OpenUI5: - Replace remaining "SAPUI5" wording with "OpenUI5" in navigation and odatav4 step READMEs and in navigation i18n appDescription strings. - Point doc/API links at the OpenUI5 SDK: rewrite ui5.sap.com/#/... to sdk.openui5.org/#/... and repair malformed sdk.openui5.org links that were missing the /#/ route (walkthrough step 03). - Normalize SDK topic links (full topic hashes, consistent /topic/ path). Project config was already OpenUI5-correct: every ui5.yaml uses framework OpenUI5 with OpenUI5-only libraries, and every package.json depends on @openui5/types. --- packages/navigation/README.md | 2 +- packages/navigation/steps/01/README.md | 6 +++--- .../steps/01/webapp/i18n/i18n.properties | 2 +- packages/navigation/steps/02/README.md | 4 ++-- .../steps/02/webapp/i18n/i18n.properties | 2 +- .../steps/03/webapp/i18n/i18n.properties | 2 +- packages/navigation/steps/04/README.md | 2 +- .../steps/04/webapp/i18n/i18n.properties | 2 +- packages/navigation/steps/05/README.md | 2 +- .../steps/05/webapp/i18n/i18n.properties | 2 +- .../steps/06/webapp/i18n/i18n.properties | 2 +- .../steps/07/webapp/i18n/i18n.properties | 2 +- packages/navigation/steps/08/README.md | 6 +++--- .../steps/08/webapp/i18n/i18n.properties | 2 +- .../steps/09/webapp/i18n/i18n.properties | 2 +- packages/navigation/steps/10/README.md | 2 +- .../steps/10/webapp/i18n/i18n.properties | 2 +- .../steps/11/webapp/i18n/i18n.properties | 2 +- .../steps/12/webapp/i18n/i18n.properties | 2 +- .../steps/13/webapp/i18n/i18n.properties | 2 +- .../steps/14/webapp/i18n/i18n.properties | 2 +- .../steps/15/webapp/i18n/i18n.properties | 2 +- .../steps/16/webapp/i18n/i18n.properties | 2 +- .../steps/17/webapp/i18n/i18n.properties | 2 +- packages/odatav4/steps/01/README.md | 4 ++-- packages/odatav4/steps/02/README.md | 8 ++++---- packages/odatav4/steps/03/README.md | 12 ++++++------ packages/odatav4/steps/04/README.md | 4 ++-- packages/odatav4/steps/05/README.md | 4 ++-- packages/odatav4/steps/06/README.md | 16 ++++++++-------- packages/odatav4/steps/07/README.md | 2 +- packages/odatav4/steps/08/README.md | 6 +++--- packages/odatav4/steps/09/README.md | 2 +- packages/odatav4/steps/10/README.md | 2 +- packages/quickstart/steps/02/README.md | 4 ++-- packages/quickstart/steps/03/README.md | 6 +++--- packages/walkthrough/steps/03/README.md | 10 +++++----- packages/walkthrough/steps/07/README.md | 2 +- packages/walkthrough/steps/19/README.md | 4 ++-- packages/walkthrough/steps/20/README.md | 2 +- packages/walkthrough/steps/27/README.md | 2 +- 41 files changed, 74 insertions(+), 74 deletions(-) diff --git a/packages/navigation/README.md b/packages/navigation/README.md index bb458a9df..6a8646500 100644 --- a/packages/navigation/README.md +++ b/packages/navigation/README.md @@ -1,6 +1,6 @@ # Navigation and Routing Tutorial -SAPUI5 comes with a powerful routing API that helps you control the state of your application efficiently. This tutorial will illustrate all major features and APIs related to navigation and routing in SAPUI5 apps by creating a simple and easy to understand mobile app. It represents a set of best practices for applying the navigation and routing features of SAPUI5 to your applications. +OpenUI5 comes with a powerful routing API that helps you control the state of your application efficiently. This tutorial will illustrate all major features and APIs related to navigation and routing in OpenUI5 apps by creating a simple and easy to understand mobile app. It represents a set of best practices for applying the navigation and routing features of OpenUI5 to your applications. In classical Web applications, the server determines which resource is requested based on the URL pattern of the request and serves it accordingly. The server-side logic controls how the requested resource or page is displayed in an appropriate way. diff --git a/packages/navigation/steps/01/README.md b/packages/navigation/steps/01/README.md index ccd56f268..74ff2888a 100644 --- a/packages/navigation/steps/01/README.md +++ b/packages/navigation/steps/01/README.md @@ -2,7 +2,7 @@ We start by setting up a simple app for this tutorial. The app displays mock data only and mimics real OData back-end calls with the mock server as you have seen in the *Walkthrough* tutorial. -The structure and data model created in this step will be used throughout the rest of this tutorial. The initial app created in this step will be extended in the subsequent steps to illustrate the navigation and routing features of SAPUI5. +The structure and data model created in this step will be used throughout the rest of this tutorial. The initial app created in this step will be extended in the subsequent steps to illustrate the navigation and routing features of OpenUI5. ## Preview @@ -53,11 +53,11 @@ webapp/ ## The Initial App -With the downloaded coding, you have an initial app with recommended settings that provides the basic features of an SAPUI5 app: +With the downloaded coding, you have an initial app with recommended settings that provides the basic features of an OpenUI5 app: - **Home Page** - The home page of our app is defined in the `webapp/index.html` file. In this file we bootstrap SAPUI5 and tell the runtime where to find our custom resources. Furthermore, we initialize the `MockServer` to simulate back-end requests as we do not have a real back-end service throughout this tutorial. Finally, we instantiate the application component, assign it to a `sap.m.Shell` control, and place the shell into the body. The corresponding `Component.ts` file in the `webapp` folder will be extended throughout this tutorial. + The home page of our app is defined in the `webapp/index.html` file. In this file we bootstrap OpenUI5 and tell the runtime where to find our custom resources. Furthermore, we initialize the `MockServer` to simulate back-end requests as we do not have a real back-end service throughout this tutorial. Finally, we instantiate the application component, assign it to a `sap.m.Shell` control, and place the shell into the body. The corresponding `Component.ts` file in the `webapp` folder will be extended throughout this tutorial. - **Data** diff --git a/packages/navigation/steps/01/webapp/i18n/i18n.properties b/packages/navigation/steps/01/webapp/i18n/i18n.properties index be58132c8..32efd070c 100644 --- a/packages/navigation/steps/01/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/01/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/02/README.md b/packages/navigation/steps/02/README.md index c390a28e2..d7bba447d 100644 --- a/packages/navigation/steps/02/README.md +++ b/packages/navigation/steps/02/README.md @@ -90,7 +90,7 @@ webapp/ ``` -Single-page applications based on SAPUI5 can use a so-called “router” to dispatch hash-based URLs to one or more views of the app. Therefore, the router needs to know how to address and show the views. In SAPUI5, we can simply add a `routing` section to our existing `sap.ui5` section in the descriptor file to configure the router. There are three properties that can be used to configure the routing of your application: +Single-page applications based on OpenUI5 can use a so-called “router” to dispatch hash-based URLs to one or more views of the app. Therefore, the router needs to know how to address and show the views. In OpenUI5, we can simply add a `routing` section to our existing `sap.ui5` section in the descriptor file to configure the router. There are three properties that can be used to configure the routing of your application: - `config` @@ -110,7 +110,7 @@ Single-page applications based on SAPUI5 can use a so-called “router” to dis This basic routing configuration was easy enough. However, you can’t see it in action until you have initialized the router. > :note: -> As of SAPUI5 version 1.30, we recommend that you define the routing in the `manifest.json` descriptor file using routes and targets. In older versions of SAPUI5, the routing configuration had to be done directly in the metadata section of the component, and with different syntax. +> As of OpenUI5 version 1.30, we recommend that you define the routing in the `manifest.json` descriptor file using routes and targets. In older versions of OpenUI5, the routing configuration had to be done directly in the metadata section of the component, and with different syntax. ## `webapp/Component.ts/.js` diff --git a/packages/navigation/steps/02/webapp/i18n/i18n.properties b/packages/navigation/steps/02/webapp/i18n/i18n.properties index be58132c8..32efd070c 100644 --- a/packages/navigation/steps/02/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/02/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/03/webapp/i18n/i18n.properties b/packages/navigation/steps/03/webapp/i18n/i18n.properties index a5e577d0f..7a7cc5393 100644 --- a/packages/navigation/steps/03/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/03/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/04/README.md b/packages/navigation/steps/04/README.md index 65d9c78c9..bf0c69bb4 100644 --- a/packages/navigation/steps/04/README.md +++ b/packages/navigation/steps/04/README.md @@ -128,7 +128,7 @@ The function checks if there is a previous hash value in the app history. If so, The third parameter of `navTo("appHome", {}, true /*no history*/);` has the value `true` and makes sure that the hash is replaced. With the line `sap.ui.core.UIComponent.getRouterFor(this)` you can easily access your component’s router throughout the app. To make it even more comfortable, we also add a handy shortcut `getRouter` to the base controller. This function is now available in each subclass as well. It is also used in the `onNavBack` handler to get a reference to the router before calling `navTo`. We now have to implement the reuse in all other controllers. > :note: -> In SAPUI5 there are multiple options to reuse code. We recommend to use a base controller for such helper methods because this allows us to decoratively use the `onNavBack` handler directly in any XML view without adding additional code to the controller. Our base controller is an abstract controller that will not be instantiated in any view. Therefore, the naming convention `*.controller.ts` does not apply, and we can just name the file `BaseController.ts`. By not using the naming convention `*.controller.ts` we can even prevent any unintentional usage in views. +> In OpenUI5 there are multiple options to reuse code. We recommend to use a base controller for such helper methods because this allows us to decoratively use the `onNavBack` handler directly in any XML view without adding additional code to the controller. Our base controller is an abstract controller that will not be instantiated in any view. Therefore, the naming convention `*.controller.ts` does not apply, and we can just name the file `BaseController.ts`. By not using the naming convention `*.controller.ts` we can even prevent any unintentional usage in views. ## `webapp/controller/NotFound.controller.ts/.js` diff --git a/packages/navigation/steps/04/webapp/i18n/i18n.properties b/packages/navigation/steps/04/webapp/i18n/i18n.properties index a5e577d0f..7a7cc5393 100644 --- a/packages/navigation/steps/04/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/04/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/05/README.md b/packages/navigation/steps/05/README.md index 9c12f758b..3557b07dc 100644 --- a/packages/navigation/steps/05/README.md +++ b/packages/navigation/steps/05/README.md @@ -197,7 +197,7 @@ Next, we have to register an event handler to the `display` event of the `notFou From the router reference we can fetch a reference to the `notFound` target. Each target configuration will create a runtime object that can be accessed through the router. -Similar to SAPUI5 controls, targets define API methods and events that can be attached. We attach a display event handler and save the configuration that was received as the event parameter `data` in an internal controller variable `this.data`. This also includes the `fromTarget` information in case the caller passed it on. However, we now have to override the base controller’s `onNavBack` implementation to change the behavior a bit. We add a special case for our *Back* functionality in case the `fromTarget` property has been passed on. If specified, we simply display the target defined as `fromTarget` manually the same way we actually called the `notFound` target manually. Otherwise we just call the `onNavBack` implementation in the `BaseController`. +Similar to OpenUI5 controls, targets define API methods and events that can be attached. We attach a display event handler and save the configuration that was received as the event parameter `data` in an internal controller variable `this.data`. This also includes the `fromTarget` information in case the caller passed it on. However, we now have to override the base controller’s `onNavBack` implementation to change the behavior a bit. We add a special case for our *Back* functionality in case the `fromTarget` property has been passed on. If specified, we simply display the target defined as `fromTarget` manually the same way we actually called the `notFound` target manually. Otherwise we just call the `onNavBack` implementation in the `BaseController`. ## webapp/i18n/i18n.properties diff --git a/packages/navigation/steps/05/webapp/i18n/i18n.properties b/packages/navigation/steps/05/webapp/i18n/i18n.properties index 6a5deb0c6..44f8555f0 100644 --- a/packages/navigation/steps/05/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/05/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/06/webapp/i18n/i18n.properties b/packages/navigation/steps/06/webapp/i18n/i18n.properties index 23e1a492e..fbefc39cb 100644 --- a/packages/navigation/steps/06/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/06/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/07/webapp/i18n/i18n.properties b/packages/navigation/steps/07/webapp/i18n/i18n.properties index 33c7e3a57..97b737152 100644 --- a/packages/navigation/steps/07/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/07/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/08/README.md b/packages/navigation/steps/08/README.md index 17248d524..4fe7b2e45 100644 --- a/packages/navigation/steps/08/README.md +++ b/packages/navigation/steps/08/README.md @@ -311,7 +311,7 @@ The target `employeeResume` references the view `employee.Resume` that we are ab Create a file `Resume.view.xml` inside the `webapp/view/employee` folder. The view uses an `IconTabBar` to display the resume data. Therefore, its binding attribute is set to `{Resume}`. -In the `IconTabBar` we display four tabs. Three of them simply use a `Text` control to display the data from the service. The *Projects* tab uses a nested XML view to display the projects of the employee. SAPUI5 takes care of loading the XML view automatically when the user navigates to the *Resume* page. +In the `IconTabBar` we display four tabs. Three of them simply use a `Text` control to display the data from the service. The *Projects* tab uses a nested XML view to display the projects of the employee. OpenUI5 takes care of loading the XML view automatically when the user navigates to the *Resume* page. ## `webapp/controller/employee/Resume.controller.ts/.js` \(New\) @@ -405,10 +405,10 @@ Create a file `Resume.controller.ts` in the `webapp/controller/employee` folder. ``` -Create a file `ResumeProjects.view.xml` in the `webapp/view/employee` folder. This view does not have a controller as we don’t need it. It just displays a `Text` control with the projects text of the selected employee. It illustrates that using nested views works just fine in combination with navigation and routing in SAPUI5. +Create a file `ResumeProjects.view.xml` in the `webapp/view/employee` folder. This view does not have a controller as we don’t need it. It just displays a `Text` control with the projects text of the selected employee. It illustrates that using nested views works just fine in combination with navigation and routing in OpenUI5. > :note: -> For more complex applications, the performance is significantly increased if parts of the UI are only loaded when the user is actively selecting it. In this example, the view is always loaded even though the user never decided to display the project information. In the next steps, we will extend the UI so that the content is loaded “lazy” by SAPUI5 only when the filter item is clicked. The back-end service will fetch the data only on request and the UI will only have to be updated with the selected data instead of loading all data. +> For more complex applications, the performance is significantly increased if parts of the UI are only loaded when the user is actively selecting it. In this example, the view is always loaded even though the user never decided to display the project information. In the next steps, we will extend the UI so that the content is loaded “lazy” by OpenUI5 only when the filter item is clicked. The back-end service will fetch the data only on request and the UI will only have to be updated with the selected data instead of loading all data. ## webapp/i18n/i18n.properties diff --git a/packages/navigation/steps/08/webapp/i18n/i18n.properties b/packages/navigation/steps/08/webapp/i18n/i18n.properties index 4f8b74451..1e8ba095c 100644 --- a/packages/navigation/steps/08/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/08/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/09/webapp/i18n/i18n.properties b/packages/navigation/steps/09/webapp/i18n/i18n.properties index 4f8b74451..1e8ba095c 100644 --- a/packages/navigation/steps/09/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/09/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/10/README.md b/packages/navigation/steps/10/README.md index d8c16d984..fcbc7b32b 100644 --- a/packages/navigation/steps/10/README.md +++ b/packages/navigation/steps/10/README.md @@ -282,7 +282,7 @@ We add the `resumeTabHobbies` and `resumeTabNotes` targets to the descriptor fil The `resumeTabHobbies` target sets the parent property to `employeeResume`. The parent property expects the name of another target. In our case, this makes sure that the view from the parent target `employeeResume` is loaded before the target `resumeTabHobbies` is displayed. This can be considered as a “view dependency”. By setting the `controlId` and `controlAggregation` properties the router places the view `ResumeHobbies` into the `content` aggregation of the `IconTabFilter` control with ID `hobbiesTab`. We also set a parameter `id` to a custom ID to illustrate how you could overrule a hard-coded ID inside a view. > :note: -> Each target can define only one parent with its parent property. This is similar to the SAPUI5 control tree where each control can have only one parent control \(accessed with the method `getParent()` of `sap.ui.base.ManagedObject`\). The `controlId` property always references a control inside the parent view that is specified with the `parent` target. +> Each target can define only one parent with its parent property. This is similar to the OpenUI5 control tree where each control can have only one parent control \(accessed with the method `getParent()` of `sap.ui.base.ManagedObject`\). The `controlId` property always references a control inside the parent view that is specified with the `parent` target. Now we add the `resumeTabNotes` target similar to the `Hobbies` target. The `resumeTabNotes` target defines the parent target `employeeResume` as well, because they share the same parent view. We place the `ResumeNotes` view into the `content` aggregation of the `IconTabFilter` control with ID `notesTab`. diff --git a/packages/navigation/steps/10/webapp/i18n/i18n.properties b/packages/navigation/steps/10/webapp/i18n/i18n.properties index 4f8b74451..1e8ba095c 100644 --- a/packages/navigation/steps/10/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/10/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/11/webapp/i18n/i18n.properties b/packages/navigation/steps/11/webapp/i18n/i18n.properties index 1e9ffae14..5e3d07bb7 100644 --- a/packages/navigation/steps/11/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/11/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/12/webapp/i18n/i18n.properties b/packages/navigation/steps/12/webapp/i18n/i18n.properties index 1e9ffae14..5e3d07bb7 100644 --- a/packages/navigation/steps/12/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/12/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/13/webapp/i18n/i18n.properties b/packages/navigation/steps/13/webapp/i18n/i18n.properties index 1e9ffae14..5e3d07bb7 100644 --- a/packages/navigation/steps/13/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/13/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/14/webapp/i18n/i18n.properties b/packages/navigation/steps/14/webapp/i18n/i18n.properties index 1e9ffae14..5e3d07bb7 100644 --- a/packages/navigation/steps/14/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/14/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/15/webapp/i18n/i18n.properties b/packages/navigation/steps/15/webapp/i18n/i18n.properties index 1e9ffae14..5e3d07bb7 100644 --- a/packages/navigation/steps/15/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/15/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/16/webapp/i18n/i18n.properties b/packages/navigation/steps/16/webapp/i18n/i18n.properties index 1e9ffae14..5e3d07bb7 100644 --- a/packages/navigation/steps/16/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/16/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/navigation/steps/17/webapp/i18n/i18n.properties b/packages/navigation/steps/17/webapp/i18n/i18n.properties index 1e9ffae14..5e3d07bb7 100644 --- a/packages/navigation/steps/17/webapp/i18n/i18n.properties +++ b/packages/navigation/steps/17/webapp/i18n/i18n.properties @@ -1,6 +1,6 @@ # App Descriptor appTitle=Navigation & Routing Tutorial -appDescription=A simple app that explains how to use navigation and routing features of SAPUI5 +appDescription=A simple app that explains how to use navigation and routing features of OpenUI5 iWantToNavigate=I want to navigate homePageTitle=Home diff --git a/packages/odatav4/steps/01/README.md b/packages/odatav4/steps/01/README.md index a4628e0b7..c61a68f24 100644 --- a/packages/odatav4/steps/01/README.md +++ b/packages/odatav4/steps/01/README.md @@ -2,7 +2,7 @@ We start by setting up a simple app that loads data from an OData service and displays it in a table. We use a mock server to simulate requests to and responses from the service. -The structure and data model created in this step will be used throughout this tutorial to illustrate the OData V4 features in SAPUI5. +The structure and data model created in this step will be used throughout this tutorial to illustrate the OData V4 features in OpenUI5. ## Preview @@ -97,7 +97,7 @@ In this tutorial, we only use the entity type `Person` of the *TripPin* service. [*OData* Reference Services including *TripPin*](http://www.odata.org/odata-services/) -[Bootstrapping: Loading and Initializing](https://sdk.openui5.org/topic/a04b0d10fb494d1cb722b9e341b584ba.html "To use SAPUI5 features in your HTML page, you have to load and initialize the SAPUI5 library.") +[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.") diff --git a/packages/odatav4/steps/02/README.md b/packages/odatav4/steps/02/README.md index cd0cb3d48..d8fe08866 100644 --- a/packages/odatav4/steps/02/README.md +++ b/packages/odatav4/steps/02/README.md @@ -227,13 +227,13 @@ We search for the following mock server requests: **Related Information** -[Bindings](https://sdk.openui5.org/topic/54e0ddf "Bindings connect SAPUI5 view elements to model data, allowing changes in the model to be reflected in the view element and vice versa.") +[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.") -[API Reference: `sap.ui.model.odata.v4.ODataMetaModel`](https://ui5.sap.com/#/api/sap.ui.model.odata.v4.ODataMetaModel) +[API Reference: `sap.ui.model.odata.v4.ODataMetaModel`](https://sdk.openui5.org/#/api/sap.ui.model.odata.v4.ODataMetaModel) -[API Reference: `sap.ui.model.odata.v4.ODataListBinding.refresh`](https://ui5.sap.com/#/api/sap.ui.model.odata.v4.ODataListBinding/methods/refresh) +[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 SAPUI5 app.") +[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.") *** diff --git a/packages/odatav4/steps/03/README.md b/packages/odatav4/steps/03/README.md index 03ea1254d..147814c30 100644 --- a/packages/odatav4/steps/03/README.md +++ b/packages/odatav4/steps/03/README.md @@ -2,7 +2,7 @@ In this step, we use the automatic data type detection of the OData V4 model to parse, validate, and format user entries. The service metadata contains type information for the properties of each entity. -The OData V4 Model utilizes this information to compute the corresponding SAPUI5 type, including constraints, and sets this type to the SAPUI5 property binding for the entity property. For example, for `` the SAPUI5 type `Int64` is used, which corresponds to the OData type `Edm.Int64`. +The OData V4 Model utilizes this information to compute the corresponding OpenUI5 type, including constraints, and sets this type to the OpenUI5 property binding for the entity property. For example, for `` the OpenUI5 type `Int64` is used, which corresponds to the OData type `Edm.Int64`. ## Preview @@ -51,9 +51,9 @@ You can download the solution for this step here: [📥 Do ``` -In the `manifest.json` descriptor file, we add the `"handleValidation": true` setting. This makes sure that any validation errors that are detected by the SAPUI5 types are shown on the UI using the message manager. +In the `manifest.json` descriptor file, we add the `"handleValidation": true` setting. This makes sure that any validation errors that are detected by the OpenUI5 types are shown on the UI using the message manager. -We now run the app using the `index.html` file and enter values that don't match the type and constraints given in the metadata file. For example, enter the string value `Young at Heart` in field *Age*, which requires an integer input \(SAPUI5 type `sap.ui.model.odata.type.Int64`, corresponding to OData type `Edm.Int64`\), or remove an entry from the *User Name* or *First Name* fields, which are mandatory. Fields with incorrect entries are highlighted and an error message is displayed. +We now run the app using the `index.html` file and enter values that don't match the type and constraints given in the metadata file. For example, enter the string value `Young at Heart` in field *Age*, which requires an integer input \(OpenUI5 type `sap.ui.model.odata.type.Int64`, corresponding to OData type `Edm.Int64`\), or remove an entry from the *User Name* or *First Name* fields, which are mandatory. Fields with incorrect entries are highlighted and an error message is displayed. > :note: > If you explicitly define a type in the binding info of a control, the automatic type detection for that binding will be turned off. For example, if you change the `Input` for `Age` in the view to ` :note: -> Many of the methods in the OData V4 API of SAPUI5 return a `Promise` to manage asynchronous processing +> Many of the methods in the OData V4 API of OpenUI5 return a `Promise` to manage asynchronous processing ## webapp/view/App.view.xml @@ -123,9 +123,9 @@ And now we are done! We built a simple application with user data from an OData **Related Information** -[Bindings](https://sdk.openui5.org/topic/54e0ddf "Bindings connect SAPUI5 view elements to model data, allowing changes in the model to be reflected in the view element and vice versa.") +[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.") -[OData Operations](https://sdk.openui5.org/topic/b54f789 "The OData V4 model supports OData operations (ActionImport, FunctionImport, bound Actions and bound Functions). Unbound parameters are limited to primitive values.") +[OData Operations](https://sdk.openui5.org/topic/b54f7895b7594c61a83fa7257fa9d13f "The OData V4 model supports OData operations (ActionImport, FunctionImport, bound Actions and bound Functions). Unbound parameters are limited to primitive values.") *** diff --git a/packages/odatav4/steps/09/README.md b/packages/odatav4/steps/09/README.md index e375c691f..4350817f7 100644 --- a/packages/odatav4/steps/09/README.md +++ b/packages/odatav4/steps/09/README.md @@ -347,7 +347,7 @@ We add the missing texts to the properties file. **Related Information** -[Data Reuse](https://sdk.openui5.org/topic/648e360 "The OData V4 model keeps data with respect to bindings, which allows different views on the same data, but also means that data is not automatically shared between bindings. There are mechanisms for sharing data to avoid redundant requests and to keep the same data in different controls in sync.") +[Data Reuse](https://sdk.openui5.org/topic/648e360fa22d46248ca783dc6eb44531 "The OData V4 model keeps data with respect to bindings, which allows different views on the same data, but also means that data is not automatically shared between bindings. There are mechanisms for sharing data to avoid redundant requests and to keep the same data in different controls in sync.") *** diff --git a/packages/odatav4/steps/10/README.md b/packages/odatav4/steps/10/README.md index f54ee9e23..0f9c66096 100644 --- a/packages/odatav4/steps/10/README.md +++ b/packages/odatav4/steps/10/README.md @@ -116,7 +116,7 @@ You can use the `Context#setKeepAlive` method to prevent the destruction of info **Related Information** -[Extending the Lifetime of a Context that is not Used Exclusively by a Table Collection](https://sdk.openui5.org/topic/648e360#loio648e360fa22d46248ca783dc6eb44531__section_ELC) +[Extending the Lifetime of a Context that is not Used Exclusively by a Table Collection](https://sdk.openui5.org/topic/648e360fa22d46248ca783dc6eb44531.html#loio648e360fa22d46248ca783dc6eb44531/section_ELC) *** diff --git a/packages/quickstart/steps/02/README.md b/packages/quickstart/steps/02/README.md index 8be09e6be..a2f511546 100644 --- a/packages/quickstart/steps/02/README.md +++ b/packages/quickstart/steps/02/README.md @@ -117,6 +117,6 @@ You can see a title bar and a blue button that reacts to your input. Congratulat **Related Information** -[XML View](https://sdk.openui5.org/#/topic/1409791afe4747319a3b23a1e2fc7064 "The XML view type is defined in an XML file, with a file name ending in .view.xml. The file name and the folder structure together specify the name of the view that equals the OpenUI5 module name.") +[XML View](https://sdk.openui5.org/topic/1409791afe4747319a3b23a1e2fc7064 "The XML view type is defined in an XML file, with a file name ending in .view.xml. The file name and the folder structure together specify the name of the view that equals the OpenUI5 module name.") -[Controller](https://sdk.openui5.org/#/topic/121b8e6337d147af9819129e428f1f75 "A controller contains methods that define how models and views interact.") +[Controller](https://sdk.openui5.org/topic/121b8e6337d147af9819129e428f1f75 "A controller contains methods that define how models and views interact.") diff --git a/packages/quickstart/steps/03/README.md b/packages/quickstart/steps/03/README.md index faa87dc66..bc5874c70 100644 --- a/packages/quickstart/steps/03/README.md +++ b/packages/quickstart/steps/03/README.md @@ -205,8 +205,8 @@ Have fun with OpenUI5! **Related Information** -[Working with Controls](https://sdk.openui5.org/#/topic/91f0a22d6f4d1014b6dd926db0e91070 "Controls are used to define the appearance and behavior of screen areas.") +[Working with Controls](https://sdk.openui5.org/topic/91f0a22d6f4d1014b6dd926db0e91070 "Controls are used to define the appearance and behavior of screen areas.") -[Data Binding](https://sdk.openui5.org/#/topic/68b9644a253741e8a4b9e4279a35c247 "You use data binding to bind UI elements to data sources to keep the data in sync and allow data editing on the UI.") +[Data Binding](https://sdk.openui5.org/topic/68b9644a253741e8a4b9e4279a35c247 "You use data binding to bind UI elements to data sources to keep the data in sync and allow data editing on the UI.") -[Routing and Navigation](https://sdk.openui5.org/#/topic/3d18f20bd2294228acb6910d8e8a5fb5 "OpenUI5 offers hash-based navigation, which allows you to build single-page apps where the navigation is done by changing the hash. In this way the browser does not have to reload the page; instead there is a callback to which the app and especially the affected view can react. A hash string is parsed and matched against patterns which will then inform the handlers.") +[Routing and Navigation](https://sdk.openui5.org/topic/3d18f20bd2294228acb6910d8e8a5fb5 "OpenUI5 offers hash-based navigation, which allows you to build single-page apps where the navigation is done by changing the hash. In this way the browser does not have to reload the page; instead there is a callback to which the app and especially the affected view can react. A hash string is parsed and matched against patterns which will then inform the handlers.") diff --git a/packages/walkthrough/steps/03/README.md b/packages/walkthrough/steps/03/README.md index 113f7bc5c..1a66bac0e 100644 --- a/packages/walkthrough/steps/03/README.md +++ b/packages/walkthrough/steps/03/README.md @@ -144,15 +144,15 @@ ui5 add sap.m [Working with Controls](https://sdk.openui5.org/topic/91f0a22d6f4d1014b6dd926db0e91070.html "Controls are used to define the appearance and behavior of screen areas.") -[API Reference: `sap.m.Text`](https://sdk.openui5.orgapi/sap.m.Text) +[API Reference: `sap.m.Text`](https://sdk.openui5.org/#/api/sap.m.Text) -[Samples: `sap.m.Text` ](https://sdk.openui5.orgentity/sap.m.Text) +[Samples: `sap.m.Text` ](https://sdk.openui5.org/#/entity/sap.m.Text) -[API Reference: `sap.ui.core.Control`](https://sdk.openui5.orgapi/sap.ui.core.Control) +[API Reference: `sap.ui.core.Control`](https://sdk.openui5.org/#/api/sap.ui.core.Control) -[API Reference: `sap.ui.core.Element`](https://sdk.openui5.orgapi/sap.ui.core.Element) +[API Reference: `sap.ui.core.Element`](https://sdk.openui5.org/#/api/sap.ui.core.Element) -[API Reference: `sap.ui.base.ManagedObject`](https://sdk.openui5.orgapi/sap.ui.base.ManagedObject) +[API Reference: `sap.ui.base.ManagedObject`](https://sdk.openui5.org/#/api/sap.ui.base.ManagedObject)
    diff --git a/packages/walkthrough/steps/07/README.md b/packages/walkthrough/steps/07/README.md index 9fc8783d8..bb485b224 100644 --- a/packages/walkthrough/steps/07/README.md +++ b/packages/walkthrough/steps/07/README.md @@ -142,6 +142,6 @@ In addition to this, we create a greeting message. We combine the static text "H [Property Binding](https://sdk.openui5.org/topic/91f0652b6f4d1014b6dd926db0e91070.html "With property binding, you can initialize properties of a control automatically and update them based on the data of the model.") -[Binding Path](https://ui5.sap.com/#/topic/2888af49635949eca14fa326d04833b9.html "Binding paths address the different properties and lists in a model and define how a node in the hierarchical data tree can be found.") +[Binding Path](https://sdk.openui5.org/topic/2888af49635949eca14fa326d04833b9.html "Binding paths address the different properties and lists in a model and define how a node in the hierarchical data tree can be found.") [API Reference: `sap.ui.base.ManagedObject.PropertyBindingInfo`](https://sdk.openui5.org/api/sap.ui.base.ManagedObject.PropertyBindingInfo "Configuration for the binding of a managed property.") diff --git a/packages/walkthrough/steps/19/README.md b/packages/walkthrough/steps/19/README.md index 7f70613cf..7796ac74b 100644 --- a/packages/walkthrough/steps/19/README.md +++ b/packages/walkthrough/steps/19/README.md @@ -202,9 +202,9 @@ In the app view we add a second view and assign it to our newly created InvoiceL [Folder Structure: Where to Put Your Files](https://sdk.openui5.org/topic/003f755d46d34dd1bbce9ffe08c8d46a.html "The details described here represent a best practice for structuring an application that features one component, one OData service and less than 20 views. If you're building an app that has more components, OData services and views, you may have to introduce more folder levels than described here.") -[Lists](https://sdk.openui5.org/#/topic/1da158152f644ba1ad408a3e982fd3df.html "Lists have properties and events and they contain list items that inherit from sap.m.ListItemBase, which provides navigation, selection and event features. The list item type determines the way the list item interacts by providing additional features.") +[Lists](https://sdk.openui5.org/topic/1da158152f644ba1ad408a3e982fd3df.html "Lists have properties and events and they contain list items that inherit from sap.m.ListItemBase, which provides navigation, selection and event features. The list item type determines the way the list item interacts by providing additional features.") -[List Binding (Aggregation Binding)](https://sdk.openui5.org/#/topic/91f057786f4d1014b6dd926db0e91070.html "List binding (or aggregation binding) is used to automatically create child controls according to model data.") +[List Binding (Aggregation Binding)](https://sdk.openui5.org/topic/91f057786f4d1014b6dd926db0e91070.html "List binding (or aggregation binding) is used to automatically create child controls according to model data.") [API Reference: `sap.ui.base.ManagedObject.AggregationBindingInfo`](https://sdk.openui5.org/api/sap.ui.base.ManagedObject.AggregationBindingInfo) diff --git a/packages/walkthrough/steps/20/README.md b/packages/walkthrough/steps/20/README.md index f312729c6..84d405454 100644 --- a/packages/walkthrough/steps/20/README.md +++ b/packages/walkthrough/steps/20/README.md @@ -135,7 +135,7 @@ As you can see above, the example uses a special binding syntax for the `number` [API Reference: sap.ui.base.ManagedObject.PropertyBindingInfo](https://sdk.openui5.org/api/sap.ui.base.ManagedObject.PropertyBindingInfo) -[API Reference: `sap.ui.model.type`](https://ui5.sap.com/#/api/sap.ui.model.type) +[API Reference: `sap.ui.model.type`](https://sdk.openui5.org/#/api/sap.ui.model.type) [API Reference: sap.ui.model.type.Currency](https://sdk.openui5.org/api/sap.ui.model.type.Currency) diff --git a/packages/walkthrough/steps/27/README.md b/packages/walkthrough/steps/27/README.md index 60abc3d79..66b00a53e 100644 --- a/packages/walkthrough/steps/27/README.md +++ b/packages/walkthrough/steps/27/README.md @@ -178,7 +178,7 @@ The test suite serves as the entry point for all tests within our project such a The previously created generic `Test.qunit.html` file is referenced as the test `page` and configured with query parameters so that individual tests can be run. The placeholders `{suite}` and `{name}` are replaced with the suite and test names respectively. -For more information, read [Test Starter - Concept and Basic Setup](https://sdk.openui5.org/#/topic/22f50c0f0b104bf3ba84620880793d3f). +For more information, read [Test Starter - Concept and Basic Setup](https://sdk.openui5.org/topic/22f50c0f0b104bf3ba84620880793d3f). ```ts // webapp/test/testsuite.qunit.ts
  • DF>tLM|)!4)om^b~nMbSU=R!APj^dYcrnzGbk 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 { + const bundle: ResourceBundle = await (this.getView().getModel("i18n") as ResourceModel).getResourceBundle() as ResourceBundle; + + return URLHelper.normalizeEmail( + `${firstName}.${lastName}@example.com`, + bundle.getText("mailSubject", [firstName]), + bundle.getText("mailBody") + ); + } + + public formatStockValue(unitPrice: float, stockLevel: number, currencyCode: string): string { + const currency: Currency = new Currency(); + return currency.formatValue([unitPrice * stockLevel, currencyCode], "string"); + } +} diff --git a/packages/databinding/steps/12/webapp/i18n/i18n.properties b/packages/databinding/steps/12/webapp/i18n/i18n.properties new file mode 100644 index 000000000..c0907bcb2 --- /dev/null +++ b/packages/databinding/steps/12/webapp/i18n/i18n.properties @@ -0,0 +1,24 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=A simple app that explains how to use data binding features of OpenUI5 + +# Field labels +firstName=First Name +lastName=Last Name +enabled=Enabled +address=Address +salesAmount=Sales Amount + +# Screen titles +panel1HeaderText=Data Binding Basics +panel2HeaderText=Address Details +panel3HeaderText=Aggregation Binding + +# Email +sendEmail=Send EMail +mailSubject=Hi {0}! +mailBody=How are you? + +# Product list +productListTitle=Product List +stockValue=Current Stock Value diff --git a/packages/databinding/steps/12/webapp/i18n/i18n_de.properties b/packages/databinding/steps/12/webapp/i18n/i18n_de.properties new file mode 100644 index 000000000..217e279b8 --- /dev/null +++ b/packages/databinding/steps/12/webapp/i18n/i18n_de.properties @@ -0,0 +1,24 @@ +# App Descriptor +appTitle=Data Binding Tutorial +appDescription=Eine einfache Anwendung zur Erkl\u00e4rung der UI5 Data Binding Funktionen + +# Field labels +firstName=Vorname +lastName=Nachname +enabled=Aktiviert +address=Adresse +salesAmount=Verk\u00e4ufe bis zum heutigen Datum + +# Screen titles +panel1HeaderText=Data Binding Grundlagen +panel2HeaderText=Adressdetails +panel3HeaderText=Aggregation Binding + +# Email +sendEmail=E-Mail versenden +mailSubject=Hallo {0}! +mailBody=Wie geht es dir? + +# Product list +productListTitle=Artikelliste +stockValue=Lagerbestand Wert diff --git a/packages/databinding/steps/12/webapp/index-cdn.html b/packages/databinding/steps/12/webapp/index-cdn.html new file mode 100644 index 000000000..e2b2e4833 --- /dev/null +++ b/packages/databinding/steps/12/webapp/index-cdn.html @@ -0,0 +1,22 @@ + + + + + + Data Binding Tutorial + + + +