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(