diff --git a/packages/databinding/README.md b/packages/databinding/README.md index 3e47dd024..6a8582610 100644 --- a/packages/databinding/README.md +++ b/packages/databinding/README.md @@ -14,7 +14,7 @@ The UI uses data binding to bind controls to the model which holds the applicati ![Preview of three panels of the OpenUI5 application that is going to be built in this tutorial. Contains three panels with name input fields, address details, and a product list.](assets/Tutorial_Data_Binding_896048e.png "The Data Binding sample app with panels for name, address, and a product list") -> :tip: +> 💡 > You don't have to do all tutorial steps sequentially, you can also jump directly to any step you want. Just download the code from the previous step, copy it to your workspace, and ensure that the application runs by calling the `webapp/index.html` file. *** diff --git a/packages/databinding/steps/02/README.md b/packages/databinding/steps/02/README.md index d7bb419b6..0af8d72d3 100644 --- a/packages/databinding/steps/02/README.md +++ b/packages/databinding/steps/02/README.md @@ -13,7 +13,7 @@ You can define the business data within a model using various formats: - Your own custom format \(not covered in this tutorial\) -> :note: +> 📝 > There's also a special type of model called a "resource model". This model type is used as a wrapper object around a resource bundle file. The names of such files must end with `.properties`. They're typically used for holding language-specific text. > > We'll use this in [Step 6: Resource Models](../06/README.md). @@ -81,7 +81,7 @@ Generally speaking, a model object holding business data should be bound to the The text that is displayed on the UI is still hard-coded and not taken from the model. We'll bind the property `greetingText` to our UI control in the next step. -> :note: +> 📝 > You can set models on every control by calling `setModel()`. The model is then propagated to all aggregated child controls \(and their children, and so on …\). All child controls then have access to that model. *** diff --git a/packages/databinding/steps/04/README.md b/packages/databinding/steps/04/README.md index 30d5db104..0604a592f 100644 --- a/packages/databinding/steps/04/README.md +++ b/packages/databinding/steps/04/README.md @@ -69,7 +69,7 @@ Replace the content of the `App.view.xml` file with the following content: ``` -> :note: +> 📝 > Requiring `sap/ui/layout/form/ColumnLayout` is needed because we use the `ColumnLayout` as `layout` for the `sap/ui/layout/form/SimpleForm`. > The `sap/ui/layout/form/SimpleForm` requires the configured layout, in case it's not done by the consumer but this may cause an additional rendering cycle if rendering starts before the layout finished loading. diff --git a/packages/databinding/steps/05/README.md b/packages/databinding/steps/05/README.md index 526050298..65d60983b 100644 --- a/packages/databinding/steps/05/README.md +++ b/packages/databinding/steps/05/README.md @@ -81,7 +81,7 @@ If you wish to alter the binding mode, you've got two options: - Specify the data binding mode for a specific binding instance by using the `oBindingInfo.mode` parameter. This change only applies to this data binding instance. Any other binding instances will continue to use the model's default binding mode.For more information, see [API Reference: `sap.ui.base.ManagedObject.bindProperty`](https://sdk.openui5.org/#/api/sap.ui.base.ManagedObject/methods/bindProperty). -> :note: +> 📝 > There are two important points to understand about alterations to a model object's data binding mode: > > - If you alter the default binding mode of a model \(as in the example above\), **all** binding instances created after that point in time will use the altered binding mode, unless you explicitly say otherwise. diff --git a/packages/databinding/steps/06/README.md b/packages/databinding/steps/06/README.md index 6fc42c302..2e282a863 100644 --- a/packages/databinding/steps/06/README.md +++ b/packages/databinding/steps/06/README.md @@ -79,7 +79,7 @@ Also add the `i18n` property to the `sap.app` section and modify the `title` and ... ``` -> :note: +> 📝 > The configured `supportedLocales` represent the following i18n files present \(see Step 7\): > > - `""` - `i18n/i18n.properties` diff --git a/packages/databinding/steps/08/README.md b/packages/databinding/steps/08/README.md index 6cb7ce781..2ff88b688 100644 --- a/packages/databinding/steps/08/README.md +++ b/packages/databinding/steps/08/README.md @@ -114,7 +114,7 @@ panel1HeaderText=Data Binding Grundlagen panel2HeaderText=Adressdetails ``` -> :note: +> 📝 > The resource bundle files now contain new properties for the address and a new panel header text. Both panel properties are numbered. > > In the XML view, inside the curly brackets for the binding path of the `htmlText` element, you'll notice that the first character is a forward slash. This is necessary for binding paths that make absolute references to properties in JSON and OData models, but you must not use it for resource models. After the first forward slash character, the binding path syntax uses the object name and the property names separated by forward slash characters \(`{/address/street}`\). diff --git a/packages/databinding/steps/09/README.md b/packages/databinding/steps/09/README.md index 694002096..a92744b1e 100644 --- a/packages/databinding/steps/09/README.md +++ b/packages/databinding/steps/09/README.md @@ -83,7 +83,7 @@ In our custom formatter, we set the first and last name currently in the model a In the `formatMail` function, we use the `sap.m.URLHelper.normalizeEmail` function that expects an e-mail address, a mail subject, and a text body. When a user follows the link, their default email client will open with these parameters. For more information, see [API Reference: `sap.m.URLHelper.normalizeEmail`](https://sdk.openui5.org/#/api/sap.m.URLHelper/methods/normalizeEmail). The `mailSubject` resource bundle text contains a placeholder for the recipient's first name \(see below\). Therefore, we provide the name with `[firstName]`. -> :note: +> 📝 > For a detailed description of the e-mail link format, see [MDN - Creating hyperlinks: Email links](https://developer.mozilla.org/de/docs/Web/Guide/HTML/Email_links). Enhance the `App.view.xml` file as shown below: @@ -132,7 +132,7 @@ For more complex bindings, we can't use the simple binding syntax with the curly This is a reference to the function that receives the parameters listed in the `parts` array. Whatever value the formatter function returns becomes the value set for the `href` property. The dot `formatMail`\) at the beginning of the formatter tells OpenUI5 to look for a `formatMail` function on the controller instance of the view. If you don't use the dot, the function will be resolved by looking into the global namespace. -> :note: +> 📝 > When using formatter functions, the binding automatically switches to "one-way". Therefore, you can’t use a formatter function for "two-way" scenarios. However, you can use data types \(which we explain in the following steps\). Add the `# E-mail` section to the `i18n.properties` and `i18n_de.properties` files as shown below. diff --git a/packages/databinding/steps/13/README.md b/packages/databinding/steps/13/README.md index 71f3ee089..7d04eee0f 100644 --- a/packages/databinding/steps/13/README.md +++ b/packages/databinding/steps/13/README.md @@ -196,7 +196,7 @@ Discontinued=Eingestellt Now, you can click on an element in the list and view its details in the panel below. You can even edit these details, and the changes are directly reflected in the list because we use two-way binding. -> :note: +> 📝 > Element bindings can also be relative to their parent context. *** diff --git a/packages/navigation/README.md b/packages/navigation/README.md index 6dae661e4..85c1bb393 100644 --- a/packages/navigation/README.md +++ b/packages/navigation/README.md @@ -10,7 +10,7 @@ For example, a classical Web application might display the employee’s resume p The information in the hash, namely everything that is following the `#` character, is interpreted by the router. -> :note: +> 📝 > This tutorial does not handle cross-app navigation with the SAP Fiori launchpad. However, the concepts described in this tutorial are also fundamental for navigation and routing between apps in the SAP Fiori launchpad. We will create a simple app displaying the data of a company’s employees to show typical navigation patterns and routing features. The complete flow of the application can be seen in the figure below. We'll start with the home page which lets users do the following: @@ -27,7 +27,7 @@ We will create a simple app displaying the data of a company’s employees to sh Throughout this tutorial we will add features for navigating to pages and bookmarking them. We will add backward and forward navigation with common transition animations \(slide, show, flip, etc.\). We will add more pages to the app and navigate between them to show typical use cases. We will even learn how to implement features for bookmarking a specific search, table sorting via filters, and dialogs. -> :tip: +> 💡 > You don't have to do all tutorial steps sequentially, you can also jump directly to any step you want. Just download the code from the previous step and make sure that the application runs as intended. > > You can view the samples for all steps here in this repository. diff --git a/packages/navigation/steps/01/README.md b/packages/navigation/steps/01/README.md index 74ff2888a..ba6651dc5 100644 --- a/packages/navigation/steps/01/README.md +++ b/packages/navigation/steps/01/README.md @@ -48,7 +48,7 @@ webapp/ └── App.view.xml ``` -> :note: +> 📝 > The content of the `localService` folder will not be changed in this tutorial. The `i18n` folder will always contain the `i18n.properties` file only. Therefore, we will show both subfolders collapsed in the following steps. ## The Initial App diff --git a/packages/navigation/steps/02/README.md b/packages/navigation/steps/02/README.md index 04bb5fb73..2c07f0221 100644 --- a/packages/navigation/steps/02/README.md +++ b/packages/navigation/steps/02/README.md @@ -96,7 +96,7 @@ Single-page applications based on OpenUI5 can use a so-called “router” to di This section contains the global router configuration and default values that apply for all routes and targets. The property `routerClass` is special as it determines the router implementation. The default value is `sap.ui.core.routing.Router`. Here, we set the `routerClass` to `sap.m.routing.Router`, because we implement an app based on `sap.m`. All other properties in `config` are given to the router instance. For example, we define where our views are located in the app. To load and display views automatically, we also specify the `controlId` of the control that is used to display the pages and the aggregation \(`controlAggregation`\) that will be filled when a new page is displayed. We will create only XMLviews in this tutorial, so we can set the `viewType` property to `XML`. All our views will be available in the `view` folder of the namespace `ui5.tutorial.navigation`, so we can set the `path` to `ui5.tutorial.navigation.view`. The `transition` allows us to set a default value for how the transition should happen; you can choose between `slide` \(default\), `flip`, `fade`, and `show`. All parameters of the config section can be overruled in the individual route and target definitions if needed. - > :note: + > 📝 > The possible values for `routerClass` are `sap.ui.core.routing.Router`, `sap.m.routing.Router`, or any other subclasses of `sap.ui.core.routing.Router`. Compared to `sap.ui.core.routing.Router` the `sap.m.routing.Router` is optimized for mobile apps and adds the properties `level`, `transition` and `transitionParameters` which can be specified for each route or target created by the `sap.m.routing.Router`. The `transitionParameters` can also be used for custom transitions. Please check the *API Reference* for more information. - `routes` @@ -109,7 +109,7 @@ Single-page applications based on OpenUI5 can use a so-called “router” to di This basic routing configuration was easy enough. However, you can’t see it in action until you have initialized the router. -> :note: +> 📝 > As of OpenUI5 version 1.30, we recommend that you define the routing in the `manifest.json` descriptor file using routes and targets. In older versions of OpenUI5, the routing configuration had to be done directly in the metadata section of the component, and with different syntax. ## `webapp/Component.ts/.js` @@ -223,7 +223,7 @@ sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) { Create a file `Home.controller.ts` in the `webapp/controller` folder. The controller for the home view does not contain any custom logic in this step, but we will add some features to it soon. Finally, run the app by calling the `webapp/index.html` file. This will be the entry point for our app in all the next steps. As you can see, the app is initially displaying the home view that we configured as the default pattern in the routing configuration. We have now successfully enabled routing in the app. -> :note: +> 📝 > We think of routing as a set of features that dispatch hash-based URLs to an app's views and manage the views' states. > > Based on the routing configuration, you define the navigation between pages and pass parameters to the target views. diff --git a/packages/navigation/steps/04/README.md b/packages/navigation/steps/04/README.md index bf0c69bb4..20c9b9020 100644 --- a/packages/navigation/steps/04/README.md +++ b/packages/navigation/steps/04/README.md @@ -127,7 +127,7 @@ The function checks if there is a previous hash value in the app history. If so, The third parameter of `navTo("appHome", {}, true /*no history*/);` has the value `true` and makes sure that the hash is replaced. With the line `sap.ui.core.UIComponent.getRouterFor(this)` you can easily access your component’s router throughout the app. To make it even more comfortable, we also add a handy shortcut `getRouter` to the base controller. This function is now available in each subclass as well. It is also used in the `onNavBack` handler to get a reference to the router before calling `navTo`. We now have to implement the reuse in all other controllers. -> :note: +> 📝 > In OpenUI5 there are multiple options to reuse code. We recommend to use a base controller for such helper methods because this allows us to decoratively use the `onNavBack` handler directly in any XML view without adding additional code to the controller. Our base controller is an abstract controller that will not be instantiated in any view. Therefore, the naming convention `*.controller.ts` does not apply, and we can just name the file `BaseController.ts`. By not using the naming convention `*.controller.ts` we can even prevent any unintentional usage in views. ## `webapp/controller/NotFound.controller.ts/.js` @@ -220,7 +220,7 @@ sap.ui.define(["ui5/tutorial/navigation/controller/BaseController"], function (B The same applies to our `Home` controller, we now also inherit from the `BaseController`. -> :note: +> 📝 > In this step we have added the *Back* button. The user can always use the browser’s native *Back* button as well. Each app can freely configure the behavior of the *Back* button. However, there is no clean way to apply the same logic for the browser’s *Back* button in single-page applications. Tweaking the browser history or using other quirks for cancelling backward or forward navigation is not recommended due to the implementation details of the browsers. The browser’s *Back* button always uses the browser history while the *Back* button of the app can make use of the browser history **or** can implement its own navigation logic. Make sure to understand this difference and only control the *Back* button inside the app. ## Conventions diff --git a/packages/navigation/steps/05/README.md b/packages/navigation/steps/05/README.md index 3557b07dc..f460c25bf 100644 --- a/packages/navigation/steps/05/README.md +++ b/packages/navigation/steps/05/README.md @@ -77,7 +77,7 @@ Inside the `onDisplayNotFound` handler we get a reference to the `Targets` helpe The `sap.m.routing.Targets` object itself can be retrieved by calling `getTargets()` on the router. It provides a convenient way for placing views into the correct containers of your application. The main benefits of targets are structuring and lazy loading: you just configure the views in the routing configuration and you do not have to load the views until you really need them. -> :note: +> 📝 > In the example code we get a reference to the `sap.m.routing.Targets` object by calling `getTargets()` on `this.getRouter()` from the base controller. However, you could also get a reference to the `sap.m.routing.Targets` object by calling `this.getOwnerComponent().getRouter().getTargets()` or `this.getOwnerComponent().getTargets()`. If you now call the app and press the *Display Not Found* button you see that the `notFound` target is displayed without changing the URL. That was easy, but suddenly our app’s *Back* button does not work anymore. The bug we have just introduced illustrates an interesting navigation trap. The application hash is still empty since we just display the target and did not hit a route. diff --git a/packages/navigation/steps/06/README.md b/packages/navigation/steps/06/README.md index c478e0c53..04394e326 100644 --- a/packages/navigation/steps/06/README.md +++ b/packages/navigation/steps/06/README.md @@ -183,7 +183,7 @@ The `employees` entry in the `targets` section references the `ui5.tutorial.navi The view that we are about to create has to be placed in the `webapp/view/employee` folder accordingly. This approach helps to structure the views of the app according to business objects and to better understand the navigation patterns of the app in larger projects. -> :note: +> 📝 > We could also have left out the `path` property to use the default `path` defined in the `config` section. In that case, we would have to change the `name` to `employee.EmployeeList` to achieve the same effect. Setting the `level` to `2` helps the router to determine how to animate the \(in our case\) `slide` transition. For us, this means that a navigation from the home page to the `employees` target will be animated with a “Slide to Left” animation. In contrast to that, the back navigation from the `employees` target to the home page will be animated with a “Slide to Right” animation. This behavior is due to the fact that the home page has a lower `level` than the `employees` target. diff --git a/packages/navigation/steps/07/README.md b/packages/navigation/steps/07/README.md index d1f60cfda..079f55905 100644 --- a/packages/navigation/steps/07/README.md +++ b/packages/navigation/steps/07/README.md @@ -194,7 +194,7 @@ Next, we have to create the view `employees.Employee`; for better illustration t Create the file `Employee.view.xml` inside the `webapp/view/employee` folder. This employee view displays master data for an employee in a panel with a `SimpleForm` control: first name, last name and so on. The data comes from a relative data binding that is set on the view level as we can see in the controller later. As we are focusing on the navigation aspects in this tutorial, we won’t go into detail on the controls of the view. Just copy the code. -> :note: +> 📝 > 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. @@ -288,7 +288,7 @@ In `_onRouteMatched` we call `bindElement()` on the view to make sure that the d We also add an event handler to the `change` event as a private function `_onBindingChange`. It checks if the data could be loaded by querying the binding context of the view. As seen in the previous steps, we will display the `notFound` target if the data could not be loaded. -> :note: +> 📝 > Instead of calling `attachMatched(…)` on a route we could also call `attachRouteMatched(…)` directly on the router. However, the event for the latter is fired for every matched event of any route in the whole app. We don’t use the latter because we would have to implement an additional check for making sure that current route is the route that has been matched. We want to avoid this extra overhead and register on the route instead. ## webapp/view/employee/EmployeeList.view.xml diff --git a/packages/navigation/steps/08/README.md b/packages/navigation/steps/08/README.md index 4fe7b2e45..8dd918d60 100644 --- a/packages/navigation/steps/08/README.md +++ b/packages/navigation/steps/08/README.md @@ -256,7 +256,7 @@ In the routing configuration, we add a new route `employeeResume` which referenc The target `employeeResume` references the view `employee.Resume` that we are about to create. The target’s `level` is `4`; compared to the employee target this is one level lower again. To configure a flip navigation, we simply set the transition of our target to `flip`. Together with the correct `level` configuration this will trigger the correct forward and backward flip navigation whenever the target is displayed. -> :note: +> 📝 > Possible values for the `transition` parameter are: > > - `slide` \(default\) @@ -407,7 +407,7 @@ Create a file `Resume.controller.ts` in the `webapp/controller/employee` folder. Create a file `ResumeProjects.view.xml` in the `webapp/view/employee` folder. This view does not have a controller as we don’t need it. It just displays a `Text` control with the projects text of the selected employee. It illustrates that using nested views works just fine in combination with navigation and routing in OpenUI5. -> :note: +> 📝 > For more complex applications, the performance is significantly increased if parts of the UI are only loaded when the user is actively selecting it. In this example, the view is always loaded even though the user never decided to display the project information. In the next steps, we will extend the UI so that the content is loaded “lazy” by OpenUI5 only when the filter item is clicked. The back-end service will fetch the data only on request and the UI will only have to be updated with the selected data instead of loading all data. ## webapp/i18n/i18n.properties diff --git a/packages/navigation/steps/10/README.md b/packages/navigation/steps/10/README.md index fcbc7b32b..b1ed85357 100644 --- a/packages/navigation/steps/10/README.md +++ b/packages/navigation/steps/10/README.md @@ -281,7 +281,7 @@ We add the `resumeTabHobbies` and `resumeTabNotes` targets to the descriptor fil The `resumeTabHobbies` target sets the parent property to `employeeResume`. The parent property expects the name of another target. In our case, this makes sure that the view from the parent target `employeeResume` is loaded before the target `resumeTabHobbies` is displayed. This can be considered as a “view dependency”. By setting the `controlId` and `controlAggregation` properties the router places the view `ResumeHobbies` into the `content` aggregation of the `IconTabFilter` control with ID `hobbiesTab`. We also set a parameter `id` to a custom ID to illustrate how you could overrule a hard-coded ID inside a view. -> :note: +> 📝 > Each target can define only one parent with its parent property. This is similar to the OpenUI5 control tree where each control can have only one parent control \(accessed with the method `getParent()` of `sap.ui.base.ManagedObject`\). The `controlId` property always references a control inside the parent view that is specified with the `parent` target. Now we add the `resumeTabNotes` target similar to the `Hobbies` target. The `resumeTabNotes` target defines the parent target `employeeResume` as well, because they share the same parent view. We place the `ResumeNotes` view into the `content` aggregation of the `IconTabFilter` control with ID `notesTab`. diff --git a/packages/navigation/steps/11/README.md b/packages/navigation/steps/11/README.md index 41624a0b1..66d515f2b 100644 --- a/packages/navigation/steps/11/README.md +++ b/packages/navigation/steps/11/README.md @@ -184,7 +184,7 @@ We extend our current routing configuration with a new route `employeeOverview`. Both targets `employeeOverviewTop` and `employeeOverviewContent` reference the target `employeeOverview` as their parent target because we want to place them both inside the parent. Please also note that we also introduce a new layer `overview` in the `path` property. -> :note: +> 📝 > The order of the routing configuration matters here, because the router stops matching additional routes when the first match is found. You can override this behavior if you set parameter `greedy` to `true` on the route. Then the route will always be matched when the pattern matches the current URL, even if another route has been matched before. The `greedy` option comes from the underlying `Crossroads.js` library, a popular routing library. A common use case for using `greedy` is configuring targets without views and then listening for route-matched events. Now we create both targets `employeeOverviewTop` and `employeeOverviewContent` as well as their parent target `employeeOverview`. On the parent target we set `level` to `2` to ensure a correct transition animation. In the targets, we also configure where the corresponding views of the children shall be displayed by setting the parameters `controlId` and `controlAggregation` to a control ID of a `sap.ui.layout.HorizontalLayout` that we are about to create in a new view. @@ -545,7 +545,7 @@ Open `webapp/index.html#/employees/overview` and check the new views. As you can Of course, you can also search the table and change the sorting. When the sorting dialog opens, it creates a block layer so that the back button and other controls cannot be accessed. However, you can still use the back button of the browser. As you can see, the dialog is closed automatically by the router before navigating. -> :note: +> 📝 > The default behavior of the `sap.m` router is that all dialogs are closed when the hash changes \(i.e. when calling `navTo`, `display` or pressing the back button of the browser\). You can change this default behavior by calling `getTargetHandler().setCloseDialogs(false)` on the router or on the `Targets` object. However, we have one problem yet to solve: the search and table ordering are not bookmarkable. Fortunately, we have additional navigation features at hand and you will see how this works in the next steps diff --git a/packages/navigation/steps/16/README.md b/packages/navigation/steps/16/README.md index 620994555..7e246dbce 100644 --- a/packages/navigation/steps/16/README.md +++ b/packages/navigation/steps/16/README.md @@ -75,7 +75,7 @@ sap.ui.define(["ui5/tutorial/navigation/controller/BaseController", "sap/base/Lo All we need to do is listen to the bypassed event on the router. If the bypassed event is triggered, we simply get the current hash and log a message. In an actual app this is probably the right place to add some application analysis features, i.e. sending analytical logs to the back end for later evaluation and processing. This could be used to improve the app, for example, to find out why the user called the app with an invalid hash. -> :note: +> 📝 > We have chosen to place this piece of code into the `App` controller because this is a global feature of the app. However, you could also place it anywhere else, for example in the `NotFound` controller file or in a helper module related to analysis. Now try to access `webapp/index.html#/thisIsInvalid` while you have your browser console open. As you can see, there is a message that issues a faulty hash. Furthermore, our `NotFound` page is displayed. diff --git a/packages/odatav4/README.md b/packages/odatav4/README.md index 08cf9f9fc..4ac636ccc 100644 --- a/packages/odatav4/README.md +++ b/packages/odatav4/README.md @@ -6,7 +6,7 @@ OData is a standard protocol for creating and consuming data using simple HTTP a We start with an initial app that simply retrieves data from an OData V4 service and displays it as a plain list, and progressively grow it across 11 steps. -> :tip: +> 💡 > You don't have to do all tutorial steps sequentially, you can also jump directly to any step you want. Just download the code from the previous step, and start there. > > You can view and download the files for all steps in the Demo Kit at [OData V4](https://sdk.openui5.org/#/entity/sap.ui.core.tutorial.odatav4). diff --git a/packages/odatav4/steps/01/README.md b/packages/odatav4/steps/01/README.md index c61a68f24..754d26f77 100644 --- a/packages/odatav4/steps/01/README.md +++ b/packages/odatav4/steps/01/README.md @@ -82,7 +82,7 @@ The `manifest.json` descriptor file contains the app configuration. In the `sap. ### Mock server \(`webapp/localService/*`\) -> :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.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. diff --git a/packages/odatav4/steps/02/README.md b/packages/odatav4/steps/02/README.md index d8fe08866..a4e67f44a 100644 --- a/packages/odatav4/steps/02/README.md +++ b/packages/odatav4/steps/02/README.md @@ -133,7 +133,7 @@ sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast", "sap/m/Messag We add the event handler `onRefresh` to the controller. In this method, we retrieve the current data binding of the table. If the binding has unsaved changes, we display an error message, otherwise we call `refresh()` and display a success message. -> :note: +> 📝 > At this stage, our app cannot have unsaved changes. We will change this in Step 6. We also add the private method `_getText` to retrieve translatable texts from the resource bundle \(`i18n` model\). @@ -197,7 +197,7 @@ We add the tooltip and message texts to the `properties` file. To get more insight into the client-server communication, we open the *Console* tab of the browser developer tools and then reload the app. -> :note: +> 📝 > To monitor the client-server communication in a productive app, you would use the *Network* tab of the developer tools. > > In this tutorial, we are using a mock server instead of a real OData service so that we can run the code in every environment. The mock server does not generate any network traffic, so we use the *Console* tab to monitor the communication. diff --git a/packages/odatav4/steps/03/README.md b/packages/odatav4/steps/03/README.md index 147814c30..1b0d246d5 100644 --- a/packages/odatav4/steps/03/README.md +++ b/packages/odatav4/steps/03/README.md @@ -55,7 +55,7 @@ In the `manifest.json` descriptor file, we add the `"handleValidation": true` se We now run the app using the `index.html` file and enter values that don't match the type and constraints given in the metadata file. For example, enter the string value `Young at Heart` in field *Age*, which requires an integer input \(OpenUI5 type `sap.ui.model.odata.type.Int64`, corresponding to OData type `Edm.Int64`\), or remove an entry from the *User Name* or *First Name* fields, which are mandatory. Fields with incorrect entries are highlighted and an error message is displayed. -> :note: +> 📝 > If you explicitly define a type in the binding info of a control, the automatic type detection for that binding will be turned off. For example, if you change the `Input` for `Age` in the view to ` :tip: +> 💡 > To see the metadata of an OData service, you append the `$metadata` variable to the URL of the service. You can try this, for example, with [http://services.odata.org/TripPinRESTierService/](http://services.odata.org/TripPinRESTierService/) and [http://services.odata.org/TripPinRESTierService/$metadata](http://services.odata.org/TripPinRESTierService/$metadata) **Related Information** diff --git a/packages/odatav4/steps/04/README.md b/packages/odatav4/steps/04/README.md index eb10ff15f..fdfd04060 100644 --- a/packages/odatav4/steps/04/README.md +++ b/packages/odatav4/steps/04/README.md @@ -142,12 +142,12 @@ The **`onSearch`** event handler filters the table for people whose last name co When the request is triggered, only entities that match the given filter criteria are requested from the OData V4 service. -> :note: +> 📝 > Filters of OData services are case-sensitive. If you prefer a non case-sensitive search, implement it in the controller logic. The **`onSort`** event handler requests the data unordered, or in ascending order, or descending order. Each time the *Sort* button is clicked, the next sort order is applied. The sorting is applied to the table by calling the `sort` method of the list binding with a new `sap.ui.model.Sorter`. -> :note: +> 📝 > The features of filtering and sorting can also be combined. We add the `order` property to variable `oJSONData` in `onInit` method. This property stores the current sort order. @@ -239,7 +239,7 @@ We add the `order` property to variable `oJSONData` in `onInit` method. This pro We add the `$count : true` parameter to tell the OData service to send the number of entities. With this setting, we automatically get the full number of entities \(20\) and the number of displayed entities \(10\) beneath the *More* button. -> :note: +> 📝 > The live TripPin service does not support the `$count` parameter yet. If you use the live service instead of the mock server, as described in Step 2, leave out the `$count` parameter. In the `OverflowToolbar`, we add a *Search* field and a *Sort* button with their events. diff --git a/packages/odatav4/steps/05/README.md b/packages/odatav4/steps/05/README.md index 5f736c3ea..54a91e3a5 100644 --- a/packages/odatav4/steps/05/README.md +++ b/packages/odatav4/steps/05/README.md @@ -32,7 +32,7 @@ In the previous steps, batch processing was turned off, so that we could monitor We now run the app and open the browser developer tools. On the *Console* tab, we clear all messages and choose the *Refresh* button. -> :tip: +> 💡 > Change the settings of the *Console* so that it only displays information messages, not warnings and errors, to make it easier to find the messages we're looking for. We see that the request is now bundled: To read the user data, the app now sends a `POST` request instead of a `GET` request to the server. The URL of the `POST` request does not include the `path` to the data we want. Instead it ends with `$batch` that indicates that this is a batch request. diff --git a/packages/odatav4/steps/08/README.md b/packages/odatav4/steps/08/README.md index 3dc4f2009..b879135a8 100644 --- a/packages/odatav4/steps/08/README.md +++ b/packages/odatav4/steps/08/README.md @@ -73,7 +73,7 @@ We call that action by first creating a deferred operation binding on the model. The invocation is asynchronous; the `invoke` method therefore returns a `Promise`. We attach simple success and error handlers to that `Promise` by calling its `then` method. -> :note: +> 📝 > Many of the methods in the OData V4 API of OpenUI5 return a `Promise` to manage asynchronous processing ## webapp/view/App.view.xml diff --git a/packages/quickstart/README.md b/packages/quickstart/README.md index 618b4f173..cd306b21b 100644 --- a/packages/quickstart/README.md +++ b/packages/quickstart/README.md @@ -10,7 +10,7 @@ We first introduce you to the basic development paradigms like *Model-View-Contr ![Preview of the OpenUI5 application that is going to be built in this tutorial. Contains a Hello World upper part with buttons and a text input. The lower part shows list of invoices with details, grouped by vendor names.](steps/03/assets/loio79e1157d948c488c9717ef840fa9b396_LowRes.png). -> :tip: +> 💡 > You don't have to do all tutorial steps sequentially, you can also jump directly to any step you want. Just download the code from the previous step and make sure that the application runs as intended. > > You can view the samples for all steps here in this repository. diff --git a/packages/quickstart/steps/01/README.md b/packages/quickstart/steps/01/README.md index 2c287cf99..abc1f14b2 100644 --- a/packages/quickstart/steps/01/README.md +++ b/packages/quickstart/steps/01/README.md @@ -114,7 +114,7 @@ Create a new file named `manifest.json` in the webapp folder; it's also known as } ``` -> :note: +> 📝 > In this tutorial step, we focus on adding the absolute minimum configuration to the app descriptor file. In certain development environments you might encounter validation errors due to missing settings. However, for the purposes of this tutorial you can safely ignore these errors. In [Step 10: Descriptor for Applications](../10/README.md) we'll examine the purpose of the file in detail and configure some further options. *** @@ -283,7 +283,7 @@ Next, we have to configure the tooling extension we installed from npm to our U - All our custom middleware extensions will be called after the `compression` middleware. -> :info: +> ℹ️ > Middleware configurations are applied in the order in which they are defined.
@@ -321,7 +321,7 @@ Now you can benefit from live reload on changes and built framework resources at

-> :note: +> 📝 > During its initial run, the `ui5-middleware-serveframework` middleware will build the framework, which can take a while. In all following steps, the build will not happen again and the framework is served from the built resources.   diff --git a/packages/quickstart/steps/03/README.md b/packages/quickstart/steps/03/README.md index bc5874c70..80a9887de 100644 --- a/packages/quickstart/steps/03/README.md +++ b/packages/quickstart/steps/03/README.md @@ -48,7 +48,7 @@ Let's spice up our app by adding some more UI controls. We add two more librarie ``` -> :tip: +> 💡 > To browse all available controls and libraries, see the [Samples](https://sdk.openui5.org/#/controls). ### webapp/App.view.xml diff --git a/packages/walkthrough/README.md b/packages/walkthrough/README.md index dfabe1918..d640abe21 100644 --- a/packages/walkthrough/README.md +++ b/packages/walkthrough/README.md @@ -10,7 +10,7 @@ We first introduce you to the basic development paradigms like *Model-View-Contr ![Preview of the OpenUI5 application that is going to be built in this tutorial. Contains a Hello World upper part with buttons and a text input. The lower part shows list of invoices with details, grouped by vendor names.](steps/38/assets/loiofb12cea5ac9b45bb9007aac5a1a8689f_LowRes.png) -> :tip: +> 💡 > You don't have to do all tutorial steps sequentially, you can also jump directly to any step you want. Just download the code from the previous step and make sure that the application runs as intended. > > You can view the samples for all steps here in this repository. diff --git a/packages/walkthrough/steps/01/README.md b/packages/walkthrough/steps/01/README.md index fe81609b8..44e11eedd 100644 --- a/packages/walkthrough/steps/01/README.md +++ b/packages/walkthrough/steps/01/README.md @@ -47,7 +47,7 @@ In our webapp folder, we create a new HTML file named `index.html` and copy the ``` -> :note: +> 📝 > An HTML document consists basically of two sections: head and body. The head part will be used by the browser to process the document. > > Using meta tags, we can influence the behavior of the browser. In this case, we tell the browser to use `UTF-8` as the document character set. @@ -66,7 +66,7 @@ Let's start by creating a new file named `manifest.json` in the webapp folder an Since we want our app to support not only the latest OpenUI5 version but rather the latest long-term maintenance version, which is OpenUI5 1.148, we set the format version to `2.8.0`. - > :note: + > 📝 > The manifest version should not necessarily align directly with the OpenUI5 version being used. Instead, choose the descriptor version that matches the requirements or supports the features you intend to use in your application. To find the appropriate `_version` for each OpenUI5 release, refer to [Descriptor for Applications, Components, and Libraries \(manifest.json\)](https://sdk.openui5.org/topic/be0cf40f61184b358b5faedaec98b2da.html) or the [mapping.json](https://github.com/SAP/ui5-manifest/blob/main/mapping.json) file of the [ui5-manifest](https://github.com/SAP/ui5-manifest/) project. @@ -79,7 +79,7 @@ Since we want our app to support not only the latest OpenUI5 version but rather - `title`: This defines the title of the application, which appears in application management tools like the SAP Fiori launchpad. - > :note: + > 📝 > It is advisable to make the title language-dependent. We'll cover implementing language-dependent titles in [Step 10: Manifest (Descriptor for Applications)](../10/README.md), but for now we'll use a static title. - `applicationVersion`: This is used to denote the version of the application using semantic versioning principles. It's typically used for tracking and managing changes to the application over time. @@ -98,7 +98,7 @@ Since we want our app to support not only the latest OpenUI5 version but rather } ``` -> :note: +> 📝 > In this tutorial step, we focus on adding the absolute minimum configuration to the app descriptor file. In certain development environments you might encounter validation errors due to missing settings. However, for the purposes of this tutorial you can safely ignore these errors. In [Step 10: Descriptor for Applications](../10/README.md) we'll examine the purpose of the file in detail and configure some further options. *** diff --git a/packages/walkthrough/steps/02/README.md b/packages/walkthrough/steps/02/README.md index 5ac1d2d0a..2233fc49d 100644 --- a/packages/walkthrough/steps/02/README.md +++ b/packages/walkthrough/steps/02/README.md @@ -153,7 +153,7 @@ We initialize the core modules with the following configuration options: ``` -> :note: +> 📝 > The namespace is a unique identifier for your application file. It helps prevent naming conflicts with other modules or libraries. *** @@ -203,7 +203,7 @@ Next, we have to configure the tooling extension we installed from npm to our U - All our custom middleware extensions will be called after the `compression` middleware. -> :info: +> ℹ️ > Middleware configurations are applied in the order in which they are defined.
@@ -241,7 +241,7 @@ Now you can benefit from live reload on changes and built framework resources at

-> :note: +> 📝 > During its initial run, the `ui5-middleware-serveframework` middleware will build the framework, which can take a while. In all following steps, the build will not happen again and the framework is served from the built resources.   diff --git a/packages/walkthrough/steps/03/README.md b/packages/walkthrough/steps/03/README.md index fb1881a6f..20f39e3c9 100644 --- a/packages/walkthrough/steps/03/README.md +++ b/packages/walkthrough/steps/03/README.md @@ -54,7 +54,7 @@ To place the text control to our HTML document, we chain the constructor call of
-> :info: +> ℹ️ > It is best practice to use of Anynchronous Module Loading (AMD) style for defining modules and their dependencies. This ensures better performance, proper dependency tracking between modules and helps avoid issues related to loading order.
@@ -81,7 +81,7 @@ sap.ui.define(["sap/m/Text"], function (Text) { All controls of OpenUI5 have a fixed set of properties, aggregations, and associations for configuration. You can find their descriptions in the Demo Kit. In addition, each control comes with a set of public functions that you can look up in the API reference. -> :info: +> ℹ️ > Only instances of `sap.ui.core.Control` or their subclasses can be rendered stand-alone and have a `placeAt` function. Each control extends `sap.ui.core.Element` that can only be rendered inside controls. Check the API reference to learn more about the inheritance hierarchy of controls. The API documentation of each control refers to the directly known subclasses. *** diff --git a/packages/walkthrough/steps/04/README.md b/packages/walkthrough/steps/04/README.md index 04c6aaa64..ba36e2b12 100644 --- a/packages/walkthrough/steps/04/README.md +++ b/packages/walkthrough/steps/04/README.md @@ -39,10 +39,10 @@ Inside the `` tag, we add the `` tag from the default XML namesp We have created an XML view that displays a text control with the text "Hello World". -> :tip: +> 💡 > XML tags are mapped to UI controls, and attributes are mapped to the properties of the control. In this case, the `` tag represents the `Text` control in the sap.m library, and the `text` attribute sets the `text` property of the control. -> :note: +> 📝 > The namespace identifies all resources of the project and has to be unique. If you develop your own application code or library, you cannot use the namespace prefix `sap`, because this namespace is reserved for SAP resources. Instead, simply define your own unique namespace \(for example, `myCompany.myApp`\). *** @@ -81,7 +81,7 @@ sap.ui.define(["sap/ui/core/mvc/XMLView"], function (XMLView) { We have now embed our app view to the body of the HTML document. -> :tip: +> 💡 >Although setting an ID is not mandatory, it greatly improves the maintainability and flexibility of your code. With a stable ID, you can easily locate and update specific parts of your application. *** diff --git a/packages/walkthrough/steps/05/README.md b/packages/walkthrough/steps/05/README.md index b79ee9550..c0eab1d8f 100644 --- a/packages/walkthrough/steps/05/README.md +++ b/packages/walkthrough/steps/05/README.md @@ -62,7 +62,7 @@ sap.ui.define(["sap/ui/core/mvc/Controller"], function (Controller) { ```
-> :note: +> 📝 > The comment `@name ui5.tutorial.walkthrough.controller.App` is a JSDoc comment that names this controller. It can be used by documentation generators and IDEs to provide more information about this class.
diff --git a/packages/walkthrough/steps/07/README.md b/packages/walkthrough/steps/07/README.md index 05fcf8a4d..bb3ad4aab 100644 --- a/packages/walkthrough/steps/07/README.md +++ b/packages/walkthrough/steps/07/README.md @@ -98,7 +98,7 @@ We add an `sap/m/Input` control to our view, allowing the user to enter a name f To make this work, we connect, or 'bind', the value of the input control to the `name` attribute of the 'recipient' object in our JSON data model. We do this using a simple binding syntax, which is a straightforward way to link data between the model and the view. -> :info: +> ℹ️ > To bind a control property to your view model data you need to specify a [`sap.ui.base.ManagedObject.PropertyBindingInfo`](https://sdk.openui5.org/api/sap.ui.base.ManagedObject.PropertyBindingInfo) for the property. A binding info is always initiated by enclosing it in curly brackets `{…}`, and the properties defined in the BindingInfos API are placed within the brackets. > > You can omit all properties of the binding info and just provide the binding path as a simple string. A binding path consists of path segments separated by a slash (`/`) which point to a property in the model that you want to bind to. This applies all OpenUI5 provided models. diff --git a/packages/walkthrough/steps/08/README.md b/packages/walkthrough/steps/08/README.md index 0a3892c6f..ac3ea118c 100644 --- a/packages/walkthrough/steps/08/README.md +++ b/packages/walkthrough/steps/08/README.md @@ -153,7 +153,7 @@ A resource bundle is a flat structure, therefore the preceding slash \(/\) can b
``` -> :note: +> 📝 > The description text is not completely localized in this example for illustration purposes. To be on the safe side, we would have to use a similar mechanism as in the controller to use a string from the resource bundle and replace parts of it. This can be done with the `sap/base/strings/formatMessage` formatter. > > Furthermore, `i18n` files only impact client-side application texts. Texts that are loaded from back-end systems can appear in all languages that are supported by the back-end system. diff --git a/packages/walkthrough/steps/10/README.md b/packages/walkthrough/steps/10/README.md index 2f823b2f7..908cbc8b3 100644 --- a/packages/walkthrough/steps/10/README.md +++ b/packages/walkthrough/steps/10/README.md @@ -57,7 +57,7 @@ We enhance the **`sap.app`** namespace by adding configuration for the following - `description`: Similarly, we make the description text language-dependent by referencing the `appDescription` text from the resource bundle using the handlebar syntax: {{key}} -> :warning: **Remember:**
+> ⚠️ **Remember:**
> Properties of the resource bundle are enclosed in two curly brackets in the manifest. This is not an OpenUI5 data binding syntax but a variable reference to the resource bundle in the manifest in handlebars syntax. In addition to the `sap.app` namespace, there are two other important namespaces: @@ -67,7 +67,7 @@ The **`sap.ui`** namespace is used for UI-specific attributes and comes with the - `deviceTypes` \(mandatory\): This property defines the supported device types for the application. It is an object that contains three boolean properties: `desktop`, `tablet`, and `phone`. Each property indicates whether the application is designed to be used on that particular device type. We define all three device types as "true", which means that our application is intended to be used on desktops, tablets, and phones. -> :note: +> 📝 > By configuring the `deviceTypes` property, developers can ensure that the application's user interface is optimized for different device types, providing a consistent and responsive experience across various devices. The **`sap.ui5`** namespace adds OpenUI5-specific configuration parameters that are automatically processed by OpenUI5. The following parameters are important: @@ -80,7 +80,7 @@ The **`sap.ui5`** namespace adds OpenUI5-specific configuration parameters that In our component we currenetly only use the `sap.ui.core` and `sap.m` liibraries. The `sap.ui.core` library provides the basic framework functionality and is required for any OpenUI5 application.
By default, loading of libraries is set to `"lazy": false`, which means they are loaded immediately when the component initializes. For libraries that are essential for your app to function from the start, like `sap.ui.core` and in our case also `sap.m` (since it is used in the root view), we can keep the default setting of `"lazy": false`. - > :info: + > ℹ️ > It is crucial to be mindful of the lazy loading configuration for libraries. Only libraries that are absolutely necessary for your component to start should be declared with `"lazy": false`. For libraries that are not required immediately, it is recommended to override the default setting and set `"lazy": true`. This approach allows for better performance and faster initial loading of the component by deferring the loading of non-essential libraries until they are actually needed. > For more information on loading libraries, refer to the [sap.ui.core.Lib.load](https://sdk.openui5.org/api/sap.ui.core.Lib#methods/sap.ui.core.Lib.load) API reference. > If your component requires a minimum version of the library, you need to specify the `minVersion` for information purposes. @@ -146,7 +146,7 @@ In our current scenario, we only have one model called `i18n`, which is a resour } ``` -> :note: +> 📝 > In this tutorial, we only introduce the most important settings and parameters of the manifest. In some development environments you may get validation errors because some settings are missing - you can ignore those in this context. *** diff --git a/packages/walkthrough/steps/11/README.md b/packages/walkthrough/steps/11/README.md index 126c42add..2fe84f48d 100644 --- a/packages/walkthrough/steps/11/README.md +++ b/packages/walkthrough/steps/11/README.md @@ -80,7 +80,7 @@ The `App` control does the following important things for us: - It writes a bunch of properties into the header of the `index.html` that are necessary for proper display on mobile devices. - It offers functionality to navigate between pages with animations. We will use this soon. -> :note: +> 📝 > The `sap/m/Page` control used here is one of the most popular view-level containers in OpenUI5. However, OpenUI5 applications are used in different environments: they can be embedded within shells that come with their own header (like e.g. SAP Build Work Zone or the SAP Fiori launchpad). Or they are displayed stand-alone without such a shell around them. This has implications on how the header area of a OpenUI5 application should look: > - A stand-alone OpenUI5 application could use a `sap/m/Page` control as root control of its views. This Page control provides a visually distinguished header bar with a title and has a built-in "back" button using which the user can navigate back to the previous page. (This back button can be enabled by setting the Page’s `showNavButton` property to `true`.) > - When, on the other hand, a containing shell already comes with a header that has a "back" button and a title, then using `sap/m/Page` controls will lead to duplicate headers (and possibly even duplicate back buttons). The `sap/f/DynamicPage` control would be a preferred alternative in such a scenario, as it comes without a header bar and back button, but still offers the option to configure a title if needed – and many other features on top of a plain `sap/m/Page`. Further alternatives are `sap/f/semantic/SemanticPage` and `sap/uxap/ObjectPageLayout`, depending on the use-case. Note that they are part of other control libraries than sap.m, so you might need to add the respective library to your application setup. For some scenarios, the `sap/tnt/ToolPage` may be another alternative. But also the `sap/m/Page` can be configured to have its header hidden. diff --git a/packages/walkthrough/steps/12/README.md b/packages/walkthrough/steps/12/README.md index cf2c5223b..a0033dc83 100644 --- a/packages/walkthrough/steps/12/README.md +++ b/packages/walkthrough/steps/12/README.md @@ -59,7 +59,7 @@ In your App view, we put the `App` control inside a `sap/m/Shell` control. The `Shell` control is now the outermost control of our app and automatically displays a so-called letterbox, if the screen size is larger than a certain width. -> :information_source: **Note:** +> ℹ️ **Note:** > We don't add the `Shell` control to the declarative UI definition in the XML view if apps run in an external shell, like the SAP Fiori launchpad that already has a shell around the component UI. There are further options to customize the shell, like setting a custom background image or color and setting a custom logo. Check the related API reference for more details. diff --git a/packages/walkthrough/steps/16/README.md b/packages/walkthrough/steps/16/README.md index 496eda529..2c9919de2 100644 --- a/packages/walkthrough/steps/16/README.md +++ b/packages/walkthrough/steps/16/README.md @@ -101,7 +101,7 @@ sap.ui.define(["sap/ui/core/mvc/Controller", "sap/m/MessageToast"], function (Co ```   -> :tip: +> 💡 > To reuse the dialog opening and closing functionality in other controllers, you might create a new file `ui5.tutorial.walkthrough.controller.controller.BaseController`, which extends `sap.ui.core.mvc.Controller`, and put all your dialog-related coding into this controller. Now, all the other controllers can extend from `ui5.tutorial.walkthrough.controller.BaseController` instead of `sap.ui.core.mvc.Controller`. diff --git a/packages/walkthrough/steps/18/README.md b/packages/walkthrough/steps/18/README.md index a966ecf4f..2b8c66842 100644 --- a/packages/walkthrough/steps/18/README.md +++ b/packages/walkthrough/steps/18/README.md @@ -56,7 +56,7 @@ We add an icon to the button that opens the dialog. The `sap-icon://` protocol i ```   -> :tip: +> 💡 > You can look up other icons using the [Icon Explorer tool](https://sdk.openui5.org/test-resources/sap/m/demokit/iconExplorer/webapp/index.html). > To call any icon, use its name as listed in the *Icon Explorer* in sap-icon://<iconname>. diff --git a/packages/walkthrough/steps/22/README.md b/packages/walkthrough/steps/22/README.md index 5bcf4d6f7..55ef3bf84 100644 --- a/packages/walkthrough/steps/22/README.md +++ b/packages/walkthrough/steps/22/README.md @@ -97,7 +97,7 @@ The new `formatter` file is placed in the model folder of the app, because forma   -> :info: +> ℹ️ > In the above example, `this` refers to the controller instance as soon as the formatter gets called. We access the resource bundle via the component using `this.getOwnerComponent().getModel()` instead of using `this.getView().getModel()`. The latter call might return `undefined`, because the view might not have been attached to the component yet, and thus the view can't inherit a model from the component. **Additional Information:** diff --git a/packages/walkthrough/steps/25/README.md b/packages/walkthrough/steps/25/README.md index 8af39c7e8..439f4dd18 100644 --- a/packages/walkthrough/steps/25/README.md +++ b/packages/walkthrough/steps/25/README.md @@ -115,7 +115,7 @@ In the `models` section, we replace the content of the `invoice` model. This key Our component now automatically creates an instance of `sap.ui.model.odata.v2.ODataModel` according to the settings we specified above, and makes it available as a model named `invoice`. When you use the `invoiceRemote` data source, the `ODataModel` fetches the data from the real Northwind OData service. The invoices we receive from the Northwind OData service have identical properties as the JSON data we used previously \(except for the `status` property, which is not available in the Northwind OData service\). -> :note: +> 📝 > If you want to have a default model on the component, you can change the name of the model to an empty string in the descriptor file. > > Automatically instantiated models can be retrieved by calling `this.getModel` in the component. In the controllers of component-based apps you can call `this.getView().getModel()` to get the automatically instantiated model. For retrieving a named model you have to pass on the model name defined in the descriptor file to `getModel`, that is, in the component you would call `this.getModel("invoice")` to get our automatically generated `invoice` model that we defined in the descriptor. diff --git a/packages/walkthrough/steps/27/README.md b/packages/walkthrough/steps/27/README.md index c342642dc..b17701547 100644 --- a/packages/walkthrough/steps/27/README.md +++ b/packages/walkthrough/steps/27/README.md @@ -4,7 +4,7 @@ Now that we have a test folder in the app, we can start to increase our test cov Actually, every feature that we added to the app so far, would require a separate test case. We have totally neglected this so far, so let’s add a simple unit test for our custom formatter function from Step 23. We will test if the long text for our status is correct by comparing it with the texts from our resource bundle. -> :note: +> 📝 > In this tutorial, we focus on a simple use case for the test implementation. If you want to learn more about QUnit tests, have a look at the [Testing Tutorial](https://sdk.openui5.org/topic/291c9121e6044ab381e0b51716f97f52.html) tutorial, especially [Step 2: A First Unit Test](https://sdk.openui5.org/topic/b81736e0fcb246efb3b0cf0ca422f8fd.html).   @@ -39,7 +39,7 @@ The new formatter file just contains one QUnit module for our formatter function Finally, we perform our assertions. We check each branch of the formatter logic by invoking the isolated formatter function with the values that we expect in the data model \(`A`, `B`, `C`, and everything else\). We strictly compare the result of the formatter function with the hard-coded strings that we expect from the resource bundle and give a meaningful error message if the test should fail. -> :note: +> 📝 > 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 diff --git a/packages/walkthrough/steps/28/README.md b/packages/walkthrough/steps/28/README.md index 8079bb8bc..5ffc6d974 100644 --- a/packages/walkthrough/steps/28/README.md +++ b/packages/walkthrough/steps/28/README.md @@ -4,7 +4,7 @@ If we want to test interaction patterns or more visual features of our app, we c We haven’t thought about testing our interaction with the app yet, so in this step we will check if the dialog actually opens when we click the “Say Hello with Dialog” button. We can easily do this with OPA5, a feature of OpenUI5 that is easy to set up and is based on JavaScript and QUnit. Using integration and unit tests and running them consistently in a continuous integration \(CI\) environment, we can make sure that we don’t accidentally break our app or introduce logical errors in existing code.   -> :note: +> 📝 > In this tutorial, we focus on a simple use case for the test implementation. If you want to learn more about OPA tests, have a look at our [Testing Tutorial](https://sdk.openui5.org/topic/291c9121e6044ab381e0b51716f97f52.html) tutorial, especially [Step 6: A First OPA Test](https://sdk.openui5.org/topic/1b47457cbe4941ee926317d827517acb.html).   diff --git a/packages/walkthrough/steps/29/README.md b/packages/walkthrough/steps/29/README.md index e1b953206..33d0817d1 100644 --- a/packages/walkthrough/steps/29/README.md +++ b/packages/walkthrough/steps/29/README.md @@ -86,7 +86,7 @@ We introduced a typo in the binding of the number attribute to simulate a freque   Now we call the app and notice that the price is actually missing. By entering the [shortcut](https://sdk.openui5.org/topic/154844c3ac2a4675a37aeb6259a5e034.html) [Ctrl\] + [Shift\] + [Alt\] /[Option\] + [S\] we open the OpenUI5 support diagnostics tool and check the app. -> :note: +> 📝 > > If you use the Google Chrome browser, you can install the *UI5 Inspector* plugin. With this plugin, you can easily debug your OpenUI5- or OpenUI5-based apps. For more information, see [UI5 Inspector](https://sdk.openui5.org/topic/b24e72443eb34d0fb7bf6940f2d697eb.html). @@ -96,7 +96,7 @@ A hierarchical tree of OpenUI5 controls is shown on the left and the properties Sometimes errors are not as easy to spot and you actually need to debug the JavaScript code with the tools of the browser. -> :note: +> 📝 > When debugging UI5 applications that use built resources, the OpenUI5 files are minified, which means that variable names are shortened and comments are removed. > > 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. diff --git a/packages/walkthrough/steps/30/README.md b/packages/walkthrough/steps/30/README.md index 24079c210..b097a2810 100644 --- a/packages/walkthrough/steps/30/README.md +++ b/packages/walkthrough/steps/30/README.md @@ -107,7 +107,7 @@ We add a new “routing" section to the `sap.ui5` part of the descriptor. There - `config` This section contains the global router configuration and default values that apply for all routes and targets. The property routerClass is special as it determines the router implementation. The default value is `sap.ui.core.routing.Router`. Here, we set the `routerClass` to `sap.m.routing.Router`, because our app is based on `sap.m`. All other properties in config are given to the router instance. For example, we define in `path` where our views are located in the app. As we want to specify view to view navigation and we only use XML views in our app we preset also the paramter `type` and `viewType`. To load and display views automatically, we also specify the `controlId` of the control that will contain the views and the aggregation (`controlAggregation`) of the control where the views will be added. Here we specify that the views are loaded into the `pages` aggregation of the control with the id we provided in the app view. - > :info: + > ℹ️ > The possible values for `routerClass` are `sap.ui.core.routing.Router`, `sap.m.routing.Router`, or any other subclasses of `sap.ui.core.routing.Router`. Compared to `sap.ui.core.routing.Router` the `sap.m.routing.Router` is optimized for mobile apps and adds the properties `level`, `transition` and `transitionParameters` which can be specified for each route or target created by the `sap.m.routing.Router`. - `routes` @@ -168,7 +168,7 @@ The router will automatically add the view that corresponds to the current URL i The overview view is always shown when the hash is empty. The detail view is shown when the hash matches the pattern `detail`. -> :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.ts/.js diff --git a/packages/walkthrough/steps/31/README.md b/packages/walkthrough/steps/31/README.md index 1c6571cb4..d87d1fabc 100644 --- a/packages/walkthrough/steps/31/README.md +++ b/packages/walkthrough/steps/31/README.md @@ -29,7 +29,7 @@ You can download the solution for this step here: [📥 Do We want to hand over the information for the selected item when navigating to the detail view. To achieve that, we add the navigation parameter `invoicePath` to the detail route in the app descriptor. There, we add a navigation parameter `invoicePath` to the detail route so that we can hand over the information for the selected item to the detail page. -> :note: +> 📝 > Mandatory navigation parameters are defined with curly brackets. ```json diff --git a/packages/walkthrough/steps/33/README.md b/packages/walkthrough/steps/33/README.md index 92c5979bc..72fe6a679 100644 --- a/packages/walkthrough/steps/33/README.md +++ b/packages/walkthrough/steps/33/README.md @@ -77,7 +77,7 @@ The `init` function is a lifecycle function that is automatically called by the The static `renderer` property expects an object that defines how the control is rendered. It is invoked initially by the OpenUI5 framework and each time a property of the control is changed. The `renderer` object has two properties: `apiVersion` and `render`. The `apiVersion` property specifies the API version of the RenderManager that is used in this renderer. The `render` property is a method that takes two parameters: a `RenderManager` object and the control instance itself. We'll delve into the implementation of our control's rendering within this method at a later stage. -> :note: +> 📝 > The RenderManager is an important component in OpenUI5 that is responsible for converting abstract representations of controls into actual HTML elements that can be displayed in the browser. There are different versions of the RenderManager API, each representing an evolution of the RenderManager with specific sets of APIs and rendering techniques. These different API versions are important to ensure compatibility between different versions of OpenUI5. > > The latest version of the RenderManager API is version 4, which introduces new features and improvements compared to previous versions. It also includes performance enhancements, making your applications run faster and more efficiently. For example, version 4 avoids re-rendering of child controls unless they are invalidated, which can save processing time. @@ -129,7 +129,7 @@ sap.ui.define(["sap/ui/core/Control"], function (Control) { ```   -> :info: +> ℹ️ > Controls always extend `sap.ui.core.Control` and render themselves. You could also extend `sap.ui.core.Element` or `sap.ui.base.ManagedObject` directly if you want to reuse life cycle features of OpenUI5 including data binding for objects that are not rendered. Please refer to the API reference to learn more about the inheritance hierarchy of controls. We now enhance our new custom control with the custom functionality that we need. In our case we want to create an interactive product rating feature. We utilize three controls provided by the sap.m library to compose our custom control: A `RatingIndicator` control to collect user input on a product, a `Label` control to display additional information, and a `Button` control that allows users to submit their rating. @@ -144,7 +144,7 @@ In the `metadata` section we therefore define several properties that we make us As described in the first paragraph, we need three internal controls to realize our rating functionality. We therefore create three “hidden aggregations” by setting the `visibility` attribute to `hidden`. This way, we can use the models that are set on the view also in the inner controls and OpenUI5 will take care of the lifecycle management and destroy the controls when they are not needed anymore. Aggregations can also be used to hold arrays of controls but we just want a single control in each of the aggregations so we need to adjust the cardinality by setting the attribute `multiple` to `false`. - > :note: + > 📝 > You can define `aggregations` and `associations` > > - An **`aggregation`** is a strong relation that also manages the lifecycle of the related control, for example, when the parent is destroyed, the related control is also destroyed. Also, a control can only be assigned to one single aggregation, if it is assigned to a second aggregation, it is removed from the previous aggregation automatically. @@ -159,7 +159,7 @@ In the `init` function we instantiate the three controls and store them in the i Let’s ignore the other internal helper functions and event handlers for now and define our renderer. By using the APIs of the RenderManager and the control instance that are passed as references, we can describe the necessary HTML for our control. To open a new HTML tag we use the `openStart` method and pass `"div"` as the HTML element to be created. We also pass our control instance (ProductRating) to be associated with the HTML tag. The RenderManager will automatically generate the properties for the control and assign it to the `div` tag. After calling `openStart`, we can chain additional methods to set attributes or styles for the element. To set our custom CSS class `myAppDemoWTProductRating` for the `div` element, we use the `class` method. If a `tooltip` exists, we call the `attr` method to set the `title` attribute with the value of the tooltip for the div element. Finally, we close the surrounding `div` tag by calling `openEnd`. -> :info: +> ℹ️ > Since our custom control extends the `sap.ui.core.Control` class, it also inherits its properties and aggregations from it. In this case, the `tooltip` property is defined in the `sap.ui.core.Element` class, which is inherited by the `sap.ui.core.Control` class. Therefore, your custom control also inherits this aggregation. However, controls must explicitly support tooltips as they have to render them. Next, we render the three child controls we defined in the aggregation of our ProductRating control. We retrieve the child controls using the `getAggregation` method with the aggregation name as the parameter. The `renderControl` method is then called on each child control to render them. Finally, we close the element by calling the `close` method on the RenderManager and passing the `"div"` element name as argument. This completes the rendering of the custom control. diff --git a/packages/walkthrough/steps/34/README.md b/packages/walkthrough/steps/34/README.md index d3054e3e2..d6808beba 100644 --- a/packages/walkthrough/steps/34/README.md +++ b/packages/walkthrough/steps/34/README.md @@ -184,7 +184,7 @@ Now we have defined our table responsively and can see the results when we decre We can see the results when we decrease the browser's screen size or open the app on a small device. -> :tip: +> 💡 > You can test the device specific features of your app with the developer tools of your browser. For example in Google Chrome, you can emulate a tablet or a phone easily and see the effects. Some responsive options of OpenUI5 are only set initially when loading the app, so you might have to reload your page to see the results. *** diff --git a/packages/walkthrough/steps/35/README.md b/packages/walkthrough/steps/35/README.md index 1a9ace206..fe049aee1 100644 --- a/packages/walkthrough/steps/35/README.md +++ b/packages/walkthrough/steps/35/README.md @@ -24,7 +24,7 @@ You can download the solution for this step here: [📥 Do 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. -> :info: +> ℹ️ > 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 @@ -146,7 +146,7 @@ We can also hide single controls by device type when we set a CSS class like `sa   The device API of OpenUI5 offers more functionality to detect various device-specific settings, please have a look at the [documentation](https://sdk.openui5.org/api/sap.ui.Device) for more details. -> :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.ts/.js @@ -284,7 +284,7 @@ We add the `number` and `numberUnit` field from the list of the previous steps a We can see the results when we decrease the browser's screen size or open the app on a small device. -> :note: +> 📝 > You can test the device specific features of your app with the developer tools of your browser. For example in Google Chrome, you can emulate a tablet or a phone easily and see the effects. Some responsive options of OpenUI5 are only set initially when loading the app, so you might have to reload your page to see the results. *** diff --git a/packages/walkthrough/steps/37/README.md b/packages/walkthrough/steps/37/README.md index 943fa1f70..b809be780 100644 --- a/packages/walkthrough/steps/37/README.md +++ b/packages/walkthrough/steps/37/README.md @@ -6,7 +6,7 @@ To achieve this, we will add ARIA attributes. ARIA attributes are used by screen One part of the ARIA attribute set are the so-called landmarks. You can compare landmarks to maps in that they help the user navigate through an app. For this step, we will use Google Chrome with a free [landmark navigation extension](https://chrome.google.com/webstore/detail/landmark-navigation-via-k/ddpokpbjopmeeiiolheejjpkonlkklgp) We will now add meaningful landmarks to our code. -> :tip: +> 💡 > ARIA is short for **Accessible Rich Internet Applications**. It is a set of attributes that enable us to make apps more accessible by assigning semantic characteristics to certain elements. For more information, see [Accessible Rich Internet Applications \(ARIA\) – Part 1: Introduction](https://blogs.sap.com/2015/06/01/accessible-rich-internet-applications-aria-part-1-introduction/).