https://raw.githubusercontent.com/ajmaradiaga/feeds/main/scmt/topics/SAPUI5-blog-posts.xml SAP Community - SAPUI5 2026-02-18T18:01:36.801543+00:00 python-feedgen SAPUI5 blog posts in SAP Community https://community.sap.com/t5/technology-blog-posts-by-sap/utilising-the-dynamic-svg-within-a-sap-ui5-application/ba-p/14300173 Utilising the dynamic SVG within a SAP UI5 application 2026-01-01T16:18:55.060000+01:00 SanketZad https://community.sap.com/t5/user/viewprofilepage/user-id/1487323 <P>Hello UI5 Developers,</P><P>The case being explained here may not be encountered frequently in UI5 development. We are all familiar with the use of SVGs in frontend applications. However, the case under analysis will concern dynamic SVGs. Dynamic SVGs have internal attributes and properties that depend on global CSS variables. The standard implementation will not yield the desired results either in rendering the SVG or in ensuring compatibility with all themes.</P><P>I will explain this case in the context of our project requirements.</P><P>Below is the SVG that uses the CSS variables for the linear gradient.&nbsp;</P><pre class="lia-code-sample language-markup"><code>&lt;svg width="306" height="67" viewBox="0 0 306 67" fill="none" xmlns="http://www.w3.org/2000/svg"&gt; &lt;path opacity="0.9" d="M224 0H89V67H156.5L224 0Z" fill="url(#paint0_linear_2040_121992)" /&gt; &lt;path opacity="0.6" d="M290 0H230V30H260L290 0Z" fill="url(#paint1_linear_2040_121992)" /&gt; &lt;path opacity="0.4" d="M81 0H0V40H40.5L81 0Z" fill="url(#paint2_linear_2040_121992)" /&gt; &lt;defs&gt; &lt;linearGradient id="paint0_linear_2040_121992" x1="83.5497" y1="67" x2="201.283" y2="-5.15128" gradientUnits="userSpaceOnUse"&gt; &lt;stop offset="0.0158962" style="stop-color: var(--sapShell_Category_1_Background)" /&gt; &lt;stop offset="0.550349" style="stop-color: var(--sapShell_Category_1_Background)" /&gt; &lt;stop offset="1" style="stop-color: var(--sapShell_Category_9_Background)" /&gt; &lt;/linearGradient&gt; &lt;linearGradient id="paint1_linear_2040_121992" x1="227.578" y1="30" x2="280.115" y2="-1.95849" gradientUnits="userSpaceOnUse"&gt; &lt;stop offset="0.0158962" style="stop-color: var(--sapShell_Category_1_Background)" /&gt; &lt;stop offset="0.550349" style="stop-color: var(--sapShell_Category_1_Background)" /&gt; &lt;stop offset="1" style="stop-color: var(--sapShell_Category_9_Background)" /&gt; &lt;/linearGradient&gt; &lt;linearGradient id="paint2_linear_2040_121992" x1="-3.27019" y1="40" x2="67.1769" y2="-3.38846" gradientUnits="userSpaceOnUse"&gt; &lt;stop offset="0.503258" style="stop-color: var(--sapShell_Category_1_Background)" /&gt; &lt;stop offset="0.503258" style="stop-color: var(--sapShell_Category_1_Background)" /&gt; &lt;stop offset="1" style="stop-color: var(--sapShell_Category_9_Background)" /&gt; &lt;/linearGradient&gt; &lt;/defs&gt; &lt;/svg&gt;</code></pre><P>&nbsp;Reason for SVG not rendering as expected,</P><P>The primary reason an SVG may not be able to fetch CSS variables in an SAPUI5 application is due to<SPAN>&nbsp;</SPAN><STRONG>scoping issues</STRONG><SPAN>, specifically when the SVG is loaded as an external resource (e.g., using an&nbsp;</SPAN><CODE>&lt;img&gt;</CODE><SPAN>&nbsp;tag or&nbsp;</SPAN><CODE>&lt;use&gt;</CODE><SPAN>&nbsp;element) or if there are conflicts with the SAPUI5's theming infrastructure.</SPAN><SPAN class=""><SPAN class="">&nbsp;</SPAN></SPAN></P><P><SPAN class=""><SPAN class="">The common problem and solution would be&nbsp;</SPAN></SPAN></P><P><SPAN class=""><SPAN class=""><STRONG>Problem</STRONG>:&nbsp;<SPAN>If the SVG is referenced using an external file (e.g.,&nbsp;</SPAN><CODE>&lt;img src="icon.svg"&gt;</CODE><SPAN>&nbsp;or&nbsp;</SPAN><CODE>&lt;use xlink:href="images/arrow.svg#arrowLeft"&gt;&lt;/use&gt;</CODE><SPAN>), the browser treats the SVG as <STRONG>an entirely separate document</STRONG> or <STRONG>scope</STRONG>. It cannot access the CSS variables defined in the main HTML document's CSS.</SPAN></SPAN></SPAN></P><P><STRONG>Solution</STRONG>: You<SPAN>&nbsp;must use&nbsp;</SPAN><STRONG>inline SVG</STRONG><SPAN>&nbsp;directly within your HTML or UI5 control's renderer. This places the SVG elements within the same DOM and CSS scope as the rest of your application, allowing them to inherit or access the CSS variables defined in the main application's stylesheet.</SPAN></P><P><SPAN>If the SVG is static, then directly using it inline will fetch the CSS variables, as the SVG will be in the global scope.</SPAN></P><P><SPAN>If the SVG is dynamic (requiring the SVG to adapt to the change in themes), then the static option won't be useful. This requires a unique and simple implementation in the controller file.</SPAN></P><P><STRONG>JavaScript Dynamic Injection from Controller file (Recommended)</STRONG></P><P>In respective controller file,&nbsp; <STRONG>yourComponentName.controller.js</STRONG></P><pre class="lia-code-sample language-javascript"><code>onAfterRendering: function () { // Post-rendering logic for header controller for rendering SVG in the background this._setBackgroundImageSVG(); }, // Other methods _setBackgroundImageSVG: function() { const vbox = this.byId("headerSearchContentVBox"); if (vbox) { const domRef = vbox.getDomRef(); if (domRef) { // Get CSS variable values const computedStyle = getComputedStyle(document.documentElement); const color1 = computedStyle.getPropertyValue('--sapShell_Category_1_Background').trim(); const color9 = computedStyle.getPropertyValue('--sapShell_Category_9_Background').trim(); // Create SVG with resolved colors const svgMarkup = `&lt;svg width="306" height="67" viewBox="0 0 306 67" fill="none" xmlns="http://www.w3.org/2000/svg"&gt; &lt;path opacity="0.9" d="M224 0H89V67H156.5L224 0Z" fill="url(#paint0_linear)" /&gt; &lt;path opacity="0.6" d="M290 0H230V30H260L290 0Z" fill="url(#paint1_linear)" /&gt; &lt;path opacity="0.4" d="M81 0H0V40H40.5L81 0Z" fill="url(#paint2_linear)" /&gt; &lt;defs&gt; &lt;linearGradient id="paint0_linear" x1="83.5497" y1="67" x2="201.283" y2="-5.15128" gradientUnits="userSpaceOnUse"&gt; &lt;stop offset="0.0158962" stop-color="${color1}" /&gt; &lt;stop offset="0.550349" stop-color="${color1}" /&gt; &lt;stop offset="1" stop-color="${color9}" /&gt; &lt;/linearGradient&gt; &lt;linearGradient id="paint1_linear" x1="227.578" y1="30" x2="280.115" y2="-1.95849" gradientUnits="userSpaceOnUse"&gt; &lt;stop offset="0.0158962" stop-color="${color1}" /&gt; &lt;stop offset="0.550349" stop-color="${color1}" /&gt; &lt;stop offset="1" stop-color="${color9}" /&gt; &lt;/linearGradient&gt; &lt;linearGradient id="paint2_linear" x1="-3.27019" y1="40" x2="67.1769" y2="-3.38846" gradientUnits="userSpaceOnUse"&gt; &lt;stop offset="0.503258" stop-color="${color1}" /&gt; &lt;stop offset="0.503258" stop-color="${color1}" /&gt; &lt;stop offset="1" stop-color="${color9}" /&gt; &lt;/linearGradient&gt; &lt;/defs&gt; &lt;/svg&gt;`; // Encode and set as background const encoded = encodeURIComponent(svgMarkup); domRef.style.setProperty('--before-content', `url('data&amp;colon;image/svg+xml,${encoded}')`); } } }</code></pre><P>View file: <STRONG>yourComponentName.view.xml</STRONG></P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns:m="sap.m" xmlns:f="sap.f" controllerName="your.project.name.controller.YourComponentController"&gt; &lt;m:VBox id="headerSearchContentVBox" class="headerSearchVBox" width="100%" justifyContent="Center"&gt; &lt;m:SearchField id="headerMainSearchField" enableSuggestions="true" class="exampleSearchField sapUiSmallMargin"/&gt; &lt;/m:VBox&gt; &lt;/mvc:View&gt;</code></pre><P>&nbsp;I will be placing this SVG in the pseudo-element associated with the VBox bearing the id&nbsp;<STRONG>headerSearchContentVBox</STRONG>.</P><P>CSS file: <STRONG>css/style.css</STRONG></P><pre class="lia-code-sample language-css"><code>.headerSearchVBox { /* Other CSS properties as per your use case */ position: relative; background-color: var(--sapShell_Category_1_Background); width: 100%; height: 4.25rem; border-width: var(--sapElement_BorderWidth); border-style: solid; border-color: var(--sapTile_BorderColor); border-radius: var(--sapTile_BorderCornerRadius); overflow: hidden; text-align: right; box-sizing: border-box; padding: var(--sapUiSmallMargin) var(--sapContent_Space_Small); } .headerSearchVBox::before { content: var(--before-content, url(../images/SearchBackground.svg)); position: absolute; top: 0; right: 1rem; width: 19.125rem; height: 4.1875rem; pointer-events: none; }</code></pre><P>&nbsp;The content property is set to <STRONG>var(—before-content, url(../images/SearchBackground.svg))</STRONG>. As previously stated, the encoded SVG is assigned to the <STRONG>before-content</STRONG>&nbsp;property in the controller. To enhance user experience, the SVG is directly loaded with a fallback color. If an error occurs, it will render with the provided color or the default browser color, which is black.</P><P>The primary function of dynamic SVG is to synchronize with changing themes. This can be enabled by subscribing to the event, which is fired when the theme is changed, i.e., the <STRONG>themeChanged</STRONG> event. Whenever the theme is changed,&nbsp;</P><P>1. Listen to this event in onInit method of same controller file</P><P>2. Call the SVG update function when the theme is changed.</P><P>3. Remove the listener attached in the onInit method from the onExit method.</P><pre class="lia-code-sample language-javascript"><code>// Import sap.ui.core.Core library onInit: function () { // Attach theme change listener to update SVG colors this._onThemeChangedHandler = this._setBackgroundImageSVG.bind(this); Core.attachThemeChanged(this._onThemeChangedHandler); }, onExit: function () { // Clean up theme change listener if (this._onThemeChangedHandler) { Core.detachThemeChanged(this._onThemeChangedHandler); } },</code></pre><P>This will facilitate the development of the UI using dynamic SVG within the UI5 framework, making it more straightforward and efficient.</P><P>The content below is newly added on request by&nbsp;<a href="https://community.sap.com/t5/user/viewprofilepage/user-id/14716">@RaminS</a>&nbsp;(thank you for highlighting)</P><P>Snippet: If the dynamic SVGs are used directly in XML or through CSS,&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SanketZad_0-1767368740634.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357793iE56F4D67A30429E2/image-size/medium?v=v2&amp;px=400" role="button" title="SanketZad_0-1767368740634.png" alt="SanketZad_0-1767368740634.png" /></span></P><P>The SVG file did not locate the CSS variables present on the page, as it was created in a different stacking context. Consequently, it used the default color, black.</P><P>Once the dynamic SVGs are loaded via the controller file as previously mentioned, the rendering of the background would be as follows.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SanketZad_1-1767368992256.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357797i0DFF4FBDA6B13B7D/image-size/medium?v=v2&amp;px=400" role="button" title="SanketZad_1-1767368992256.png" alt="SanketZad_1-1767368992256.png" /></span></P><P>Regards,</P><P>Sanket Zad</P> 2026-01-01T16:18:55.060000+01:00 https://community.sap.com/t5/frontend-ui5-sap-fiori-blog-posts/implementation-guide-integrating-the-ui5-spreadsheet-importer-in-a-fiori/ba-p/14300547 Implementation Guide: Integrating the UI5 Spreadsheet Importer in a FIORI Application 2026-01-02T13:41:22.383000+01:00 Saswata89 https://community.sap.com/t5/user/viewprofilepage/user-id/13405 <H2 id="toc-hId-1787618536"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="unnamed.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357735iA2770FDA46DB03CC/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="unnamed.png" alt="unnamed.png" /></span></H2><H2 id="toc-hId-1591105031">1.0 Introduction to the UI5 Spreadsheet Importer</H2><P>The Spreadsheet Importer offers several strategic advantages that make it an invaluable tool for modern FIORI development:</P><UL><LI><STRONG>Seamless Data Integration:</STRONG> It allows applications to read and process Excel files directly on the front end, eliminating cumbersome manual data entry and reducing the risk of errors. A key benefit is its independence from the specific backend OData service implementation, making it compatible with both Cloud Application Programming Model (CAP) and ABAP RESTful Application Programming Model (RAP) scenarios.</LI><LI><STRONG>Out-of-the-Box Functionality:</STRONG> The component supports standard spreadsheet formats (<CODE>.xls</CODE> and <CODE>.xlsx</CODE>) and utilizes the robust SheetJS library to parse file data within the browser. This avoids sending large binary files to the backend. It automatically converts spreadsheet data into the required OData format for seamless communication with your SAP system.</LI><LI><STRONG>Productivity Improvements:</STRONG> With an intuitive interface and straightforward configuration, the component requires minimal coding effort from developers. It supports every type of OData service (SEGW, RAP, and CAP), accelerating development cycles and improving the efficiency of business processes.</LI><LI><STRONG>Community and Security:</STRONG> As a well-regarded open-source tool, the Spreadsheet Importer benefits from strong community support on GitHub, ensuring it is well-documented and continuously improved. It maintains high security standards with no known vulnerabilities reported in recent audits.</LI><LI><STRONG>Proven Track Record:</STRONG> The component has been successfully adopted by numerous SAP developers across the ecosystem. Its reliability is underscored by its implementation as an extension in a standard SAP banking application, proving its scalability for mission-critical enterprise environments.</LI></UL><P>Before beginning implementation, it is essential to understand the specific licensing and versioning requirements associated with this component.</P><H2 id="toc-hId-1394591526">2.0 Critical Prerequisites: Licensing and Version Control</H2><P>Adhering to the correct licensing and versioning requirements is a critical prerequisite for any implementation. This ensures full compliance with open-source software policies and guarantees the long-term stability and supportability of your application.</P><P>The component is available under two distinct licensing models, and it is mandatory to use the approved version.</P><TABLE border="1"><TBODY><TR><TD><P>Version</P></TD><TD><P>Licensing and Usage Details</P></TD></TR><TR><TD><P>Version 1.x (e.g., 1.7.4)</P></TD><TD><P>Licensed under <STRONG>Apache 2.0</STRONG>. This is the free-of-charge Community Edition.&nbsp;</P></TD></TR><TR><TD><P>Version 2.0.0 and above</P></TD><TD><P>Requires a <STRONG>commercial license.</STRONG></P></TD></TR></TBODY></TABLE><H3 id="toc-hId-1327160740">Mandatory Version</H3><P>To ensure consistency and compliance across all projects, a specific version has been mandated for all development.</P><P><STRONG>The required and approved version for all implementations we have used in this article - 1.7.4.</STRONG></P><P>With these prerequisites established, we can proceed with the technical steps required for integration.</P><H2 id="toc-hId-1001564516">3.0 Step-by-Step Implementation Workflow</H2><P>Integrating the Spreadsheet Importer into a FIORI Element application involves five core steps. This workflow covers modifications to the UI manifest, the implementation of controller logic, and the necessary configurations for the project's dependencies and build scripts.</P><H3 id="toc-hId-934133730">3.1 Step 1: Add the Custom "Excel Upload" Action</H3><P>The first step is to add a custom action button to the List Report page. This is accomplished by extending the view controller within the application's <CODE>manifest.json</CODE> file.</P><P>Add the following <CODE>"extends"</CODE> block to your <CODE>manifest.json</CODE>:</P><PRE><CODE>"extends": { "extensions": { "sap.ui.controllerExtensions": { "sap.suite.ui.generic.template.ListReport.view.ListReport": { "controllerName": "&lt;yournamespace&gt;.doerategrp.ext.controller.ListReportExt", "sap.ui.generic.app": { "rateGroupSet": { "EntitySet": "rateGroupSet", "Actions": { "excelUpload": { "id": "excelUploadButton", "text": "Excel Upload", "press": "openSpreadsheetUpload", "requiresSelection": false } } } } } } } }</CODE></PRE><P>This configuration achieves the following:</P><UL><LI><CODE><STRONG>"controllerName"</STRONG></CODE>: Specifies the custom controller extension file that will contain the button's logic.</LI><LI><CODE><STRONG>"id": "excelUploadButton"</STRONG></CODE>: Assigns a unique ID to the new button control.</LI><LI><CODE><STRONG>"text": "Excel Upload"</STRONG></CODE>: Sets the display text for the button on the UI.</LI><LI><CODE><STRONG>"press": "openSpreadsheetUpload"</STRONG></CODE>: Defines the event handler function that will be called when the user clicks the button.</LI></UL><P><STRONG>Pro-Tip:</STRONG> The <CODE>controllerName</CODE> (<CODE>com.deere.doerategrp.ext.controller.ListReportExt</CODE>) and the EntitySet key (<CODE>rateGroupSet</CODE>) are specific to this example. You must update these values to match your project's namespace and the target OData EntitySet for the table you are extending.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Excel Upload Button.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357717iE0FE0C1E7092D7F6/image-size/large?v=v2&amp;px=999" role="button" title="Excel Upload Button.png" alt="Excel Upload Button.png" /></span></P><H3 id="toc-hId-737620225">3.2 Step 2: Configure the Controller Extension (<CODE>ListReportExt.controller.js</CODE>)</H3><P>Next, you must implement the <CODE>openSpreadsheetUpload</CODE> event handler defined in the <CODE>manifest.json</CODE>. This function will reside in your custom controller extension file, <CODE>ListReportExt.controller.js</CODE>.</P><P>Use the following code to implement the function. It is important to only use the <CODE>openSpreadsheetUpload</CODE> function as provided; other methods mentioned in the source documentation, such as <CODE>attachChangeBeforeCreate</CODE> and <CODE>attachUploadButtonPress</CODE>, may not be usable and should be ignored.</P><PRE><CODE>sap.ui.define(["sap/m/MessageToast"], function (MessageToast) { 'use strict'; return { openSpreadsheetUpload: async function (oEvent) { MessageToast.show("Spreadsheet upload initiated."); this.getView().setBusyIndicatorDelay(0); this.getView().setBusy(true); this.spreadsheetUpload = await this.getView() .getController() .getOwnerComponent() .createComponent({ usage: "spreadsheetImporter", async: true, componentData: { context: this, columns: ['shippingPoint', 'destRegion', 'matFrtGroup', 'shippingType', 'validFrom', 'validTo', 'rateGroups'], mandatoryFields: ['shippingPoint', 'destRegion', 'matFrtGroup', 'shippingType', 'validFrom'], showBackendErrorMessages: true, continueOnError: true, useTableSelector: false, addArrayToMessages: true, strict: true, tableId: "&lt;yournamespace&gt;.doerategrp::sap.suite.ui.generic.template.ListReport.view.ListReport::rateGroupSet--responsiveTable", sampleData: [{ shippingPoint: 'HX1C', destRegion: 'MD', matFrtGroup: 'OMFR0005', shippingType: '10', validFrom: '9999-12-31', validTo: '9999-12-31', rateGroups: 'RT002' }] }, }); await this.spreadsheetUpload.triggerInitContext(); this.spreadsheetUpload.openSpreadsheetUploadDialog(); this.getView().setBusy(false); } }; });</CODE></PRE><P>The <CODE>componentData</CODE> object is used to configure the importer's behavior. Key parameters include:</P><UL><LI><CODE><STRONG>columns</STRONG></CODE>: An array of strings defining the OData property names that correspond to the columns in the spreadsheet.</LI><LI><CODE><STRONG>mandatoryFields</STRONG></CODE>: An array of strings specifying which columns must contain data for a row to be considered valid.</LI><LI><CODE><STRONG>tableId</STRONG></CODE>: The full ID of the target table in the List Report where the data will be inserted. You can find this ID by inspecting the table element in your browser's developer tools. It must be the complete, stable ID for the target <CODE>sap.m.Table</CODE> or <CODE>sap.ui.table.Table</CODE>.</LI><LI><CODE><STRONG>sampleData</STRONG></CODE>: Provides a sample data row that can be used to generate a downloadable template for users.</LI></UL><H3 id="toc-hId-541106720">3.3 Step 3: Define Component Dependencies in <CODE>manifest.json</CODE></H3><P>The application manifest must be updated to declare its dependency on the Spreadsheet Importer component. This allows the UI5 framework to locate and load the component's resources.</P><P>Add the following <CODE>componentUsages</CODE> and <CODE>resourceRoots</CODE> configurations to your <CODE>manifest.json</CODE>:</P><PRE><CODE>"componentUsages": { "spreadsheetImporter": { "name": "cc.spreadsheetimporter.v1_7_4" } }, "resourceRoots": { "cc.spreadsheetimporter.v1_7_4": "./thirdparty/customcontrol/spreadsheetimporter/v1_7_4" },</CODE></PRE><P>The key <CODE>spreadsheetImporter</CODE> is a local alias defined for this component's usage. This alias is what you will use to reference the component within your application's controller code. The <CODE>resourceRoots</CODE> path shown here is configured for local development and testing within SAP Business Application Studio (BAS). This path will be adjusted for deployment to an on-premise system, as detailed later in this guide.</P><H3 id="toc-hId-344593215">3.4 Step 4: Update Project Dependencies in <CODE>package.json</CODE></H3><P>To enable local development, testing, and builds, the Spreadsheet Importer component must be added as a dependency in the project's <CODE>package.json</CODE> file.</P><P>Add the following line to the <CODE>dependencies</CODE> section of your <CODE>package.json</CODE>:</P><PRE><CODE>// In your package.json, add the following to "dependencies": "ui5-cc-spreadsheetimporter": "^1.7.4"</CODE></PRE><H3 id="toc-hId-148079710"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image-2025-9-24_15-11-39.png" style="width: 688px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357720iEFD26EA05C0F2306/image-size/large?v=v2&amp;px=999" role="button" title="image-2025-9-24_15-11-39.png" alt="image-2025-9-24_15-11-39.png" /></span></H3><H3 id="toc-hId--123665164">3.5 Step 5: Configure the Build Script</H3><P>The final configuration step is to modify the build script in <CODE>package.json</CODE> to ensure that all dependencies, including the custom component, are correctly processed during the build.</P><P>Add the <CODE>--all</CODE> flag to the <CODE>"build"</CODE> script command:</P><PRE><CODE>"build": "ui5 build --config=ui5.yaml --all --clean-dest --dest dist",</CODE></PRE><P>With the application now fully configured for local development and testing, the next crucial phase is to prepare it for deployment to a production-like on-premise environment, which follows a specific, centralized strategy.</P><H2 id="toc-hId--26775662">4.0 On-Premise Deployment Strategy</H2><P>For on-premise landscapes, a centralized deployment model for the Spreadsheet Importer component is mandatory. This model leverages a single, pre-existing instance of the component that has been deployed centrally as a BSP application. This simplifies individual application deployments, as you only need to reference this central component rather than deploying it with every application.</P><H3 id="toc-hId--516692174">4.1 Understanding the <CODE>resourceRoots</CODE> Path for Deployed Applications</H3><P>The <CODE>resourceRoots</CODE> path in the <CODE>manifest.json</CODE> file is environment-specific. The path used for local development in BAS is different from the one required for a deployed application running on the FIORI Launchpad.</P><TABLE border="1"><TBODY><TR><TD><P>Environment</P></TD><TD><P><CODE>resourceRoots</CODE> Path in <CODE>manifest.json</CODE></P></TD></TR><TR><TD><P><STRONG>BAS / Local Development</STRONG></P></TD><TD><P><CODE>"./thirdparty/customcontrol/spreadsheetimporter/v1_7_4"</CODE></P></TD></TR><TR><TD><P><STRONG>On-Premise Deployment</STRONG></P></TD><TD><P><CODE>"./thirdparty/customcontrol/spreadsheetimporter/v1_7_4"</CODE></P></TD></TR></TBODY></TABLE><P>The path for the deployed application points to a centrally available BSP application named <CODE><STRONG>ZVA0_EXL_UPD</STRONG></CODE>. This BSP application already contains the approved version 1.7.4 of the Spreadsheet Importer component, making it available to all other FIORI applications on the same system.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image-2025-9-24_12-4-52.png" style="width: 609px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357725i750D974D81C7CB0F/image-size/large?v=v2&amp;px=999" role="button" title="image-2025-9-24_12-4-52.png" alt="image-2025-9-24_12-4-52.png" /></span></P><H3 id="toc-hId--713205679">4.2 Recommended Deployment Configuration for Consistency</H3><P>To avoid manually changing the <CODE>resourceRoots</CODE> path in <CODE>manifest.json</CODE> before each deployment, the recommended best practice is to update the build configuration. By excluding certain development-only files from the deployment package, you can maintain a single, consistent <CODE>manifest.json</CODE> for both local and deployed environments.</P><P>Update your <CODE>ui5-deploy.yaml</CODE> file to exclude TypeScript (<CODE>.ts</CODE>, <CODE>.ts.map</CODE>) and JavaScript map (<CODE>.js.map</CODE>) files from the deployment artifact.</P><PRE><CODE>- name: deploy-to-abap afterTask: generateCachebusterInfo configuration: target: # ... target system details app: # ... application details exclude: - /test/ - .*\.ts - .*\.ts.map - .*\.js.map</CODE></PRE><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image-2025-11-18_12-56-58.png" style="width: 354px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357723i55DF4BE0CD2BB27F/image-size/large?v=v2&amp;px=999" role="button" title="image-2025-11-18_12-56-58.png" alt="image-2025-11-18_12-56-58.png" /></span></P><P>This approach is critical for robust development and deployment pipelines, as it eliminates the need for error-prone manual modifications to the <CODE>manifest.json</CODE> file between environments. The local <CODE>resourceRoots</CODE> path is automatically ignored during deployment, and the relative path to the central BSP is correctly resolved when the application runs on the FIORI Launchpad.</P><H2 id="toc-hId--616316177">5.0 Troubleshooting and Known Issues</H2><P>While the integration process is generally straightforward, developers should be aware of a known issue that may occur during the deployment from SAP Business Application Studio to an on-premise system.</P><P>When deploying an application that uses the Spreadsheet Importer component, the deployment process in BAS may report a failure. However, in most cases, the BSP application is still created or updated successfully in the backend ABAP system. This behavior is a known open issue that has been reported to SAP.</P><P>For more details on this issue, please refer to the SAP Community discussion: <A href="https://community.sap.com/t5/technology-q-a/error-when-deploying-ui5-app-sapui5-library-component-used-in-application/qaa-p/12742483#M4782696" target="_blank">Error when deploying UI5 app (sapui5 library/component used in application)</A></P><P>If you encounter this error, verify the status of the BSP application directly in the backend system before attempting to redeploy.</P><H2 id="toc-hId--812829682">6.0 Final Validation</H2><P>The final step is to verify that the Spreadsheet Importer component is fully functional in the target environment.</P><P>After a successful deployment, launch your application from the SAP FIORI Launchpad. You should see the <STRONG>"Excel Upload"</STRONG> button on the List Report page. Clicking this button should open the spreadsheet import dialog, confirming that the integration was successful and is ready for users to begin the data import process.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-01-02 175700.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357726iE18215CFBA9D1257/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-01-02 175700.png" alt="Screenshot 2026-01-02 175700.png" /></span></P><H2 id="toc-hId--1009343187"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-01-02 180156.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/357727i5450CA8CAA9671C8/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-01-02 180156.png" alt="Screenshot 2026-01-02 180156.png" /></span></H2><H2 id="toc-hId--1205856692">7.0 Appendix: Key Resources</H2><P>For further information, detailed documentation, and community support, please refer to the following resources:</P><UL><LI><STRONG>Official GitHub Repository:</STRONG> <A href="https://github.com/marianfoo/ui5-cc-spreadsheetimporter" target="_blank" rel="noopener nofollow noreferrer">ui5-cc-spreadsheetimporter</A></LI><LI><STRONG>Live Demo Launchpad:</STRONG> <A href="https://livedemo.spreadsheet-importer.com/launchpad.html#Shell-home" target="_blank" rel="noopener nofollow noreferrer">Spreadsheet Importer Live Demo</A></LI><LI><STRONG>NPM Package:</STRONG> <A href="https://www.npmjs.com/package/ui5-cc-spreadsheetimporter" target="_blank" rel="noopener nofollow noreferrer">ui5-cc-spreadsheetimporter</A></LI><LI><STRONG>Yeoman Generator:</STRONG> <A href="https://www.npmjs.com/package/generator-ui5-cc-spreadsheetimporter" target="_blank" rel="noopener nofollow noreferrer">generator-ui5-cc-spreadsheetimporter</A></LI><LI><STRONG>Official Documentation:</STRONG> <A href="https://docs.spreadsheet-importer.com/" target="_blank" rel="noopener nofollow noreferrer">Spreadsheet Importer Docs</A></LI><LI><STRONG>Example Applications:</STRONG> <A href="https://github.com/marianfoo/ui5-cc-spreadsheetimporter/tree/main/examples" target="_blank" rel="noopener nofollow noreferrer">Code Samples</A></LI><LI><STRONG>wdi5 Tests:</STRONG> <A href="https://github.com/marianfoo/ui5-cc-spreadsheetimporter/tree/main/examples/packages/ui5-app-wdi5" target="_blank" rel="noopener nofollow noreferrer">Test Examples</A></LI></UL> 2026-01-02T13:41:22.383000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/simplifying-ui5-library-use-on-btp-by-bundling-libraries-at-build-time/ba-p/14304012 Simplifying UI5 Library Use on BTP by Bundling Libraries at Build Time 2026-01-10T23:27:11.400000+01:00 MioYasutake https://community.sap.com/t5/user/viewprofilepage/user-id/789 <H2 id="toc-hId-1787732797">Background: Reusing UI5 Libraries on SAP BTP</H2><P>When using a custom UI5 library on SAP BTP, the most common approach is to deploy the library source first and then consume it from other applications.</P><P>Based on previous blogs and samples, there are two established approaches, which can be broadly categorized into an older and a newer approach.</P><H3 id="toc-hId-1720302011">Approaches Without CDM (older approach)</H3><UL><LI><STRONG>Shared Destination Instance</STRONG>: In this approach, the library and the consuming applications share the same destination service instance (<A href="https://community.sap.com/t5/technology-blog-posts-by-sap/designing-ui5-apps-for-sap-launchpad-service-part-3/ba-p/13513569" target="_self">blog 1</A>).</LI><LI><STRONG>Separate Destination Instances per Application</STRONG>: Each application uses its own destination service instance, and the consuming application explicitly defines destinations for the library (HTML5 app repo host and UAA) (<A href="https://community.sap.com/t5/technology-blog-posts-by-sap/designing-ui5-apps-for-sap-launchpad-service-part-4/ba-p/13724339" target="_self">blog 2</A>).</LI></UL><H3 id="toc-hId-1523788506">Approaches with CDM (newer approach)</H3><UL><LI><STRONG>Library as Content Provider:</STRONG> When using CDM, the library defines a content provider destination and a cdm.json file containing skeleton definitions for catalogs, groups, and roles. The consuming application depends on the library's HTML5 app repo host (<A href="https://community.sap.com/t5/technology-blog-posts-by-sap/designing-ui5-apps-as-business-solution-for-sap-build-work-zone-part-3/ba-p/13976027" target="_self">blog 3</A>).</LI></UL><H3 id="toc-hId-1327275001">Benefits and Limitations of Deploying Libraries Independently</H3><P>Deploying the library source as an independent artifact has a clear advantage: when the library changes, it needs to be fixed or updated in only one place.</P><P>However, this approach makes the deployment of consuming applications more complex. The application's mta.yaml requires additional, library-specific configuration. This complexity is manageable when only a single shared library is used, but it increases significantly when multiple independent libraries are involved.</P><P>Using CDM can reduce this complexity. However, CDM is primarily intended for defining the layout and content structure in SAP Build Work Zone from the application side, and in my opinion, it is more suitable for SaaS-oriented solutions. Therefore, it does not fit well with workflows where application developers implement the apps, while a separate authorization or platform team designs and manages the Work Zone contents.</P><H3 id="toc-hId-1130761496">An Alternative Approach: Bundling the Library at Build Time</H3><P>In this blog, a different approach is introduced: bundling the library source directly into the consuming application at build time and deploying them together. The main advantages of this approach are simpler application configuration and the removal of dependencies on resources defined by the library, such as destinations or HTML5 app host service instances.</P><P>The main drawback is that when the library changes, each consuming application must be redeployed individually. On the other hand, this also has a positive side effect: incompatible changes in the library can be introduced without impacting existing applications that rely on older versions.</P><H2 id="toc-hId-805165272">Developing and Deploying Applications with a Bundled UI5 Library</H2><P>The following sections describe how to develop an application by bundling a UI5 library directly into the consuming application.</P><H3 id="toc-hId-737734486">Prerequisites</H3><UL><LI><STRONG>SAP Fiori tools</STRONG> is installed in Visual Studio Code.</LI></UL><H3 id="toc-hId-541220981">Steps Overview</H3><OL><LI>Create the UI5 library</LI><LI>Create an application that consumes the library</LI><LI>Configure the project for deployment</LI><LI>Build and Deploy</LI></OL><H3 id="toc-hId-344707476">&nbsp;</H3><H3 id="toc-hId-148193971">1. Create the UI5 library</H3><P>Open the Command Pallete and select "<STRONG>Fiori: Open Reusable Library Generator</STRONG>".</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Command Palette showing Fiori: Open Reusable Library Generator option" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359953i49FE57073CC2845F/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_0-1767992221609.png" alt="MioYasutake_0-1767992221609.png" /></span></P><P>Fill the project details as shown below.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Library Generator wizard showing project details configuration" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359975i4C0427B790235E99/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_1-1767992633906.png" alt="MioYasutake_1-1767992633906.png" /></span></P><H4 id="toc-hId--416953910">Clean Up the Generated Library</H4><P>Under the <CODE>/src/&lt;namespace&gt;/&lt;libraryname&gt;</CODE> folder, remove all sample content except for the following files:</P><UL><LI><CODE>.library</CODE></LI><LI><CODE>library.ts</CODE></LI><LI><CODE>messagebundle.properties</CODE></LI></UL><P>After the cleanup, the folder structure should look like the image below.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Library folder structure after cleanup showing essential files" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359980i5B2B96A510241524/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_3-1767993147983.png" alt="MioYasutake_3-1767993147983.png" /></span></P><H4 id="toc-hId--613467415">Replace the Example Controller Logic</H4><P>The generated <CODE>library.ts</CODE> file contains example controller logic that is not required for this scenario. Replace its content with the implementation provided in the following GitHub repository:</P><P><A href="https://github.com/miyasuta/fiori-use-library-blog/blob/main/com.myorg.reuselib/src/com/myorg/reuselib/library.ts" target="_blank" rel="noopener nofollow noreferrer">https://github.com/miyasuta/fiori-use-library-blog/blob/main/com.myorg.reuselib/src/com/myorg/reuselib/library.ts</A></P><P>This sample library provides a dialog with controller logic attached.</P><P>The complete implementation can be found here:</P><P><A href="https://github.com/miyasuta/fiori-use-library-blog/tree/main/com.myorg.reuselib/src/com/myorg/reuselib" target="_blank" rel="noopener nofollow noreferrer">https://github.com/miyasuta/fiori-use-library-blog/tree/main/com.myorg.reuselib/src/com/myorg/reuselib</A></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Library implementation files including dialog and controller" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359981iB9032C3A0AA9314C/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_4-1767993461175.png" alt="MioYasutake_4-1767993461175.png" /></span></P><P>Since the project uses TypeScript, make sure to build the library after applying the changes:</P><pre class="lia-code-sample language-bash"><code>npm run build</code></pre><H3 id="toc-hId--516577913">&nbsp;</H3><H3 id="toc-hId--713091418">2. Create an Application That Consumes the Library</H3><P>Next, create a UI5 application that consumes the library.</P><H4 id="toc-hId--1203007930">Generate the application</H4><P>Open the Application Generator and select the <STRONG>"Basic"</STRONG> template (other templates such as <EM>List Report</EM> can also be used).</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Application Generator showing Basic template selection" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359982i94E4B6D0F5E4948C/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_5-1767993668031.png" alt="MioYasutake_5-1767993668031.png" /></span></P><P>Enter the project details. Select both "<STRONG>Add Deployment Configuration</STRONG>" and "<STRONG>Add SAP Fiori Launchpad Configuration</STRONG>".</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Application Generator project details with deployment options" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359983i530BDA097D8BA3C8/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_6-1767993769822.png" alt="MioYasutake_6-1767993769822.png" /></span></P><P>For the deployment configuration:</P><UL><LI>Select <STRONG>Cloud Foundry</STRONG> as the target</LI><LI>Select <STRONG>"Add Application to Managed Application Router"</STRONG>, since the app will run on SAP Build Work Zone</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Deployment configuration for Cloud Foundry with Managed Application Router" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359984i4ECE3EEE40394ACC/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_7-1767993923859.png" alt="MioYasutake_7-1767993923859.png" /></span></P><P>Configure the SAP Fiori Launchpad settings as shown below.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SAP Fiori Launchpad configuration settings" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359985iF5FB2DCCF5E87131/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_8-1767994000656.png" alt="MioYasutake_8-1767994000656.png" /></span></P><H4 id="toc-hId--1399521435">Add a Reference to the Library</H4><P>Now that both the library and the consuming application exist, connect them.</P><P>Open the Command Pallete and select "<STRONG>Fiori: Add Reference to SAP Fiori Reusable Libraries</STRONG>".</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Command Palette showing Fiori: Add Reference to SAP Fiori Reusable Libraries option" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359987i94145F33ADB9E0D0/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_9-1767994928640.png" alt="MioYasutake_9-1767994928640.png" /></span></P><P>Select the project folder path (consumerapp) and the library.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Library selection dialog showing available reusable libraries" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359988iE11EC154811A6369/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_10-1767995063589.png" alt="MioYasutake_10-1767995063589.png" /></span></P><P>This action adds entries to <CODE>manifest.json</CODE>, <CODE>ui5.yaml</CODE>, and <CODE>ui5-local.yaml</CODE>.</P><P><STRONG>manifest.json</STRONG></P><pre class="lia-code-sample language-json"><code> "sap.ui5": { "flexEnabled": true, "dependencies": { "minUI5Version": "1.143.2", "libs": { "sap.m": {}, "sap.ui.core": {}, "com.myorg.reuselib": { "lazy": false } } },</code></pre><P><STRONG>ui5.yaml / ui5-local.yaml</STRONG></P><P>By default, the <CODE>src</CODE> path does not point to <CODE>dist</CODE>. Because the library is implemented in TypeScript, it is adjusted to reference the compiled output in the <CODE>dist</CODE> folder.</P><pre class="lia-code-sample language-yaml"><code> - name: fiori-tools-servestatic beforeMiddleware: fiori-tools-proxy configuration: paths: - path: /resources/com/myorg/reuselib # src: ../com.myorg.reuselib/src/com/myorg/reuselib src: ../com.myorg.reuselib/dist/resources/com/myorg/reuselib fallthrough: false</code></pre><P>To ensure that TypeScript recognizes the library's type definitions, add the following to <CODE>tsconfig.json</CODE>:</P><pre class="lia-code-sample language-json"><code> "include": [ "./webapp/**/*", "../com.myorg.reuselib/dist/index.d.ts" ]</code></pre><H4 id="toc-hId--1596034940">&nbsp;</H4><H4 id="toc-hId--1792548445">Use the Library in the Application</H4><P>Now the library can be used in the application locally.</P><P>In this example, a button is placed on the UI. When pressed, it opens a dialog defined in the library. After the user presses <STRONG>OK</STRONG>, a callback is triggered, allowing the consuming application to execute its own logic.</P><P><STRONG>View</STRONG></P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="miyasuta.consumerapp.controller.View1" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"&gt; &lt;Page id="page" title="{i18n&gt;title}"&gt; &lt;Button id="pressMeButton" text="Press Me" press="onOpenDialog" /&gt; &lt;/Page&gt; &lt;/mvc:View&gt;</code></pre><P><STRONG>Controller</STRONG></P><pre class="lia-code-sample language-javascript"><code>import Controller from "sap/ui/core/mvc/Controller"; import MyDialogHanlder, { MyDialogHandler$SubmitEvent } from "com/myorg/reuselib/controller/MyDialogHandler"; import MessageToast from "sap/m/MessageToast"; /** * miyasuta.consumerapp.controller */ export default class View1 extends Controller { /*eslint-disable @typescript-eslint/no-empty-function*/ public onInit(): void { } public onOpenDialog(): void { const dialogHandler = new MyDialogHanlder(); dialogHandler.attachSubmit((event: MyDialogHandler$SubmitEvent) =&gt; { const message = event.getParameter("message"); MessageToast.show(`Dialog submitted with message: ${message}`); }); dialogHandler.open(); } }</code></pre><H3 id="toc-hId--1695658943">&nbsp;</H3><H3 id="toc-hId--1892172448">3. Configure the Project for Deployment</H3><P>This section describes the key configuration required to <STRONG>bundle the library source into the consuming application at build time</STRONG>.</P><P>&nbsp;</P><H4 id="toc-hId-2081062027">3.1 Library Project Configuration</H4><P><STRONG>ui5.yaml</STRONG></P><P>Update <CODE>metadata.name</CODE> to include the full namespace:</P><pre class="lia-code-sample language-yaml"><code>metadata: # name: "reuselib" name: com.myorg.reuselib</code></pre><P>Add a <CODE>resources</CODE> section to the builder configuration to exclude test resources from the build:</P><pre class="lia-code-sample language-yaml"><code>builder: resources: excludes: - "/test-resources/**"</code></pre><P>The complete <CODE>builder</CODE> section will look like this:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Complete ui5.yaml builder configuration for the library" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/360086i0BCB9395932FE574/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_0-1768076704730.png" alt="MioYasutake_0-1768076704730.png" /></span></P><H4 id="toc-hId-1884548522">&nbsp;</H4><H4 id="toc-hId-1688035017">3.2 Application Project Configuration</H4><P><STRONG>package.json</STRONG></P><P>Add the library as a dependency and specify the version in the library's package.json.</P><pre class="lia-code-sample language-json"><code> "dependencies": { "com.myorg.reuselib": "^1.0.0" },</code></pre><P><STRONG>ui5-deploy.yaml</STRONG></P><P>Configure the builder to include the library dependency during the build:</P><pre class="lia-code-sample language-yaml"><code>builder: settings: includeDependency: - com.myorg.reuselib</code></pre><P>Add <CODE>includeDependencies</CODE> to the <CODE>ui5-task-zipper</CODE> configuration so that the library resources are included in the final ZIP archive:</P><pre class="lia-code-sample language-yaml"><code> - name: ui5-task-zipper afterTask: generateCachebusterInfo configuration: ... includeDependencies: - com.myorg.reuselib ...</code></pre><P>The complete <CODE>builder</CODE> section will look like this:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Complete ui5-deploy.yaml builder configuration for the application" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/360089i9F8623629906382C/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_3-1768080412099.png" alt="MioYasutake_3-1768080412099.png" /></span></P><P><STRONG>xs-app.json</STRONG></P><P>Add the following route to ensure that library resources are served from the application instead of the UI5 CDN.</P><pre class="lia-code-sample language-json"><code> { "source": "^/resources/com/myorg/reuselib/(.*)$", "target": "/resources/com/myorg/reuselib/$1", "service": "html5-apps-repo-rt", "authenticationType": "xsuaa" },</code></pre><P><STRONG>manifest.json</STRONG></P><P>Add <CODE>resourceRoots</CODE> so that the library can be resolved when launched from SAP Build Work Zone:</P><pre class="lia-code-sample language-json"><code> "sap.ui5": { "flexEnabled": true, "resourceRoots": { "com.myorg.reuselib": "./resources/com/myorg/reuselib" }</code></pre><P><STRONG>index.html</STRONG></P><P>Add the library to <CODE>data-sap-ui-resource-roots</CODE> so that it can be resolved when running from the HTML5 Application Repository:</P><pre class="lia-code-sample language-markup"><code> data-sap-ui-resource-roots='{ "miyasuta.consumerapp": "./", "com.myorg.reuselib": "./resources/com/myorg/reuselib" }'</code></pre><H3 id="toc-hId-1784924519">&nbsp;</H3><H3 id="toc-hId-1588411014">4. Build and Deploy</H3><P>Run the following command in the consuming application project:</P><pre class="lia-code-sample language-bash"><code>npm run build:mta</code></pre><P>Verify that the library resources are included under the <CODE>dist/resources</CODE> folder.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Build output showing library resources in dist/resources folder" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/360088i95F5F17366D14818/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="MioYasutake_2-1768079434521.png" alt="MioYasutake_2-1768079434521.png" /></span></P><P>Deploy the application to Cloud Foundry:</P><pre class="lia-code-sample language-bash"><code>npm run deploy</code></pre><P>Finally, verify that the application can be launched successfully from both the HTML5 Application Repository and SAP Build Work Zone.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Application running successfully in SAP Build Work Zone with dialog from library" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/360090iD1240BB07B8D677F/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="MioYasutake_4-1768080498442.png" alt="MioYasutake_4-1768080498442.png" /></span></P><H2 id="toc-hId-1685300516">Conclusion</H2><P>Reusing UI5 libraries on SAP BTP offers multiple established patterns, but these often introduce additional deployment complexity for consuming applications.</P><P>This blog presented an alternative approach: bundling the UI5 library source into the consuming application at build time. While this approach requires changes across several configuration files, all adjustments remain application-scoped, reducing cross-project dependencies and overall complexity.</P><P>This approach is not intended to replace existing patterns, but to provide a pragmatic option for scenarios where applications are developed and deployed independently.</P> 2026-01-10T23:27:11.400000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464 SAP UI5 - QUnit-Testing 2026-01-11T23:36:30.594000+01:00 dreichert https://community.sap.com/t5/user/viewprofilepage/user-id/835133 <P><ul =""><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId-1638366408">1. Setup</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId-1570935622">1.1 Installing the required libraries</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId-1177908612">1.2 Folder structure</a></li><li style="list-style-type:disc; margin-left:30px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--143834573">1.2.1 AllTests.js</a></li><li style="list-style-type:disc; margin-left:30px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--340348078">1.2.2 unitTests.qunit.html</a></li><li style="list-style-type:disc; margin-left:30px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--536861583">1.2.3 unitTests.qunit.js</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--146569074">2. Implementing unit tests</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--636485586">2.1 Basic structure of a test module</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--832999091">2.2 Simple Test using sinon.stub and sinon.spy</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--1029512596">2.3 Testing functions with Return Values or Promises</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--1226026101">2.4 Testing error cases and exceptions</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--1422539606">2.5 Larger example with more branches</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId-1172365833">3. Running tests and coverage</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId-850633012">3.1 Running tests</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId-457606002">3.2 Test results in the QUnit interface</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId-261092497">3.3 Enabling code coverage</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--328448018">3.4 Detailed view of individual files</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--524961523">3.5 Coverage reports</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-qunit-testing/ba-p/14295464#toc-hId--428072021">4. Conclusion</a></li></ul></P><P>In this post, I share practical experience with QUnit tests from my UI5 projects.<BR />This post is aimed at developers who are already familiar with SAP UI5 and just getting started with QUnit. It was originally intended as an onboarding guide, but I noticed that information on this topic is quite scattered.<BR />The examples in this post are kept close to real project code and can also be used as a reference when implementing similar test cases.</P><P>&nbsp;</P><H1 id="toc-hId-1638366408">1. Setup</H1><H2 id="toc-hId-1570935622">1.1 Installing the required libraries</H2><P>To implement and execute QUnit tests in a UI5 application, just a few libraries are needed.<BR />We just add them to the dependencies section of the application's package.json.</P><P>Depending on the project, either a specific version or simply <EM>latest</EM> can be used.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="2bf0122c-3d55-455e-9ff0-7f420cfa5216.png" style="width: 467px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/354787i71265CF561ED7782/image-size/large?v=v2&amp;px=999" role="button" title="2bf0122c-3d55-455e-9ff0-7f420cfa5216.png" alt="2bf0122c-3d55-455e-9ff0-7f420cfa5216.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>For later test coverage analysis (<EM>see section 3. Running Tests &amp; Coverage</EM>), an additional library should be included:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="fc07ecd0-4e3d-40a4-acc5-db89aaeec475.png" style="width: 371px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/354793iD74A597BA6C9FAD2/image-size/large?v=v2&amp;px=999" role="button" title="fc07ecd0-4e3d-40a4-acc5-db89aaeec475.png" alt="fc07ecd0-4e3d-40a4-acc5-db89aaeec475.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>This library also needs to be added to the ui5.yaml under "server&gt;customMiddleware&gt;".</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="0c5b668b-f789-4c0f-b720-2c3759930ba2.png" style="width: 321px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/354794iADF3774B45A507D2/image-size/medium?v=v2&amp;px=400" role="button" title="0c5b668b-f789-4c0f-b720-2c3759930ba2.png" alt="0c5b668b-f789-4c0f-b720-2c3759930ba2.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>After adding the required libraries, they can be installed using:</P><pre class="lia-code-sample language-bash"><code>npm install</code></pre><H2 id="toc-hId-1374422117">&nbsp;</H2><H2 id="toc-hId-1177908612">1.2 Folder structure</H2><P>The folder structure for the unit tests is quite simple and is set up as follows:</P><pre class="lia-code-sample language-bash"><code>- webapp - test - unit AllTests.js unitTests.qunit.html unitTests.qunit.js</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="d4c4ece3-f37d-4551-937b-eb15392cf5d0.png" style="width: 309px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/354795i9077DB597E82660B/image-size/large?v=v2&amp;px=999" role="button" title="d4c4ece3-f37d-4551-937b-eb15392cf5d0.png" alt="d4c4ece3-f37d-4551-937b-eb15392cf5d0.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>The actual test files should ideally follow a structure similar to the application itself.<BR />This not a rule -&gt; <STRONG>just for readability</STRONG><BR />For example, if unit tests are required for <EM>BaseController.js</EM>, a corresponding folder <EM>controller</EM> and a file <EM>BaseController.js</EM> should be created inside the test directory:</P><pre class="lia-code-sample language-bash"><code>- webapp - controller BaseController.js - test - unit - controller BaseController.js</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="21df0360-5d37-40a8-9ee4-560ab966e762.png" style="width: 309px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/354796i94B02ED89C3398E4/image-size/large?v=v2&amp;px=999" role="button" title="21df0360-5d37-40a8-9ee4-560ab966e762.png" alt="21df0360-5d37-40a8-9ee4-560ab966e762.png" /></span></P><P>&nbsp;</P><H2 id="toc-hId-981395107">&nbsp;</H2><H2 id="toc-hId-784881602">&nbsp;</H2><H2 id="toc-hId-588368097">&nbsp;</H2><H2 id="toc-hId-391854592">&nbsp;</H2><H2 id="toc-hId-195341087">&nbsp;</H2><H2 id="toc-hId--1172418">&nbsp;</H2><H3 id="toc-hId--143834573">1.2.1 AllTests.js</H3><P>All QUnit tests to be executed are included in the define section of this file.<BR />It is a central place where we can enable or disable tests for execution.</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "./controller/BaseController", // "./controller/OtherController", // "./controller/OtherController2", // "./services/SampleService", // "./util/SampleFormatter", ], function () { "use strict"; });</code></pre><P>(Tip)</P><P>When working on unit tests for a specific controller an running them frequently, tests for other controllers can be commented out here, so they are not executed each time.</P><P>&nbsp;</P><H3 id="toc-hId--340348078">1.2.2 unitTests.qunit.html</H3><P>Short and simple: we start our tests using this file.<BR />All required libraries for test execution are included here.<BR /><BR /></P><pre class="lia-code-sample language-markup"><code>&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;Unit tests for Template&lt;/title&gt; &lt;meta charset="utf-8"&gt; &lt;script id="sap-ui-bootstrap" src="../../../../resources/sap-ui-core.js" data-sap-ui-resourceroots='{ "app.unittester": "../" }' data-sap-ui-async="true" data-sap-ui-preload="async"&gt; &lt;/script&gt; &lt;link rel="stylesheet" type="text/css" href="../../../../resources/sap/ui/thirdparty/qunit-2.css"&gt; &lt;script src="../../../../resources/sap/ui/thirdparty/qunit-2.js"&gt;&lt;/script&gt; &lt;script src="../../../../resources/sap/ui/qunit/qunit-junit.js"&gt;&lt;/script&gt; &lt;script src="../../../../resources/sap/ui/thirdparty/sinon.js"&gt;&lt;/script&gt; &lt;script src="../../../../resources/sap/ui/thirdparty/sinon-qunit.js"&gt;&lt;/script&gt; &lt;script src="../../../../resources/sap/ui/qunit/qunit-coverage-istanbul.js" data-sap-ui-cover-only="app/unittester/" data-sap-ui-cover-never="app/unittester/test, app/unittester/localServices, app/unittester/services"&gt; &lt;/script&gt; &lt;script src="../../../../resources/sap/ui/thirdparty/sinon.js"&gt;&lt;/script&gt; &lt;script src="../../../../resources/sap/ui/thirdparty/sinon-qunit.js"&gt;&lt;/script&gt; &lt;script src="unitTests.qunit.js"&gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="qunit"&gt;&lt;/div&gt; &lt;div id="qunit-fixture"&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt;</code></pre><P>Just a few notes on the most important scripts that are included here (because we will use them later).</P><P><STRONG>QUnit</STRONG></P><pre class="lia-code-sample language-markup"><code>&lt;link rel="stylesheet" type="text/css" href="../../../../resources/sap/ui/thirdparty/qunit-2.css"&gt; &lt;script src="../../../../resources/sap/ui/thirdparty/qunit-2.js"&gt;&lt;/script&gt; &lt;script src="../../../../resources/sap/ui/qunit/qunit-junit.js"&gt;&lt;/script&gt;</code></pre><P>(Roughly summarized) The actual QUnit framework is included here.</P><P><STRONG>sinon and sinon-qunit</STRONG></P><pre class="lia-code-sample language-markup"><code>&lt;script src="../../../../resources/sap/ui/thirdparty/sinon.js"&gt;&lt;/script&gt; &lt;script src="../../../../resources/sap/ui/thirdparty/sinon-qunit.js"&gt;&lt;/script&gt;</code></pre><P>These libraries enable spying, stubbing and mocking dependencies.</P><P><STRONG>qunit-coverage-istanbul.js (Code Coverage Integration)</STRONG></P><pre class="lia-code-sample language-markup"><code>&lt;script src="../../../../resources/sap/ui/qunit/qunit-coverage-istanbul.js" data-sap-ui-cover-only="app/unittester/" data-sap-ui-cover-never="[ app/unittester/test, app/unittester/localServices, app/unittester/services ]"&gt; &lt;/script&gt;</code></pre><P>This is required later for code coverage.</P><P>(Note) In this post, we use Istanbul for code coverage.<BR />Of course, other options such as Karma can also be used if desired.</P><P>Important attributes:</P><UL><LI><STRONG>data-sap-ui-cover-only</STRONG><BR />files to be included in the coverage</LI><LI><STRONG>data-sap-ui-cover-never</STRONG><BR />files and directories to be excluded from coverage</LI></UL><P>&nbsp;</P><H3 id="toc-hId--536861583">1.2.3 unitTests.qunit.js</H3><P>This is the central entry point for the unit tests.<BR />Here we only include the tests from AllTests.js in the define section and start the test run.</P><pre class="lia-code-sample language-javascript"><code>QUnit.config.autostart = false; sap.ui.getCore().attachInit(function () { "use strict"; sap.ui.require([ "app/unittester/test/unit/AllTests" ], function () { QUnit.start(); }); });</code></pre><H1 id="toc-hId--146569074">2. Implementing unit tests</H1><P>In this example, we will look at some unit tests for the BaseController as well as the basic structure.<BR />In addition, we will see how sinon can be used to handle dependencies.<BR /><SPAN><BR /></SPAN></P><H2 id="toc-hId--636485586">2.1 Basic structure of a test module</H2><P>Each module has its own tests and provides lifecycle hooks such as beforeEach and afterEach.<BR />The modules can be structured freely, depending on the requirements.</P><P>We start with a test module for BaseController.js and implement the module in the test file.<BR />(webapp&gt;test&gt;unit&gt;controller&gt;BaseController)</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/unittester/controller/BaseController", "sap/ui/thirdparty/sinon", "sap/ui/thirdparty/sinon-qunit", ], function(BaseController) { "use strict"; /** * Module */ QUnit.module("Module", { beforeEach: function() { this.oBaseController = new BaseController(); }, afterEach: function() { this.oBaseController.destroy(); } }); });</code></pre><P>First, we include the actual BaseController and the two sinon libraries in the define statement.</P><P>After that, the module itself:<BR />The lifecycle functions of the module are executed before and after each test.<BR />In order to test the functions of the BaseController, we instatiate it before the test and destroy it afterwards.</P><H2 id="toc-hId--832999091">2.2 Simple Test using sinon.stub and sinon.spy</H2><P>In our first test, we will take the <STRONG>getRouter</STRONG> function from our BaseController.</P><pre class="lia-code-sample language-javascript"><code>getRouter: function() { return this.getOwnerComponent().getRouter(); },</code></pre><P>We only want to test whether the function can be executed.</P><P>However, it has two dependencies:<BR />this.getOwnerComponent and its function getRouter.</P><P>In order to test getRouter without having to instantiate all dependencies, we will "simulate" them with sinon.</P><P>This would look like the following:</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/unittester/controller/BaseController", "sap/ui/thirdparty/sinon", "sap/ui/thirdparty/sinon-qunit", ], function(BaseController) { "use strict"; /** * Module */ QUnit.module("Module", { beforeEach: function() { this.oBaseController = new BaseController(); }, afterEach: function() { this.oBaseController.destroy(); } }); /** * BaseController - Check getRouter */ QUnit.test("BaseController - Check getRouter", function(assert) { sinon.stub(this.oBaseController, "getOwnerComponent").returns({ getRouter: sinon.stub() }); const fnSpy = sinon.spy(this.oBaseController, "getRouter"); this.oBaseController.getRouter(); assert.ok(fnSpy.calledOnce, "Check getRouter successful"); }); });</code></pre><P>The function flow:</P><P>In line 24, we create a stub for this.oBaseController (our controller instance) and listen for calls to the getOwnerComponent function.<BR />As soon as this function is executed, a return value is "simulated" that provides another simulated function, getRouter.</P><P>After that, we create a spy for this.oBaseController, that listens for calls to the getRouter function.</P><P>Then comes the actual function call in line 29.</P><P>Finally, we use an assertion where we check (via fnSpy.calledOnce) whether getRouter was executed at least once within the function.</P><P>And that's basically it. Every following test has the same structure (sometimes more or less complex).</P><P><STRONG>Running the test:<BR /></STRONG><SPAN>If we look at the coverage here now (more on this later), we can see that the function was executed exactly once:</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Citrix.DesktopViewer.App_Ap7HtUUpEr.png" style="width: 420px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356299i98D20494C4CF1148/image-size/large?v=v2&amp;px=999" role="button" title="Citrix.DesktopViewer.App_Ap7HtUUpEr.png" alt="Citrix.DesktopViewer.App_Ap7HtUUpEr.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId--1029512596">2.3 Testing functions with Return Values or Promises</H2><P>Many controller methods are asynchronous and return a Promise.</P><P>These tests are implemented using <EM>async</EM> and <EM>await</EM>.<BR />Using <EM>await</EM> ensures that the test waits properly for execution to finish.</P><P>Example: <STRONG>onStartEditMode<BR /></STRONG></P><pre class="lia-code-sample language-javascript"><code>onStartEditMode: async function () { try { this._fireSetViewBusy(true); await this.oDraftManager.onEdit(); } catch (oError) { this.evaluateException(oError); } finally { this._fireSetViewBusy(false); } },</code></pre><P>The corresponding test:</P><pre class="lia-code-sample language-javascript"><code>QUnit.test("BaseController - Start edit mode", async function (assert) { sinon.stub(this.oBaseController, "_fireSetViewBusy"); sinon.stub(this.oBaseController, "evaluateException"); this.oBaseController.oDraftManager = { onEdit: sinon.stub().returns(Promise.resolve(true)) }; const fnSpy = sinon.spy(this.oBaseController, "onStartEditMode"); await this.oBaseController.onStartEditMode(); assert.ok(fnSpy.calledOnce, "onStartEditMode executed successfully"); });</code></pre><H2 id="toc-hId--1226026101">2.4 Testing error cases and exceptions</H2><P>Error cases are just as important as successful scenarios.</P><P>In this example, we provoke an error by not stubbing any dependencies, so the function runs into the error path.<BR />This verifies that the error handling logic is executed correctly.</P><P>Example function onFullScreenPressed:</P><pre class="lia-code-sample language-javascript"><code>onFullScreenPressed: function() { try { this._fireSetLayout("MidColumnFullScreen"); this.getOwnerComponent().getModel("util").setProperty(`/ViewControl/Layout`, "MidColumnFullScreen"); return true; } catch (oError) { this.evaluateException(oError); return false; } },</code></pre><P>The test:</P><pre class="lia-code-sample language-javascript"><code>QUnit.test("BaseController - Check onFullScreenPressed Exception", async function(assert) { assert.notOk(await this.oBaseController.onFullScreenPressed(), "Check onFullScreenPressed Exception successful"); });</code></pre><H2 id="toc-hId--1422539606">2.5 Larger example with more branches</H2><P>A slightly larger example is the&nbsp;onCancel function, which is a bit more complex.&nbsp;</P><P>The function checks whether changes exist in the current draft.</P><P>Several paths (branches):<BR />Changes exist: the user is shown a dialog, where they can decide whether to discard the draft<BR />No changes: the draft is discarded<BR />Error case: an error message is shown</P><P>onCancel:</P><pre class="lia-code-sample language-javascript"><code>onCancel: async function(oEvent) { try { const oData = this.getView().getBindingContext().getObject(); //are there changes? if (oData.DraftEntityCreationDateTime.toString() !== oData.DraftEntityLastChangeDateTime.toString()) { const oSource = oEvent.getSource(); this._openDiscardDraftDialog(oSource); return true; } else { //no changes, so discard the draft return Promise.resolve(await this.onDiscardDraft()); } } catch (oError) { this.evaluateException(oError); return false; } },</code></pre><P>We will now test all paths to achieve full coverage.</P><P>1. onCancel with draft changes</P><pre class="lia-code-sample language-javascript"><code>/** * BaseController - Check onCancel (with draft changes) */ QUnit.test("BaseController - Check onCancel - with draft changes", async function(assert) { this.oBaseController.onDiscardDraft = sinon.stub().returns(Promise.resolve(true)); sinon.stub(this.oBaseController, "_openDiscardDraftDialog"); sinon.stub(this.oBaseController, "getView").returns({ getBindingContext: sinon.stub().returns({ getObject: sinon.stub().returns({ DraftEntityCreationDateTime: 1, DraftEntityLastChangeDateTime: 2 }) }) }); const oMockButton = new Button(); const oEvent = { getSource: function() { return oMockButton; } }; assert.ok(await this.oBaseController.onCancel(oEvent), "Check onCancel (with draft changes) successful"); });</code></pre><P>2. onCancel without changes</P><pre class="lia-code-sample language-javascript"><code>/** * BaseController - Check onCancel (no draft changes) */ QUnit.test("BaseController - Check onCancel - no changes", async function(assert) { this.oBaseController.onDiscardDraft = sinon.stub().returns(Promise.resolve(true)); sinon.stub(this.oBaseController, "getView").returns({ getBindingContext: sinon.stub().returns({ getObject: sinon.stub().returns({ DraftEntityCreationDateTime: 1, DraftEntityLastChangeDateTime: 1 }) }) }); assert.ok(await this.oBaseController.onCancel(), "Check onCancel (no draft changes) successful"); });</code></pre><P>3. onCancel error case</P><pre class="lia-code-sample language-javascript"><code>/** * BaseController - Check onCancel Exception */ QUnit.test("BaseController - Check onCancel Exception", async function(assert) { assert.notOk(await this.oBaseController.onCancel(), "Check onCancel Exception successful"); });</code></pre><P>When we now run the tests, we can see that we have covered all paths (branches):</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Citrix.DesktopViewer.App_89tf2RbxCT.png" style="width: 836px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359491iFD56956C4B3C4522/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Citrix.DesktopViewer.App_89tf2RbxCT.png" alt="Citrix.DesktopViewer.App_89tf2RbxCT.png" /></span></P><H1 id="toc-hId--1325650104">&nbsp;</H1><H1 id="toc-hId--1353979918">&nbsp;</H1><H1 id="toc-hId--1550493423">&nbsp;</H1><H1 id="toc-hId--1747006928">&nbsp;</H1><H1 id="toc-hId--1943520433">&nbsp;</H1><H1 id="toc-hId--2140033938">&nbsp;</H1><H1 id="toc-hId-1958419853">&nbsp;</H1><H1 id="toc-hId-1761906348">&nbsp;</H1><H1 id="toc-hId-1565392843">&nbsp;</H1><H1 id="toc-hId-1368879338">&nbsp;</H1><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H1 id="toc-hId-1172365833">3. Running tests and coverage</H1><P>Unit tests can be executed in different ways.<BR />In practice, running them via an npm script has proven to be the most convenient option.</P><H2 id="toc-hId-850633012">3.1 Running tests</H2><P>Unit tests can be started using a script defined in package.json:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="ead89be6-66ce-46de-ba9e-71ee5911ac1e.png" style="width: 652px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356300iBA999B7D4B6F2362/image-size/large?v=v2&amp;px=999" role="button" title="ead89be6-66ce-46de-ba9e-71ee5911ac1e.png" alt="ead89be6-66ce-46de-ba9e-71ee5911ac1e.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>The test run is then started using the following command:</P><pre class="lia-code-sample language-bash"><code>npm run unit-tests</code></pre><P>Once the tests are started, the browser opens and all tests (that are not commented out in AllTests.js) are executed automatically.</P><H2 id="toc-hId-654119507"><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="chrome_ESZgy2uDlt.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356302i73B0FA71475C46DE/image-size/large?v=v2&amp;px=999" role="button" title="chrome_ESZgy2uDlt.png" alt="chrome_ESZgy2uDlt.png" /></span></H2><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId-457606002">3.2 Test results in the QUnit interface</H2><P>After the tests have been executed, an overview is displayed in the browser showing which tests were successful an which were not.<BR />This is also indicated by colors and shown together with an error message.</P><P>Example:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Citrix.DesktopViewer.App_x1y8OK1r6d.png" style="width: 831px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359498i781293553B3CD7B4/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Citrix.DesktopViewer.App_x1y8OK1r6d.png" alt="Citrix.DesktopViewer.App_x1y8OK1r6d.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>Failed tests can also be inspected in the browser console.</P><P>&nbsp;</P><H2 id="toc-hId-261092497">3.3 Enabling code coverage</H2><P>Code coverage can be enabled using the checkbox "Enable coverage" in the QUnit toolbar.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="chrome_wCUCF5JgZH.png" style="width: 470px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356303i110ACAF488DA950F/image-size/large?v=v2&amp;px=999" role="button" title="chrome_wCUCF5JgZH.png" alt="chrome_wCUCF5JgZH.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P><BR />Once enabled, tests are re-run automatically and an interactive coverage overview is displayed below the test results.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="chrome_8BNiBACVoC.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356304i3EA63012E324B1B2/image-size/large?v=v2&amp;px=999" role="button" title="chrome_8BNiBACVoC.png" alt="chrome_8BNiBACVoC.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId-64578992">&nbsp;</H2><P>In the coverage overview, we can see the tested controllers and the information about the coverage itself.</P><P><STRONG>statements and lines<BR /></STRONG>Shows which lines of code were executed during the tests.<STRONG><BR /><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Citrix.DesktopViewer.App_0wXwB0uyW2.png" style="width: 502px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359505i8ABDD2D629EDBB9E/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Citrix.DesktopViewer.App_0wXwB0uyW2.png" alt="Citrix.DesktopViewer.App_0wXwB0uyW2.png" /></span></STRONG></P><P>&nbsp;</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Citrix.DesktopViewer.App_HBOPFpsmIf.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359506i81D36037A147CE78/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Citrix.DesktopViewer.App_HBOPFpsmIf.png" alt="Citrix.DesktopViewer.App_HBOPFpsmIf.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>functions</STRONG><BR />Indicates which functions were called at least once.</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Citrix.DesktopViewer.App_9BZibtM57b.png" style="width: 422px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359507iF07F249E02A0DC7C/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Citrix.DesktopViewer.App_9BZibtM57b.png" alt="Citrix.DesktopViewer.App_9BZibtM57b.png" /></span></STRONG></P><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>branches</STRONG><BR />Shows whether different execution paths (e.g. if/else, try/catch) were actually tested.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Citrix.DesktopViewer.App_ytqFExFz8B.png" style="width: 419px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359508i6554488C536D7CD1/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Citrix.DesktopViewer.App_ytqFExFz8B.png" alt="Citrix.DesktopViewer.App_ytqFExFz8B.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId--131934513">&nbsp;</H2><H2 id="toc-hId--328448018">3.4 Detailed view of individual files</H2><P>Clicking a file opens a detailed coverage view.</P><P><BR /><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="b133915d-24cb-42aa-bded-f293d2c098e4.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356305iF8CB479341F44936/image-size/large?v=v2&amp;px=999" role="button" title="b133915d-24cb-42aa-bded-f293d2c098e4.png" alt="b133915d-24cb-42aa-bded-f293d2c098e4.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>green lines</STRONG> were executed<BR /><STRONG>red lines</STRONG> were not covered</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="991f915c-863c-48bf-9760-32060dfd6041.png" style="width: 465px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356306iCF44FBC9E9C5CDD0/image-size/large?v=v2&amp;px=999" role="button" title="991f915c-863c-48bf-9760-32060dfd6041.png" alt="991f915c-863c-48bf-9760-32060dfd6041.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId--524961523">3.5 Coverage reports</H2><P>In addition to the browser view, a coverage report is generated under:<BR /><STRONG>tmp/coverage-reports/html/index.html</STRONG></P><P>Alternatevly, it can also be found in the IDE's project folder:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="1e4624f5-8474-419b-a54c-40e0d2aad0d7.png" style="width: 268px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/356307iDC7BB59732EA959A/image-size/large?v=v2&amp;px=999" role="button" title="1e4624f5-8474-419b-a54c-40e0d2aad0d7.png" alt="1e4624f5-8474-419b-a54c-40e0d2aad0d7.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>The report is updated automatically, can be opened in the browser independently.</P><H1 id="toc-hId--428072021">4. Conclusion</H1><P>With QUnit, we mainly focus on isolated controller logic and function behavior.&nbsp;<BR />Therefore keeping functions cleanly encapsulated and dependencies low during development leads to much easier unit tests</P><P>A note on coverage:<BR />I know that coverage percentages are often defined as targets.<BR />I have also seen that the temptation to push these numbers can be quite high by starting to test trivial functions (e.g. getters or setters). This usually backfires sooner or later.<BR />It makes much more sense to focus on testing critical logic, error cases and especially different paths and branches.</P> 2026-01-11T23:36:30.594000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/using-sap-ui5-amp-public-api-to-build-a-sudoku-game-a-learning-experiment/ba-p/14303259 Using SAP UI5 & Public API to Build a Sudoku Game – A Learning Experiment 2026-01-14T19:53:09.152000+01:00 Dhanasupriya https://community.sap.com/t5/user/viewprofilepage/user-id/146681 <P><STRONG>Application Approach</STRONG><BR />I followed the standard MVC structure in UI5.<BR />The view displays a 9×9 Sudoku grid using sap.m.Input controls, while the controller manages the API calls, timer logic, and input validation.<BR />Instead of introducing additional abstraction early on, I handled the Sudoku grid and game state directly inside the controller. Prefilled cells are rendered as non-editable, and empty cells accept user input.<BR /><STRONG>Integrating the Sudoku API</STRONG><BR />The Sudoku puzzle is fetched from a public API when the application starts or when the user chooses to begin a new game. The API response is processed and the values are assigned to the corresponding input fields in the grid.<BR />Initially, I noticed some UI flickering when updating values. This was caused by updating inputs multiple times inside a loop. After restructuring the logic to update each cell only once, the UI behavior became much smoother.<BR /><STRONG>Timer Implementation</STRONG><BR />To make the game more engaging, I added a simple timer. The timer starts when the puzzle is loaded. Managing the timer inside a UI5 application helped me understand how asynchronous logic works alongside UI events.<BR /><STRONG>Final Thoughts</STRONG><BR />This Sudoku game started as a small experiment but turned into a useful learning project. It showed me that SAP UI5 is flexible enough to handle interactive applications with real-time feedback.<BR />I plan to continue improving it by refining the validation logic and adding small usability improvements as I keep learning UI5.<BR /><STRONG>controller.js</STRONG></P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/m/Input", "sap/m/HBox", "sap/m/VBox", "sap/ui/core/HTML" ], (Controller, Input, HBox, VBox, HTML) =&gt; { "use strict"; return Controller.extend("com.sudokuapi.controller.App", { timer: null, seconds: 0, cells: [], onInit() { this.onNewGame(); }, /* =================== NEW GAME =================== */ onNewGame: function () { this.resetTimer(); this.startTimer(); fetch("https://sugoku.onrender.com/board?difficulty=easy") .then(r =&gt; r.json()) .then(d =&gt; this.buildGrid(d.board)); }, /* =================== TIMER =================== */ startTimer: function () { if (this.timer) clearInterval(this.timer); this.timer = setInterval(() =&gt; { this.seconds++; this.updateTimerText(); }, 1000); }, resetTimer: function () { this.seconds = 0; this.updateTimerText(); }, updateTimerText: function () { const m = String(Math.floor(this.seconds / 60)).padStart(2, "0"); const s = String(this.seconds % 60).padStart(2, "0"); this.byId("timerText").setText(`Time: ${m}:${s}`); }, /* =================== GRID =================== */ buildGrid: function (board) { const oBox = this.byId("sudokuGridInner") || document.getElementById("sudokuGridInner"); oBox.innerHTML = ""; // Clear old grid this.cells = []; for (let r = 0; r &lt; 9; r++) { const rowDiv = document.createElement("div"); rowDiv.className = "sudokuRow"; this.cells[r] = []; for (let c = 0; c &lt; 9; c++) { const isFixed = board[r][c] !== 0; const input = document.createElement("input"); input.type = "number"; input.maxLength = 1; input.value = isFixed ? board[r][c] : ""; input.disabled = isFixed; input.className = "sudokuCell"; input.dataset.row = r; input.dataset.col = c; // Live validation input.addEventListener("input", () =&gt; { this.validateCell(r, c); }); this.cells[r][c] = input; rowDiv.appendChild(input); } oBox.appendChild(rowDiv); } }, /* =================== VALIDATION =================== */ onCheckSolution: function () { for (let r = 0; r &lt; 9; r++) { for (let c = 0; c &lt; 9; c++) { this.validateCell(r, c, true); // force full check } } }, validateCell: function (row, col, force = false) { const input = this.cells[row][col]; if (!input || input.disabled) return; const val = parseInt(input.value, 10); let isValid = true; if (isNaN(val) || val &lt; 1 || val &gt; 9) { isValid = false; } else { // Check row for (let c = 0; c &lt; 9; c++) { if (c !== col &amp;&amp; parseInt(this.cells[row][c].value, 10) === val) { isValid = false; break; } } // Check column for (let r = 0; r &lt; 9; r++) { if (r !== row &amp;&amp; parseInt(this.cells[r][col].value, 10) === val) { isValid = false; break; } } // Check 3x3 box const startRow = Math.floor(row / 3) * 3; const startCol = Math.floor(col / 3) * 3; for (let r2 = startRow; r2 &lt; startRow + 3; r2++) { for (let c2 = startCol; c2 &lt; startCol + 3; c2++) { if ((r2 !== row || c2 !== col) &amp;&amp; parseInt(this.cells[r2][c2].value, 10) === val) { isValid = false; } } } } input.style.backgroundColor = isValid ? "white" : "lightcoral"; } }); });</code></pre><P><STRONG>view.xml</STRONG></P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="com.sudokuapi.controller.App" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m" xmlns:core="sap.ui.core"&gt; &lt;App&gt; &lt;pages&gt; &lt;Page title="Sudoku"&gt; &lt;content&gt; &lt;VBox alignItems="Center" width="100%"&gt; &lt;Text id="timerText" text="Time: 00:00" class="sapUiMediumMargin"/&gt; &lt;!-- Use escaped HTML inside core:HTML --&gt; &lt;core:HTML id="sudokuGrid" content="&amp;lt;div id='sudokuGridInner' class='sudokuGrid'&amp;gt;&amp;lt;/div&amp;gt;" /&gt; &lt;HBox justifyContent="Center" class="sapUiMediumMarginTop"&gt; &lt;Button text="New Game" press="onNewGame" /&gt; &lt;Button text="Check Solution" press="onCheckSolution" class="sapUiMediumMarginBegin"/&gt; &lt;/HBox&gt; &lt;/VBox&gt; &lt;/content&gt; &lt;/Page&gt; &lt;/pages&gt; &lt;/App&gt; &lt;/mvc:View&gt;</code></pre><P><STRONG>output</STRONG></P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="1.png" style="width: 526px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359216i3F1CF53CB18E7838/image-dimensions/526x540/is-moderation-mode/true?v=v2" width="526" height="540" role="button" title="1.png" alt="1.png" /></span></STRONG></P><P>Thank you!!</P> 2026-01-14T19:53:09.152000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472 SAP UI5 - Draft Handling in FreeStyle Applications 2026-01-14T23:43:12.458000+01:00 dreichert https://community.sap.com/t5/user/viewprofilepage/user-id/835133 <P><ul =""><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-1638455810">1. Creating a DraftManager Class</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-1571025024">1.1 Initialization</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-1374511519">1.2 Draft Functions in the DraftManager</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-1048915295">2. Instantiating the DraftManager in the Controller</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-852401790">3. Using the DraftManager in the Controller</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-784971004">3.1 Start Editing</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-588457499">3.2 Save</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-391943994">3.3 Cancel</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId-66347770">4. DraftHandling and MERGE lock errors</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-draft-handling-in-freestyle-applications/ba-p/14298472#toc-hId--130165735">5. Conclusion</a></li></ul></P><P>&nbsp;</P><H1 id="toc-hId-1638455810">1. Creating a DraftManager Class</H1><P>In this post, I would like to share some of my experience with draft handling in FreeStyle applications.<BR />After we decided in one of our projects to implement draft handling and at the same time had to consider our new component-based architecture, we encapsulated SAP’s DraftController (sap.ui.generic.app.transaction.DraftController) in a way that made sense for our use case.</P><P>I describe one possible encapsulation of the DraftController in a service class called "DraftManager", which (depending on the project) can be integrated and used across FreeStyle applications.<BR />This approach worked very well for us, especially since official documentation was quite limited at that time.</P><P><STRONG>Important</STRONG>: In our application we use an OData V2 service (with CDS views) that is already prepared accordingly for draft handling.</P><H2 id="toc-hId-1571025024">1.1 Initialization</H2><P>The DraftManager is a service class used to encapsulate the DraftController.<BR />The following basic structure has proven itself in our projects and can be used as is or adapted and extended as needed.</P><P>When initializing the DraftManager in the controller, we only pass the component instance and the view’s binding context.</P><P>(Example)</P><pre class="lia-code-sample language-javascript"><code>this.oDraftManager = new DraftManager(this.getOwnerComponent(), oContext);</code></pre><P>The binding context is stored internally within the class for further processing and can be accessed or modified if required using the helper functions getActiveContext and setActiveContext.</P><P>Basic structure:</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "sap/ui/generic/app/transaction/DraftController" ], function (DraftController) { "use strict"; const DraftManager = function (oComponent, oContext) { this.oModel = oComponent.getModel(); this.oDraftController = new DraftController(this.oModel); this.oActiveContext = oContext; this.oDraftContext = null; }; DraftManager.prototype.setActiveContext = function (oContext) { this.oActiveContext = oContext; }; DraftManager.prototype.getActiveContext = function () { return this.oActiveContext; }; return DraftManager; });</code></pre><P>&nbsp;</P><H2 id="toc-hId-1374511519">1.2 Draft Functions in the DraftManager</H2><P>The DraftManager can be extended as needed, but in this post I would like to focus in particular on the main draft handling functions (Activate, Edit, and Discard).</P><P><STRONG>Determine or Create a Draft</STRONG></P><P>The DraftController itself is relatively easy to use.<BR />However, there are operations, such as Edit, that always require multiple steps.<BR />If a draft already exists, a new one cannot be created and an error will occur.</P><P>To avoid having to implement the same check in every controller, this logic is encapsulated.<BR />This way, only a single function needs to be executed.</P><P>In this example code, we first check whether a draft exists for the active view context.<BR />If so, it is returned and the controller can rebind the view accordingly.<BR />If not, a new draft is created.<BR /><BR />If needed, success and error messages can also be handled inside the function (for example, if they should be consistent across several applications). We use our own separate error handling in this case.</P><pre class="lia-code-sample language-javascript"><code>DraftManager.prototype.getOrCreateEditDraft = async function () { const oExistingDraft = await this.oDraftController.getDraftForActiveEntity(this.oActiveContext); if (oExistingDraft &amp;&amp; !oExistingDraft.context.getObject().IsActiveEntity) { this.oDraftContext = oExistingDraft.context; return this.oDraftContext; } const oDraft = await this.oDraftController.createEditDraftEntity(this.oActiveContext); this.oDraftContext = oDraft.context; return this.oDraftContext; };</code></pre><P><STRONG>Activate a Draft</STRONG></P><P>Activating a draft is pretty straightforward in this case. Here as well: The function can be extended as needed (error + success handling e.g.)</P><pre class="lia-code-sample language-javascript"><code>DraftManager.prototype.activateDraft = async function () { await this.oDraftController.activateDraftEntity(this.oDraftContext); this.oActiveContext = this.oDraftContext; };</code></pre><P><STRONG>Discard a Draft</STRONG></P><P>Discarding a draft is also easy to implement in this case.</P><pre class="lia-code-sample language-javascript"><code>DraftManager.prototype.discardDraft = async function () { await this.oDraftController.discardDraft(this.oDraftContext); this.oDraftContext = null; };</code></pre><H1 id="toc-hId-1048915295">2. Instantiating the DraftManager in the Controller</H1><P>As mentioned at the beginning, the DraftManager can be instantiated quite easily.</P><P>Example: We have a master/detail application with an onRouteMatched function in the detail controller.<BR />Assume we have a SmartTable on the master page and navigate to the detail page after clicking on an entry.<BR />Here, we pass the corresponding bound id (or guid) of the list item and the HasActiveEntity property.</P><P>Using this properties, we now create a raw context (helper function _loadContext) and start instantiating the DraftManager .</P><pre class="lia-code-sample language-javascript"><code>onRouteMatched: async function (oEvent) { const sId = oEvent.getParameter("arguments").UUID; const bActive = oEvent.getParameter("arguments").HasActiveEntity; try { const oContext = await this._loadContext("/EntitySet", { "UUID": sId, "IsActiveEntity": bActive }); this.oDraftManager = new DraftManager(this.getOwnerComponent(), oContext); } catch (oError) { //error handling } }</code></pre><pre class="lia-code-sample language-javascript"><code>_loadContext: async function (sEntity, oContextParameters) { const oModel = this.getView().getModel(); await oModel.metadataLoaded(); const sKey = oModel.createKey(sEntity, oContextParameters); const oContext = new sap.ui.model.Context(oModel, sKey); return Promise.resolve(oContext); },</code></pre><P>After that, the DraftManager is instantiated and can be used.</P><H1 id="toc-hId-852401790">3. Using the DraftManager in the Controller</H1><P>From this point on things become much more relaxed, as encapsulating the draft logic significantly reduces the number of operations in the controller.</P><P>I will keep the following examples to a minimum. Depending on the requirements, these can of course become more complex.</P><P>For rebinding/refreshing the view, we're using the following helper function:</P><pre class="lia-code-sample language-javascript"><code>_bindView: async function (sId, bIsActiveEntity) { return new Promise((resolve) =&gt; { this.getView().bindElement({ path: `/EntitySet(UUID=guid'${sId}',IsActiveEntity=${bIsActiveEntity})`, parameters: { expand: "DraftAdministrativeData" }, events: { change: (oEvent) =&gt; { resolve(oEvent.getSource().getBoundContext()); } } }); }); };</code></pre><H2 id="toc-hId-784971004">3.1 Start Editing</H2><P>Editing has now become a fire-and-forget function, as everything is nicely encapsulated.<BR />After each draft operation, the view should be updated (in this case using _bindView).</P><pre class="lia-code-sample language-javascript"><code>onEdit: async function () { try { const oDraftContext = await this.oDraftManager.getOrCreateEditDraft(); await this._bindView( oDraftContext.getProperty("UUID"), oDraftContext.getProperty("IsActiveEntity") ); } catch (oError) { //error handling... } }</code></pre><H2 id="toc-hId-588457499">3.2 Save</H2><P>After activating the draft, the view is rebound to the active entity.</P><pre class="lia-code-sample language-javascript"><code>onSave: async function () { try { await this.oDraftManager.activateDraft(); await this._bindView( this.oDraftManager.getActiveContext().getProperty("UUID"), this.oDraftManager.getActiveContext().getProperty("IsActiveEntity") ); } catch (oError) { //error handling... } }</code></pre><H2 id="toc-hId-391943994">3.3 Cancel</H2><P>When canceling, the draft is discarded and the view is rebound to the active context.</P><pre class="lia-code-sample language-javascript"><code>onDiscard: async function () { try { await this.oDraftManager.discardDraft(); await this._bindView( this.oDraftManager.getActiveContext().getProperty("UUID"), this.oDraftManager.getActiveContext().getProperty("IsActiveEntity") ); } catch (oError) { //error handling... } }</code></pre><H1 id="toc-hId-66347770">4. DraftHandling and MERGE lock errors</H1><P>One important learning from our projects was dealing with MERGE lock errors.<BR />As soon as draft handling is applied to larger views with many form fields and especially rich text editors, you will sooner or later run into locking issues, as we did.</P><P>This happens because the DraftController triggers a MERGE request on every change in the view (which is correct behavior).<BR />However, if <STRONG>too many changes</STRONG> are <STRONG>performed too quickly</STRONG>, multiple MERGE requests can be sent at the same time and end up locking each other.</P><P>This can easily be reproduced with a rich text editor in the view by pressing the Enter key very quickly. The result is a large number of lock errors (The draft itself is still updated correctly, but this can become quite annoying when working with messaging).</P><P>Our solution for this was to work with <STRONG>deferredGroups</STRONG> and <STRONG>changeGroups</STRONG>.</P><P>In order to get this to work with the DraftController and the service class, we use the merge timer provided by SAP.</P><P>First we start by setting the deferredGroup and changeGroup in the onInit function of the view controller.</P><pre class="lia-code-sample language-javascript"><code>this.getOwnerComponent().getModel().setDeferredGroups(["Changes"]); this.getOwnerComponent().getModel().setChangeGroups({ "*": { groupId: "Changes", changeSetId: "Changes", single: false } }); this.getOwnerComponent().getModel().attachPropertyChange(this.onEnqueueSubmitChanges.bind(this, 1));</code></pre><P>After that we define the propertyChange handler function onEnqueueSubmitChanges.</P><pre class="lia-code-sample language-javascript"><code>onEnqueueSubmitChanges: function (nIntervalInSeconds) { const mParameters = this.getOwnerComponent().getModel("util").getProperty("/ViewControl/batchParameters"); this.oDraftManager.triggerSubmitChanges(nIntervalInSeconds, mParameters); }</code></pre><P>And finally, we define a new DraftManager function triggerSubmitChanges, which bundles the requests using the merge timer and submits them accordingly.</P><pre class="lia-code-sample language-javascript"><code>oDraftManager.prototype.triggerSubmitChanges = function (nIntervalInSeconds, mParameters) { const oDraftMergeTimer = this.oDraftController._oDraftMergeTimer; if (!oDraftMergeTimer.nTimeoutID) { oDraftMergeTimer.nTimeoutID = setTimeout(() =&gt; { oDraftMergeTimer.nTimeoutID = null; if (this.oModel.hasPendingChanges()) { this.oDraftController.triggerSubmitChanges(mParameters) .catch((oError) =&gt; { console.error("Error during submit changes:", oError); }); } }, (nIntervalInSeconds || 0) * 1000); } };</code></pre><H1 id="toc-hId--130165735">5. Conclusion</H1><P>I have reduced the examples to the basics, as they can be extended as needed. We had very good experience with this service class, and draft handling in FreeStyle applications in combination with an OData V2 service worked very well.</P><P>The DraftController can of course also be used directly in the controller.<BR />However, as mentioned before, encapsulating it is worthwhile when working with a larger application architecture, especially when testing is relevant in the project.</P> 2026-01-14T23:43:12.458000+01:00 https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/sap-fiori-development-newsletter-january-2026-issue-38/ba-p/14304753 SAP Fiori Development newsletter January 2026 (issue #38) 2026-01-15T15:50:00.034000+01:00 PeterSpielvogel https://community.sap.com/t5/user/viewprofilepage/user-id/543 <P>Happy New Year. If 2025 was the year of AI, what will 2026 bring? Likely more AI.</P><P>Some predictions:</P><OL><LI>Vibe coding will become more mainstream with MCP servers and LLMs working together to deliver more predictable and consistent results.</LI><LI>We will start to see the emergence of the “post-app” era in enterprise software. I don’t think it will be entirely chat-based, but I do expect some parts to be generated on demand.</LI><LI>The knowledge gap between developers who use AI and those who don’t will continue to grow. Now, the early adopters have a 1-year head start. For those who choose to remain on the sidelines, that will continue to grow. Don’t be that person – you need to start experimenting with AI, even if you don’t include the results in your production code</LI></OL><P>What are your predictions for 2026? Let us know or add to the comments below. If you want to discuss your thoughts with your fellow developers, I encourage you to join our monthly SAP Fiori development roundtable. <A href="https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fdocs.google.com%2Fforms%2Fd%2F1ZqIX3zzGBOOIqehmlIRz_HhpQEhqunkLSzN1gnaOwP8%2Fedit&amp;data=05%7C02%7Cpeter.spielvogel%40sap.com%7C1744854ba1f649092b6b08dd30c5a09e%7C42f7676cf455423c82f6dc2d99791af7%7C0%7C0%7C638720347573866887%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&amp;sdata=xgQxtmNy3e97xOOJC304tGHhsgIVLId5KJSRNyshxq0%3D&amp;reserved=0" target="_blank" rel="noopener nofollow noreferrer">Register online</A> or email me for an invitation.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SAP_Fiori_Dev_Newsletter_38.png" style="width: 661px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361101i139D54B36689BF1D/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="SAP_Fiori_Dev_Newsletter_38.png" alt="SAP_Fiori_Dev_Newsletter_38.png" /></span></P><H2 id="toc-hId-1787739649">Development News</H2><P><STRONG>The complete 6-part SAP Fiori Development Portal blog series<BR /></STRONG>In this 6-part series, Katrin Polloczek and Marcel Waechter explore the SAP Fiori development portal and how to use it. Topics include SAP Fiori elements, building blocks, extension options, custom pages, and how all these pieces fit together. <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/introducing-the-sap-fiori-development-portal-your-gateway-to-rapid-sap/ba-p/14236768" target="_blank">Read part 1 – with links to all the posts</A>.</P><P><STRONG>SAP Fiori tools 2601 update – Mobile Device Preview, Building Blocks, and AI-Driven Productivity<BR /></STRONG>SAP Fiori tools 2601 includes enhancements for previewing applications, managing system connections, building SAP Fiori elements–based UIs, and accelerating development with AI-powered tooling. In parallel, we continue to refine the fundamentals in security, modeling, and extensibility. <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-fiori-tools-2601-update-mobile-device-preview-building-blocks-and-ai/ba-p/14292653" target="_blank">See details</A>.</P><P><STRONG>Joule in SAP Build Code can now create freestyle UI5 apps<BR /></STRONG>Last year, we introduced the SAPUI5 extension for the Joule coding assistant in SAP Build Code Over time, it has evolved to support conversion of UI5 apps to TypeScript and setting up new pages and the respective navigation. The latest improvement lets you create new freestyle UI5 apps according to your requirements, bringing the functionality right to developers using SAP Build Code—no other tools needed. <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/joule-in-sap-build-code-can-now-create-freestyle-ui5-apps/ba-p/14264445" target="_blank">Discover how it works</A>.</P><P><STRONG>Inside Joule: Lessons from building an AI coding assistant for SAP Build<BR /></STRONG>This is a&nbsp;<EM>"making of"</EM>&nbsp;article about the implementation of the new&nbsp;/ui5-create-app&nbsp;slash command available within Joule in SAP Build Code. This new feature enables a conversation between an application developer and their AI assistant about building a new freestyle UI5 application. <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/inside-joule-lessons-from-building-an-ai-coding-assistant-for-sap-build/ba-p/14264447" target="_blank">Learn more</A>.</P><P><STRONG>UI5 MCP Server's New TypeScript Migration Feature<BR /></STRONG><SPAN>The new UI5 MCP server will give you a massive head start on converting your existing JavaScript UI5 apps to TypeScript. A multi-day manual conversion becomes an hour of agent work plus several hours of polishing. The time savings add up with each project. </SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/ui5-mcp-server-s-new-typescript-migration-feature-real-world-experience-and/ba-p/14289451" target="_blank">Learn more</A><SPAN>.</SPAN></P><P><STRONG>Harmonizing UI5 URLs: Convenient URLs for SAPUI5/OpenUI5 CDN<BR /></STRONG>When accessing the UI5 Demo Kit and when using UI5 in cloud scenarios, you use URLs like <A href="https://ui5.sap.com/" target="_blank" rel="noopener noreferrer">https://ui5.sap.com/</A>. We are harmonizing these URLs to make them future-proof. <A href="https://community.sap.com/t5/frontend-ui5-sap-fiori-blog-posts/harmonizing-ui5-urls-convenient-urls-for-sapui5-openui5-cdn/ba-p/14280931" target="_blank">See more details</A>.</P><H2 id="toc-hId-1591226144">Events</H2><P><STRONG>SAP Fiori Innovation Day</STRONG> 2025 is a wrap. The final event in Walldorf included an overview of AI capabilities in SAP Cloud ERP, two hands-on exercises, and some great insights from the sponsoring partners. <A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/sap-fiori-innovation-day-in-walldorf-featured-two-hands-on-sessions-plus-ai/ba-p/14289329" target="_blank">Learn the details</A>.</P><P>We are planning more <STRONG>SAP Fiori Innovation Days in 2026</STRONG>. Watch your inbox for announcements about where and when we will be hosting these events.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-right" image-alt="Call for papers is open for SAP Sapphire 2026" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/359936i80BAB6AAFE7338AF/image-size/small/is-moderation-mode/true?v=v2&amp;px=200" role="button" title="sapphire.jpg" alt="Call for papers is open for SAP Sapphire 2026" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Call for papers is open for SAP Sapphire 2026</span></span>Call for Proposals open for 2026 <STRONG>SAP Sapphire &amp; ASUG Annual Conference Orlando.<BR /></STRONG>Share your success story or lead an interactive discussion with your peers.&nbsp;<BR /><A href="https://www.cvent.com/c/abstracts/1bf726e9-95be-452e-8ae4-23e9b65846f7" target="_blank" rel="noopener nofollow noreferrer">Submit your proposal</A>.</P><H2 id="toc-hId-1311650375" id="toc-hId-1394712639"><STRONG>Back issues from the past year</STRONG></H2><P><A class="" href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/sap-fiori-development-newsletter-november-2025-issue-37/ba-p/14268465" target="_blank">SAP Fiori development newsletter November 2025 (issue #37)</A></P><P><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/sap-fiori-development-newsletter-september2025-issue-36/ba-p/14212556" target="_self">SAP Fiori development newsletter September 2025 (issue #36)</A></P><P><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/sap-fiori-development-newsletter-july-2025-issue-35/ba-p/14139032" target="_self">SAP Fiori development newsletter July 2025 (issue #35)</A></P><P><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/sap-fiori-development-newsletter-may-2025-issue-34/ba-p/14091410" target="_self">SAP FIori development newsletter May 2025 (Issue #34)</A></P><P><A href="https://community.sap.com/t5/enterprise-resource-planning-blogs-by-sap/sap-fiori-development-newsletter-march-2025-issue-33/ba-p/14036207" target="_self">SAP Fiori development newsletter March 2025 (Issue #33)</A></P><P><A href="https://community.sap.com/t5/enterprise-resource-planning-blogs-by-sap/sap-fiori-development-newsletter-january-2025-issue-32/ba-p/13984589" target="_self">SAP Fiori development newsletter January 2025 (issue #32)</A></P> 2026-01-15T15:50:00.034000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/code-connect-2026-is-coming-mark-your-calendars/ba-p/14307923 Code Connect 2026 is Coming – Mark Your Calendars! 2026-01-16T10:17:39.514000+01:00 BirgitS https://community.sap.com/t5/user/viewprofilepage/user-id/41902 <P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Code Connect logo" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361588i1404B9EF84CF278E/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="CodeConnectBanner.png" alt="CodeConnectBanner.png" /></span></SPAN></P><P>&nbsp;</P><P><SPAN>We’re excited to announce the return of&nbsp;<A href="https://code-connect.dev/" target="_blank" rel="noopener nofollow noreferrer"><STRONG>Code Connect</STRONG>&nbsp;</A>for its third edition, bringing together three dynamic events - <STRONG>UI5con, reCAP, and HANA Tech Con</STRONG> - under one roof. Mark your calendars for&nbsp;<STRONG>July 13 to 16, 2026</STRONG>, and join us in&nbsp;<STRONG>St. Leon-Rot, Germany</STRONG>, or online.</SPAN></P><P>&nbsp;</P><H2 id="toc-hId-1787830851"><SPAN>What is Code Connect?</SPAN></H2><P><SPAN>Code Connect creates a unique opportunity to experience three specialized events in one location: <A href="https://openui5.org/ui5con/" target="_blank" rel="noopener nofollow noreferrer">UI5con</A>, <A href="https://recap-conf.dev/" target="_blank" rel="noopener nofollow noreferrer">reCAP</A>, and <A href="https://hanatech.community/" target="_blank" rel="noopener nofollow noreferrer">HANA Tech Con</A>, allowing you to dive deep into different aspects of SAP development. Code Connect is designed for developers at every level: Whether you're an SAP veteran or just starting out, this is your chance to&nbsp;connect, learn, and innovate&nbsp;alongside a vibrant community of developers.</SPAN></P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Collage of photos from past Code Connect events" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361582i126BE1733C3ACA09/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Collage.png" alt="Collage.png" /></span></SPAN></P><P>&nbsp;</P><H3 id="toc-hId-1720400065">Your Week at Code Connect</H3><H3 id="toc-hId-1523886560"><SPAN>July 13: Code Jam Sessions and Warmup</SPAN></H3><P><SPAN>Kick things off with our&nbsp;Code Jam sessions - a hands-on way to sharpen your skills before the main event. Afterward, join us for a&nbsp;pre-conference networking event&nbsp;to meet fellow attendees in a relaxed setting.</SPAN></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Logo UI5con" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361598i6723ED463E741644/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="UI5con2.png" alt="UI5con2.png" /></span></P><H3 id="toc-hId-1327373055">&nbsp;</H3><H3 id="toc-hId-1130859550">&nbsp;</H3><H3 id="toc-hId-934346045"><SPAN>July 14: UI5con </SPAN></H3><P>The official program kicks off with UI5con, bringing together UI5 enthusiasts to share insights, explore the latest innovations, and build new connections. Expect expert sessions, interactive workshops, and plenty of opportunities to engage with the UI5 community.</P><P><SPAN><A href="https://openui5.org/ui5con/" target="_blank" rel="noopener nofollow noreferrer">Learn more about UI5con</A>.</SPAN></P><P>&nbsp;</P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Logo reCAP" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361611i7856787EDD6734D2/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="reCAP_3.png" alt="reCAP_3.png" /></span></SPAN></P><H5 id="toc-hId-995997978">&nbsp;</H5><H5 id="toc-hId-799484473">&nbsp;</H5><H5 id="toc-hId-602970968">&nbsp;</H5><H5 id="toc-hId-406457463">&nbsp;</H5><H3 id="toc-hId--123452849"><SPAN>July 15: reCAP</SPAN></H3><P>The next day focuses on the SAP Cloud Application Programming Model (CAP). At reCAP, developers, customers, and partners meet the CAP Product Team to discuss technical concepts, share project experiences, and explore future possibilities.</P><P><SPAN><A href="https://recap-conf.dev/" target="_blank" rel="noopener nofollow noreferrer">Learn more about reCAP</A>.</SPAN></P><P>&nbsp;</P><P><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Logo HANA Tech Con" style="width: 150px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361578iD47004A3E8E22283/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="HANA_tech_con.png" alt="HANA_tech_con.png" /></span></SPAN></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H3 id="toc-hId--319966354">&nbsp;</H3><H3 id="toc-hId--516479859">July 16: HANA Tech Con</H3><P>The week concludes with HANA Tech Con. Delve into the HANA universe and join development experts, users, and partners to exchange knowledge and ignite new ideas. If you've ever had questions about HANA that you haven't found answers to, this is your chance to get them resolved.</P><P><SPAN><A href="https://hanatech.community/" target="_blank" rel="noopener nofollow noreferrer">Learn more about HANA Tech Con</A>.</SPAN></P><P><SPAN>&nbsp;</SPAN></P><H2 id="toc-hId--419590357">Early Bird Process</H2><P><SPAN>Planning a longer trip? We offer a limited number of early bird tickets for attendees who need to arrange travel well in advance. </SPAN></P><P>Check our <SPAN><A href="https://code-connect.dev/faq.html" target="_blank" rel="noopener nofollow noreferrer">FAQ document</A></SPAN> for details on how to secure your ticket.</P><P>&nbsp;</P><H2 id="toc-hId--616103862"><SPAN>Sponsorship Opportunities</SPAN></H2><P>Be a part of Code Connect 2026 and become a sponsor to support the developer community at UI5con, reCAP, and HANA Tech Con. By sponsoring Code Connect, you gain access to a diverse audience spanning front-end developers, backend specialists, and database experts – all in one event.</P><P><SPAN>Read our <A href="https://cap.cloud.sap/resources/events/Code_Connect_2026_Sponsor_Packages.pdf" target="_blank" rel="noopener nofollow noreferrer">sponsorship prospectus</A> to check our sponsorship opportunities.</SPAN></P><P>&nbsp;</P><H2 id="toc-hId--812617367"><SPAN>Call for Proposals</SPAN></H2><P>Our Call for Speakers runs from 26 January to 13 March. As a speaker, you’ll be an active part of Code Connect, shaping the conversation, sharing your expertise, and inspiring the developer community. Don’t wait and submit your session proposal by 13 March at the latest.</P><UL><LI><SPAN><A href="https://ui5con.cfapps.eu12.hana.ondemand.com/" target="_blank" rel="noopener nofollow noreferrer">Call for proposals UI5con</A></SPAN></LI><LI><SPAN><A href="https://recap.cfapps.eu12.hana.ondemand.com/" target="_blank" rel="noopener nofollow noreferrer">Call for proposals reCAP</A></SPAN></LI><LI><SPAN><A href="https://hanatech.cfapps.eu12.hana.ondemand.com/" target="_blank" rel="noopener nofollow noreferrer">Call for proposals HANA Tech Con</A></SPAN></LI></UL><H2 id="toc-hId--1009130872">&nbsp;</H2><H2 id="toc-hId--1205644377"><SPAN>Important Dates</SPAN></H2><UL><LI><SPAN>Call for Proposals: January 26, 2026 to March 13, 2026.</SPAN></LI><LI><SPAN>Registration Opens: April 10, 2026</SPAN></LI><LI><SPAN>Agenda Published: Early June 2026</SPAN></LI><LI><SPAN>Code Connect Week: July 13 to 16, 2026</SPAN></LI></UL><P><SPAN>&nbsp;</SPAN></P><H2 id="toc-hId--1402157882"><SPAN>Ready to Connect?</SPAN></H2><P><SPAN><A href="https://code-connect.dev/" target="_blank" rel="noopener nofollow noreferrer">Code Connect 2026</A> represents more than just learning opportunities. It's about building relationships, sharing knowledge, and being part of a community that's shaping the future of SAP development.</SPAN></P><P><SPAN>Join us at Code Connect 2026 and be part of a community driving the future of technology. We can't wait to see you there!</SPAN></P><P>&nbsp;</P> 2026-01-16T10:17:39.514000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/simplify-your-test-setup-introducing-the-test-starter-concept-for-your-ui5/ba-p/14303076 Simplify Your Test Setup: Introducing the Test Starter Concept for Your UI5 Projects 2026-01-19T10:17:31.056000+01:00 FlorianVogt https://community.sap.com/t5/user/viewprofilepage/user-id/216201 <P><SPAN class=""><SPAN class="">With UI5<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">v</SPAN><SPAN class="">ers</SPAN><SPAN class="">ion<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">1.12</SPAN><SPAN class="">4 (</SPAN></SPAN><SPAN class=""><EM><SPAN class="">Note:&nbsp;</SPAN><SPAN class="">A&nbsp;</SPAN><SPAN class="">d</SPAN><SPAN class="">ownport</SPAN><SPAN class="">&nbsp;for UI5 1.120&nbsp;</SPAN><SPAN class="">is&nbsp;</SPAN><SPAN class="">available with</SPAN></EM><SPAN class=""><EM>&nbsp;1.120.24</EM>)</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">the test starter concept<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">i</SPAN><SPAN class="">s officially enabled for UI5 application projects.<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">Previously, this</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>concept was<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">mainly used</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>in UI5<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">framework<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">librar</SPAN><SPAN class="">ies</SPAN><SPAN class="">. This<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">guide explains</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">how to migrate<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">an application project’s</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>test setup<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">to</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>a<SPAN>&nbsp;</SPAN></SPAN></SPAN><A class="" href="https://ui5.sap.com/#/topic/032be2cb2e1d4115af20862673bedcdb" target="_blank" rel="noreferrer noopener"><SPAN class=""><SPAN class="">test<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">starter</SPAN><SPAN class="">-</SPAN><SPAN class="">based</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>setup</SPAN></SPAN></A><SPAN class=""><SPAN class="">.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></P><P><SPAN>In the&nbsp;previous&nbsp;setup,&nbsp;the central entry point&nbsp;for running&nbsp;tests&nbsp;is&nbsp;</SPAN><STRONG><SPAN>webapp/test/testsuite.qunit.html,&nbsp;</SPAN></STRONG><SPAN>which usually references&nbsp;</SPAN><STRONG><SPAN>testsuite.qunit.js</SPAN></STRONG><SPAN>&nbsp;located&nbsp;next to&nbsp;it.&nbsp;Test runners like UI5 Test Runner or&nbsp;WebdriverIO&nbsp;QUnit&nbsp;Service&nbsp;also request both files.</SPAN><SPAN>&nbsp;</SPAN></P><H2 id="toc-hId-1787703196"><SPAN><SPAN class=""><SPAN class="">webapp/test/testsuite.qunit.html</SPAN></SPAN></SPAN></H2><P><SPAN>To&nbsp;migrate&nbsp;to the new setup,&nbsp;replace&nbsp;the two script tags with one new script tag.&nbsp;This tag&nbsp;loads&nbsp;the sap/ui/test/starter/createSuite&nbsp;module, references&nbsp;the testsuite.qunit.js file,&nbsp;and configures&nbsp;the test namespace. This module&nbsp;creates&nbsp;the actual test suite&nbsp;based&nbsp;on the configuration&nbsp;in&nbsp;the&nbsp;</SPAN><STRONG><SPAN>testsuite.qunit.js</SPAN></STRONG><SPAN>&nbsp;file.</SPAN><SPAN>&nbsp;</SPAN></P><H3 id="toc-hId-1720272410"><SPAN><SPAN class=""><SPAN class="">Old</SPAN></SPAN></SPAN></H3><pre class="lia-code-sample language-markup"><code>&lt;!doctype html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;QUnit test suite for the UI5 Application: ui5.myapp&lt;/title&gt; &lt;script src="../resources/sap/ui/qunit/qunit-redirect.js"&gt;&lt;/script&gt; &lt;script src="testsuite.qunit.js" data-sap-ui-testsuite&gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt;&lt;/body&gt; &lt;/html&gt; </code></pre><H3 id="toc-hId-1523758905"><SPAN><SPAN class=""><SPAN class="">New</SPAN></SPAN></SPAN></H3><pre class="lia-code-sample language-markup"><code>&lt;!doctype html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;QUnit test suite for the UI5 Application: ui5.myapp&lt;/title&gt; &lt;!-- Adjust the paths in the following lines to reflect your component namespace --&gt; &lt;script src="../resources/sap/ui/test/starter/createSuite.js" data-sap-ui-testsuite="test-resources/ui5/myapp/testsuite.qunit" data-sap-ui-resource-roots='{ "test-resources.ui5.myapp": "./" }' &gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt;&lt;/body&gt; &lt;/html&gt; </code></pre><P>&nbsp;</P><H2 id="toc-hId-1198162681"><SPAN><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class="">webapp/test/testsuite.qunit.</SPAN><SPAN class="">js</SPAN></SPAN></SPAN></SPAN></SPAN></H2><P><SPAN class=""><SPAN class="">In the<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">pre</SPAN><SPAN class="">vious</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>setup, the<SPAN>&nbsp;</SPAN></SPAN></SPAN><STRONG><SPAN class=""><SPAN class="">testsuite.qunit.js</SPAN></SPAN></STRONG><SPAN class=""><SPAN class=""><SPAN>&nbsp;</SPAN>file<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">regist</SPAN><SPAN class="">ered</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">the<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">relevant test pages</SPAN><SPAN class="">. This<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">remains</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>true<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">in</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the new<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">setup</SPAN><SPAN class="">,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">but</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">nu</SPAN><SPAN class="">m</SPAN><SPAN class="">ber</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">of</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>conf</SPAN><SPAN class="">ig</SPAN><SPAN class="">uration</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>options<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">ha</SPAN><SPAN class="">s</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>increased<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">significantl</SPAN><SPAN class="">y</SPAN><SPAN class="">,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>and the<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">file<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">structure</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">has</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>ch</SPAN><SPAN class="">anged.<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">In the new setup,<SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">testsuite.qunit.js</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">must</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>return an object<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">repre</SPAN><SPAN class="">se</SPAN><SPAN class="">nt</SPAN><SPAN class="">ing</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">the<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">test suite</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>configuration.</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">We describe the available</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>configuration options<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">in detail</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">in<SPAN>&nbsp;</SPAN></SPAN></SPAN><A class="" href="https://ui5.sap.com/#/topic/738ed025b36e484fa99046d0f80552fd" target="_blank" rel="noreferrer noopener"><SPAN class=""><SPAN class="">UI5 Test Starter –<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">Configuration</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">Op</SPAN><SPAN class="">tions</SPAN></SPAN></A><SPAN class=""><SPAN class="">.<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">Unlike the<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">previous</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">setup</SPAN><SPAN class="">,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the configuration<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">does</SPAN><SPAN class="">n’</SPAN><SPAN class="">t</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>reference HTML</SPAN><SPAN class="">-</SPAN><SPAN class="">based test<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">pages</SPAN><SPAN class="">.</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">Instead,<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">it points to<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">test pages written in JavaScript or TypeScript.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></P><H3 id="toc-hId-1130731895"><SPAN><SPAN class=""><SPAN class="">Old</SPAN></SPAN></SPAN></H3><pre class="lia-code-sample language-javascript"><code>window.suite = function () { var suite = new parent.jsUnitTestSuite(); var aParts = location.pathname.match(/(.*\/)(?:[^/]+)/); var sContextPath = aParts &amp;&amp; aParts[1]; suite.addTestPage(sContextPath + "unit/unitTests.qunit.html"); suite.addTestPage(sContextPath + "integration/opaTests.qunit.html"); return suite; }; </code></pre><H3 id="toc-hId-934218390"><SPAN class=""><SPAN class="">New</SPAN></SPAN></H3><pre class="lia-code-sample language-javascript"><code>sap.ui.define(function () { "use strict"; return { name: "QUnit test suite for the UI5 Application: ui5.myapp", defaults: { // Adjust the path in the next line to reflect your component namespace page: "ui5://test-resources/ui5/myapp/Test.qunit.html?testsuite={suite}&amp;test={name}", qunit: { version: 2 }, sinon: { version: 1 }, ui5: { language: "EN", theme: "sap_horizon" }, coverage: { // Adjust the path in the next line to reflect your component namespace only: "ui5/myapp/", // Adjust the path in the next line to reflect your component namespace never: "test-resources/ui5/myapp/" }, loader: { paths: { // Adjust the path in the next line to reflect your component namespace "ui5/myapp": "../" } } }, tests: { "unit/unitTests": { title: "Unit tests for ui5.myapp" }, "integration/opaTests": { title: "Integration tests for ui5.myapp" } } }; });</code></pre><P>&nbsp;</P><H2 id="toc-hId-608622166"><SPAN class=""><SPAN class=""><SPAN class="">webapp/test/</SPAN><SPAN class="">unit/unitTests</SPAN><SPAN class="">.qunit.</SPAN><SPAN class="">js</SPAN></SPAN></SPAN></H2><P><SPAN class=""><SPAN class="">Previously</SPAN><SPAN class="">, the test pages<SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">unitTests.qunit.js<SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">and<SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">opaTests.qunit.js</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">registered a function to be executed after<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">UI5<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">booted, load</SPAN><SPAN class="">ed</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>all test files (</SPAN><SPAN class="">QUnit</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>or OPA5</SPAN><SPAN class="">)<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">,</SPAN><SPAN class="">and</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>start</SPAN><SPAN class="">ed</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the test execution.<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">T</SPAN><SPAN class="">he new test starter concept simplifies<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">these file</SPAN><SPAN class="">s</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">significant</SPAN><SPAN class="">ly</SPAN><SPAN class="">. The</SPAN><SPAN class="">ir</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>only task</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">is to require all relevant test pages.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></P><H3 id="toc-hId-541191380"><SPAN class="">Old</SPAN></H3><pre class="lia-code-sample language-javascript"><code>QUnit.config.autostart = false; sap.ui.getCore().attachInit(function () { "use strict"; sap.ui.require(["unit/controller/Main.qunit"], function () { QUnit.start(); }); });</code></pre><H3 id="toc-hId-344677875"><SPAN class="">New</SPAN></H3><pre class="lia-code-sample language-javascript"><code>sap.ui.define(["./controller/Main.qunit"]);</code></pre><P>&nbsp;</P><H2 id="toc-hId-19081651"><SPAN class=""><SPAN class=""><SPAN class="">webapp/test/</SPAN><SPAN class="">integration/</SPAN><SPAN class="">opaTests</SPAN><SPAN class="">.qunit.</SPAN><SPAN class="">js</SPAN></SPAN></SPAN></H2><H3 id="toc-hId--123580504"><SPAN class="">Old</SPAN></H3><pre class="lia-code-sample language-javascript"><code>QUnit.config.autostart = false; sap.ui.getCore().attachInit(function () { "use strict"; sap.ui.require(["integration/HelloJourney"], function () { QUnit.start(); }); }); </code></pre><H3 id="toc-hId--320094009"><SPAN class="">New</SPAN></H3><pre class="lia-code-sample language-javascript"><code>sap.ui.define(["./HelloJourney"]);</code></pre><P>&nbsp;</P><H2 id="toc-hId--223204507"><SPAN class=""><SPAN class=""><SPAN class="">webapp/test/unit/unitTests.qunit.html&nbsp;</SPAN>and<SPAN class="">&nbsp;webapp/test/integration/opaTests.qunit.html</SPAN></SPAN></SPAN></H2><P><SPAN class=""><SPAN class="">You</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>can<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">now<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">delet</SPAN><SPAN class="">e</SPAN><STRONG><SPAN class="">&nbsp;</SPAN></STRONG></SPAN><SPAN class=""><STRONG><SPAN class="">webapp/test/</SPAN><SPAN class="">unit/unitTests</SPAN><SPAN class="">.qunit.</SPAN></STRONG><SPAN class=""><STRONG>html</STRONG><SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">and</SPAN></SPAN><SPAN class=""><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><STRONG><SPAN class="">webapp/test/</SPAN><SPAN class="">integration/opaTests</SPAN><SPAN class="">.qunit.</SPAN><SPAN class="">html</SPAN></STRONG></SPAN><SPAN class=""><SPAN class="">, as they</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>are no longer<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">need</SPAN><SPAN class="">ed</SPAN><SPAN class="">.</SPAN></SPAN></P><P>&nbsp;</P><H2 id="toc-hId--419718012"><SPAN class=""><SPAN class=""><SPAN class=""><STRONG><SPAN class="">webapp/test/</SPAN><SPAN class="">Test</SPAN></STRONG><SPAN class=""><STRONG>.qunit.html</STRONG></SPAN></SPAN></SPAN></SPAN></H2><P><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class="">Instead,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">create a new generic test execution page</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>called</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><STRONG><SPAN class="">webapp/test/</SPAN><SPAN class="">Test</SPAN></STRONG><SPAN class=""><STRONG>.qunit.html</STRONG>.</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class=""><SPAN class="">This file<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">execute</SPAN><SPAN class="">s</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the test<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">pages</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>migrated in the<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">previous</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">step</SPAN><SPAN class="">.</SPAN></SPAN></SPAN></SPAN></P><pre class="lia-code-sample language-markup"><code>&lt;!doctype html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset="utf-8" /&gt; &lt;!-- Adjust the paths in the following lines to reflect your component namespace --&gt; &lt;script src="../resources/sap/ui/test/starter/runTest.js" data-sap-ui-resource-roots='{ "test-resources.ui5.myapp": "./" }' &gt;&lt;/script&gt; &lt;/head&gt; &lt;body class="sapUiBody"&gt; &lt;div id="qunit"&gt;&lt;/div&gt; &lt;div id="qunit-fixture"&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt; </code></pre><P>&nbsp;</P><H1 id="toc-hId--322828510"><SPAN class=""><SPAN class="">TypeScript</SPAN></SPAN></H1><P><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class=""><EM>Note: The following section is only relevant, if your project is using TypeScript.</EM></SPAN></SPAN></SPAN></SPAN></P><P><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class="">With UI5 1.142</SPAN><SPAN class="">,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">TypeScript suppo</SPAN><SPAN class="">rt<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">for</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>defining<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">a<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">test suite</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>has improved</SPAN><SPAN class="">.</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">Simply</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>import the<SPAN>&nbsp;</SPAN></SPAN><STRONG><SPAN class="">sap/</SPAN><SPAN class="">ui</SPAN><SPAN class="">/test/starter/config/</SPAN><SPAN class="">SuiteConfiguration</SPAN></STRONG><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">types offered by U</SPAN><SPAN class="">I5.<SPAN>&nbsp;</SPAN></SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></SPAN></P><pre class="lia-code-sample language-javascript"><code>import type {SuiteConfiguration} from "sap/ui/test/starter/config"; export default { name: "QUnit test suite for the UI5 Application: ui5.myapp", defaults: { // Adjust the path in the next line to reflect your component namespace page: "ui5://test-resources/ui5/myapp/Test.qunit.html?testsuite={suite}&amp;test={name}", qunit: { version: 2 }, sinon: { version: 1 }, ui5: { language: "EN", theme: "sap_horizon" }, coverage: { // Adjust the path in the next line to reflect your component namespace only: "ui5/myapp/", // Adjust the path in the next line to reflect your component namespace never: "test-resources/ui5/myapp/" }, loader: { paths: { // Adjust the path in the next line to reflect your component namespace "ui5/myapp": "../" } } }, tests: { "unit/unitTests": { title: "Unit tests for ui5.myapp" }, "integration/opaTests": { title: "Integration tests for ui5.myapp" } } } satisfies SuiteConfiguration; </code></pre><P>&nbsp;</P><H1 id="toc-hId--519342015"><SPAN class=""><SPAN class="">Dive Deeper</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></H1><P><SPAN class=""><SPAN class=""><SPAN class="">The UI5 Demo Kit<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">provides</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">detailed</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>documentation<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">on</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the<SPAN>&nbsp;</SPAN></SPAN></SPAN><A class="" href="https://ui5.sap.com/#/topic/22f50c0f0b104bf3ba84620880793d3f" target="_blank" rel="noreferrer noopener"><SPAN class=""><SPAN class="">general concept</SPAN></SPAN></A><SPAN class=""><SPAN class=""><SPAN>&nbsp;</SPAN>and<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">various</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN></SPAN><A class="" href="https://ui5.sap.com/#/topic/738ed025b36e484fa99046d0f80552fd" target="_blank" rel="noreferrer noopener"><SPAN class=""><SPAN class="">configuration options</SPAN></SPAN></A><SPAN class=""><SPAN class="">,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">helping</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>you<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">get the most out of the test starter for your project.</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">For</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>a more interactive<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">learning experience,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>watch</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the<SPAN>&nbsp;</SPAN></SPAN></SPAN><A class="" href="https://www.youtube.com/watch?v=uIG7bSo8wpg" target="_blank" rel="noreferrer noopener nofollow"><SPAN class=""><SPAN class="">UI5ers live #43 session</SPAN></SPAN></A><SPAN class=""><SPAN class="">,</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>where we presented the test starter concept</SPAN><SPAN class="">.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></P><P>&nbsp;</P><H1 id="toc-hId--715855520"><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class="">Summary</SPAN></SPAN></SPAN></SPAN></H1><P><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class=""><SPAN class="">The new test starter concept simplifies the test setup, reduces the boiler</SPAN><SPAN class="">plate code needed to write a<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">QUnit</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>test,<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">and<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">ensures the loading of<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">commonly used</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>testing frameworks (</SPAN><SPAN class="">such as</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">QUnit</SPAN><SPAN class="">, Sinon.JS,<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">and<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">qunit</SPAN><SPAN class="">-reporter)</SPAN><SPAN class="">.</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">It<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">ensures these testing frameworks and your test modules<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">load</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>asynchronously and<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">comply with</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>the Content Security Policy</SPAN><SPAN class="">.</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN></SPAN><SPAN class="">It also</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>allows moving the configuration of the testing frameworks out of the test code.<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">We<SPAN>&nbsp;</SPAN></SPAN><SPAN class="">look</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>forward to your feedback and experience</SPAN><SPAN class="">s</SPAN><SPAN class=""><SPAN>&nbsp;</SPAN>when applying the test starter concept to your UI5 projects.</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></SPAN></SPAN></SPAN></SPAN></P><P>&nbsp;</P> 2026-01-19T10:17:31.056000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/ui5-mcp-server-got-extended-for-ui-integration-cards-generation/ba-p/14312670 UI5 MCP Server Got Extended for UI Integration Cards Generation 2026-01-22T13:54:47.553000+01:00 daniel_vladinov https://community.sap.com/t5/user/viewprofilepage/user-id/198422 <P>Following the&nbsp;<A title="UI5 MCP server article" href="https://community.sap.com/t5/technology-blog-posts-by-sap/give-your-ai-agent-some-tools-introducing-the-ui5-mcp-server/ba-p/14200825" target="_blank">recent announcement</A> for the release and availability of the&nbsp;<SPAN><STRONG>UI5 MCP server</STRONG>,&nbsp;</SPAN><SPAN>we are pleased to announce that the UI5 MCP Server now supports the development of <STRONG>UI Integration Cards</STRONG>. In its latest version – v0.2.1 – it includes three new tools dedicated to UI Integration Cards. These tools assist Large Language Models (LLMs) in creating new cards from scratch or editing existing ones, following the best practices for card development.</SPAN></P><H2 id="toc-hId-1788602686">Infuse Creativity Into Your UI Integration Cards</H2><P>The updated UI5 MCP Server version v.0.2.1 enhances card development with AI Agents like Cline. Fostering creativity and efficiency, this approach makes it easy for both developers and non-developers to create UI Integration Cards smoothly.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="daniel_vladinov_0-1769086020322.png" style="width: 524px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364106i404ABB65D842874E/image-dimensions/524x314/is-moderation-mode/true?v=v2" width="524" height="314" role="button" title="daniel_vladinov_0-1769086020322.png" alt="daniel_vladinov_0-1769086020322.png" /></span></P><P class="lia-align-center" style="text-align: center;"><EM>Usage of the UI5 MCP Server in Cline.</EM></P><H2 id="toc-hId-1592089181">&nbsp;</H2><H2 id="toc-hId-1395575676">Why Use the UI5 MCP Server for Integration Cards Development?</H2><P>The reasons for initially using UI5 MCP Server are similarly applicable to developing Integration Cards:</P><UL><LI>LLMs might lack information on the latest best practices and APIs recommended for UI Integration Cards development.</LI><LI>LLMs might suggest using the properties of UI Integration Cards or APIs that are not available in your project's version or have been marked as deprecated.</LI><LI>Scaffolding can help agents kickstart new projects faster and with established patterns, saving token costs.</LI></UL><P><STRONG>and furthermore:</STRONG></P><UL><LI>Encouraging the use of cards of type Declarative to make the card also compatible with Mobile Native platforms.</LI><LI>Providing specific knowledge for each card type to the LLMs, such as the available chart types in Analytical cards, and enabling the LLMs to suggest the best chart type for your data.</LI><LI>And more…</LI></UL><H2 id="toc-hId-1199062171">Tools</H2><OL><LI>Best practice guidance for your agent: <STRONG><FONT face="courier new,courier">get_integration_cards_guidelines</FONT></STRONG></LI><LI>Scaffolding a new Integration Card: <FONT face="courier new,courier"><STRONG>create_integration_card</STRONG></FONT></LI><LI>Validating the Card’s manifest: <FONT face="courier new,courier"><STRONG>run_manifest_validation</STRONG></FONT></LI></OL><H2 id="toc-hId-1002548666">Setup &amp; Use&nbsp;</H2><P>Install the UI5 MCP Server v.0.2.1 in your AI agent, as described <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/give-your-ai-agent-some-tools-introducing-the-ui5-mcp-server/ba-p/14200825" target="_blank">here</A>. Once this version or later is installed, your AI Agent should automatically gain access to the new tools. Once you define your prompt for generating a Card the AI Agent will offer you as first step to run the&nbsp;<STRONG><FONT face="courier new,courier">get_integration_cards_guidelines</FONT></STRONG><FONT face="courier new,courier"><FONT face="arial,helvetica,sans-serif">, then will scaffold the very coding project with dynamically created Card, and finally will run the manifest schema validation tool. Enjoy your Card!&nbsp;</FONT></FONT></P><H2 id="toc-hId-806035161">What’s Next?</H2><P>We plan to further enhance the existing tools to handle more complex scenarios, such as creating or editing child cards, pagination in list and table cards, a more advanced configuration editor for the generated card, and more.</P> 2026-01-22T13:54:47.553000+01:00 https://community.sap.com/t5/developer-news/sap-developer-news-january-22nd-2026/ba-p/14312899 SAP Developer News, January 22nd, 2026 2026-01-22T21:10:00.030000+01:00 thomas_jung https://community.sap.com/t5/user/viewprofilepage/user-id/139 <P><div class="video-embed-center video-embed"><iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FWH52v01BYOo%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DWH52v01BYOo&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FWH52v01BYOo%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="400" height="225" scrolling="no" title="UI5Con Bangalore, BTP Automation Roundtable, Event Mesh Monitoring, UI5 Roadmap | SAP Developer News" frameborder="0" allow="autoplay; fullscreen; encrypted-media; picture-in-picture;" allowfullscreen="true"></iframe></div></P><H3 id="toc-hId-1917687398"><SPAN>DESCRIPTION</SPAN><SPAN>&nbsp;</SPAN></H3><P><STRONG><SPAN>UI5Con&nbsp;Bangalore</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>UI5con&nbsp;Bangalore:&nbsp;</SPAN><A href="https://openui5.org/ui5con/bengaluru2026/index.html" target="_blank" rel="noopener nofollow noreferrer"><SPAN>https://openui5.org/ui5con/bengaluru2026/index.html</SPAN></A><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>&nbsp;</SPAN><STRONG><SPAN>SAP BTP Platform Automation Roundtable #1</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Blog post&nbsp;</SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/customer-amp-partner-roundtable-for-sap-btp-platform-automation-february/ba-p/14306394" target="_blank"><SPAN>https://community.sap.com/t5/technology-blog-posts-by-sap/customer-amp-partner-roundtable-for-sap-btp-platform-automation-february/ba-p/14306394</SPAN></A><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Registration&nbsp;</SPAN><A href="https://events.teams.microsoft.com/event/ccc79b7d-c96b-4564-a1a3-361b82789171@42f7676c-f455-423c-82f6-dc2d99791af7" target="_blank" rel="noopener nofollow noreferrer"><SPAN>https://events.teams.microsoft.com/event/ccc79b7d-c96b-4564-a1a3-361b82789171@42f7676c-f455-423c-82f6-dc2d99791af7</SPAN></A><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Event Mesh capability&nbsp;monitoring&nbsp;in SAP Cloud ALM</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Monitoring Event Mesh from SAP Integration Suite with SAP Cloud ALM:&nbsp;</SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/monitoring-event-mesh-from-sap-integration-suite-with-sap-cloud-alm/ba-p/14309869" target="_blank"><SPAN>https://community.sap.com/t5/technology-blog-posts-by-sap/monitoring-event-mesh-from-sap-integration-suite-with-sap-cloud-alm/ba-p/14309869</SPAN></A><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>SAPUI5 Roadmap Explorer</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Link to the Roadmap:&nbsp;</SPAN><A href="https://roadmaps.sap.com/board?PRODUCT=73554900100800001361&amp;amp;range=CURRENT-LAST&amp;range=CURRENT-LAST#Q1%202026" target="_blank" rel="noopener noreferrer"><SPAN>https://roadmaps.sap.com/board?PRODUCT=73554900100800001361&amp;amp;range=CURRENT-LAST&amp;range=CURRENT-LAST#Q1%202026</SPAN></A><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Code Connect 2026</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Code Connect website&nbsp;</SPAN><A href="https://code-connect.dev/" target="_blank" rel="noopener nofollow noreferrer"><SPAN>https://code-connect.dev/</SPAN></A><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Devtoberfest Winners Interview - Gert Mertens</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><H3 id="toc-hId-1721173893"><SPAN>CHAPTER TITLES&nbsp;</SPAN><SPAN>&nbsp;</SPAN></H3><P><SPAN>0:00 Intro</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>0:10&nbsp;UI5con&nbsp;Bangalore</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>0:45&nbsp;SAP BTP Platform Automation Roundtable #1</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>1:28&nbsp;Event Mesh capability&nbsp;monitoring&nbsp;in SAP Cloud ALM</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>2:19&nbsp;SAPUI5 Roadmap Explorer</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>3:23&nbsp;Code Connect 2026</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>4:48&nbsp;Devtoberfest Winners Interview - Gert Mertens</SPAN><SPAN>&nbsp;</SPAN></P><H3 id="toc-hId-1524660388"><SPAN>Transcript</SPAN></H3><P>00;00;00;25 - 00;00;06;20</P><P>This is the SAP Developer News for January 22nd, 2026.</P><P>00;00;06;23 - 00;00;09;18</P><P>We have kick started the first CodeJam of 2026 in</P><P>00;00;09;26 - 00;00;25;10</P><P>Hyderabad. We have two more CodeJams on SAP Gen AI and ABAP Cloud. Do let us know in which city you want to have the next CodeJam. And I'll just have one more news to share. UI5Con is coming to Bangalore after many years. So all UI5 enthusiast</P><P>00;00;25;10 - 00;00;44;18</P><P>Just watch out this space for UI5Con Bangalore happening at SAP Labs Innovation Park, Bangalore on February 28th. Mark the date it's February 28th. All the information about the registration can be in the link provided in the description. Thank you.</P><P>00;00;44;21 - 00;01;15;26</P><P>The first monthly customer and partner roundtable for SAP BTP Platform Automation is set to take place next month. Tuesday, the 10th of February at 1100 hours CET. BTP Platform automation. That's right. Topics such as the BTP CLI and Terraform. Two of my favorite topics during the session. There'll be Q&amp;A. There'll be a round of updates, some demos and also a look ahead for the rest of 2026.</P><P>00;01;15;28 - 00;01;28;20</P><P>Everyone's welcome. You just need to register. It's free. Christian's blog post has all the details, so head over there and check it out. Link in the description.</P><P>00;01;28;22 - 00;01;58;22</P><P>Hola developers! Are you using the event mesh capability and you also use cloud them for monitoring? Well, here's some news for you. It is now possible to monitor the event mesh capability directly in SAP cloud ALM, meaning that you can now not just monitor your cloud integration tenant, but also your event mesh capability program for the broker. You can monitor the broker health active connections, message consumers and message queues.</P><P>00;01;58;25 - 00;02;18;20</P><P>The setup is pretty straightforward. If your integration suite is already in cloud ALM, then you just hit the scope selector in the health monitoring app, toggle on the event Mesh capability and that's it. Want to learn more about it? Check out Shivani’s blog post on SAP community. Hasta luego.</P><P>00;02;18;22 - 00;02;44;23</P><P>Developers love road maps, particularly when we're not the ones that have to deliver on them. They're kind of like wish lists that are going to be fulfilled. So it's fun to check out what's, on upcoming road maps. This month we see the updated SAPUI5 road map and contains lots of new functionality around Fiori web components and other capabilities that SAPUI5 makes possible.</P><P>00;02;44;26 - 00;03;11;17</P><P>Just to, give you an example of a few of them. For instance, there's a new geo map control plan in the road map, that will be, metadata driven control. And there is a planned MDC server for, UI5 web components. And that's just one of many things planned in Q1, Q1 being the section that has the most details.</P><P>00;03;11;20 - 00;03;23;26</P><P>But if you're a UI five developer, I'm sure this is a roadmap you're going to want to check out to see what things you can take advantage of in the near future.</P><P>00;03;23;28 - 00;04;05;21</P><P>It's only January, but we can already happily announce that CodeConnect is happening again this year. Undeniably a key in-person event for developers in the SAP ecosphere. This year, it includes three classic one day conferences. UI5Con on Tuesday, reCAP on Wednesday and HANA TechCon on Thursday, and starting the CodeConnect week off on Monday. I'm not one, not two, but three CodeJams one on CAP topics, one on Gen AI coding on the SAP rapid one model and one on UI5.</P><P>00;04;05;23 - 00;04;35;03</P><P>When is CodeConnect, I hear you ask? Well, it's pretty much right in the center of July. It's most of the week beginning the 13th of July. It's been in St. Leon/ROT in Germany as usual at the SAP campus. There we can mingle and chat with like minded developers, listen to and maybe even give sessions and just generally geek out. It's become a must attend event for folks like you and like me.</P><P>00;04;35;05 - 00;04;49;01</P><P>Anyway, head over to the CodeConnect website to learn more and if you are able to become a sponsor, then get in touch with the organizers there. Lovely people and thank you.</P><P>00;04;49;03 - 00;05;13;20</P><P>It's so I'm happy to right back from at TechEd. So what else do you join? And I'm one of the ministers. What are you? Key takeaways. The main things. What did you like? What did you like? What did I learn from the photographers I really liked? Especially this year that, you had, like, all the five tracks, so that you could really choose what you wanted to, to focus on.</P><P>00;05;13;22 - 00;05;37;05</P><P>And also, the photography is also really focused on both juniors and more experienced people. This read something for everybody and also with the funny sense it's it's really yeah, you learn a lot, but also doing it via for example, the puzzle you learn a little of things there. So it was really fun to do and yeah, to be able to just have like a gamification around it.</P><P>00;05;37;08 - 00;05;57;24</P><P>Yeah. That really speaks to me as a developer. So yeah, that it was me. A lot of fun and a lot of learning to do. Yeah. And now you're here and then. Oh yeah. So many highlights. What did you like most? Especially at the community, after, Covid, of course. Yeah. Being back in a community meeting, the mentors.</P><P>00;05;57;24 - 00;06;17;19</P><P>Meeting the advocates. Yeah. Especially that. And also with AI, the big buzzwords, seeing what is a piece doing with it? Yeah, all of those agents that they're building, it's really, really, really nice to just be here and feel the vibe and seeing what is in the future for for it. Yeah, that's what I really like about it I guess right now.</P><P>00;06;17;20 - 00;06;20;10</P><P>Yeah. But thank you guys. No problem.</P><P>&nbsp;</P> 2026-01-22T21:10:00.030000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239 SAP UI5 - Component-Based Architecture: Shell, Feature and Modules 2026-01-23T06:34:36.055000+01:00 dreichert https://community.sap.com/t5/user/viewprofilepage/user-id/835133 <P><ul =""><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-1441940266">1. Shell</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-1374509480">App View</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-1177995975">App Controller</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-981482470">Shell View</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-784968965">Shell Controller</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-588455460">Shell Manifest</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-391941955">Shell Component</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-66345731">2. Feature List</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--1085055">App View</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId-149655797">App Controller</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--46857708">List View</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--243371213">List Controller</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--439884718">List Manifest</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--342995216">3. Feature Detail</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--832911728">Detail View</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--1029425233">Detail Controller</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--1225938738">Detail Manifest</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--1129049236">4. Modules</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--1325562741">5. Communication between Components</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--1647295562">Event handling with sap.ui.core.ComponentRegistry</a></li><li style="list-style-type:disc; margin-left:15px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--2040322572">Passing an API</a></li><li style="list-style-type:disc; margin-left:0px; margin-bottom:1px;"><a href="https://community.sap.com/t5/technology-blog-posts-by-members/sap-ui5-component-based-architecture-shell-feature-and-modules/ba-p/14298239#toc-hId--1943433070">6. Conclusion</a></li></ul></P><P>This post describes one possible way to structure larger Freestyle SAP UI5 applications in a meaningful way.</P><P>The focus here is on a component-based architecture, where the application is split into a parent shell and several functional feature components, following a structure that worked well in our projects.</P><P>In this compact example, the application is hierarchically split into multiple UI5 components.</P><P>Each component is technically a standalone UI5 application with its own Component.js and its own manifest.<BR />This way, the application grows in a controlled manner and can be extended at any time with new features and modules.</P><P>We use a classic list/detail application here. Shell as the framework, Feature List as the entry point, and Feature Detail for the object view.<BR />Modules are smaller (reusable) building blocks within a feature.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="image-20251221-103415.png" style="width: 958px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364013iAA05195729F7D83C/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="image-20251221-103415.png" alt="image-20251221-103415.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="image-20251221-103523.png" style="width: 211px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364017i3CDC0D47E7A7D79D/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="image-20251221-103523.png" alt="image-20251221-103523.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H1 id="toc-hId-1638453771">&nbsp;</H1><H1 id="toc-hId-1441940266">1. Shell</H1><P>The Shell is a standalone UI5 application and represents the technical framework of the application.</P><P>It renders the feature components at the appropriate places and is otherwise responsible for layout, navigation, or UI states (e.g. busy states).</P><H2 id="toc-hId-1374509480">App View</H2><P>The App view is the root view of the Shell and is only responsible for the pure UI structure.</P><P>The Shell view is loaded into the FlexibleColumnLayout.</P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="app.shell.controller.App" displayBlock="true" height="100%" width="100%" xmlns="sap.m" xmlns:f="sap.f" xmlns:mvc="sap.ui.core.mvc"&gt; &lt;App id="app" busy="{appView&gt;/busy}"&gt; &lt;f:FlexibleColumnLayout id="layout" layout="{appView&gt;/layout}" backgroundDesign="Translucent" /&gt; &lt;/App&gt; &lt;/mvc:View&gt;</code></pre><H2 id="toc-hId-1177995975">App Controller</H2><P>The corresponding controller does not contain any notable logic except for initializing the view model appView.</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/utility/controller/BaseController", "sap/ui/model/json/JSONModel" ], function ( BaseController, JSONModel ) { "use strict"; return BaseController.extend("app.shell.controller.App", { onInit: function () { const oViewModel = this._createViewModel(); this.setModel(oViewModel, "appView"); this.getView().addStyleClass(this.getOwnerComponent().getContentDensityClass()); }, _createViewModel: function () { return new JSONModel({ busy: false, delay: 0, layout: "OneColumn", previousLayout: "" }); } }); });</code></pre><H2 id="toc-hId-981482470">Shell View</H2><P>We keep the Shell view fairly simple.</P><P>It contains a layout and an area (the mainContents aggregation) where feature components are rendered, e.g. using a ToolPage.</P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="app.shell.controller.Shell" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:tnt="sap.tnt" xmlns:core="sap.ui.core" displayBlock="true"&gt; &lt;tnt:ToolPage id="toolPage" sideExpanded="false"&gt; &lt;tnt:sideContent&gt; &lt;tnt:SideNavigation id="sideNavigation"&gt; &lt;tnt:item&gt; &lt;tnt:NavigationList id="navList" expanded="false" selectedKey=""&gt; &lt;tnt:NavigationListItem id="navListItemList" text="List" icon="sap-icon://list" select="onNavToList" /&gt; &lt;/tnt:NavigationList&gt; &lt;/tnt:item&gt; &lt;/tnt:SideNavigation&gt; &lt;/tnt:sideContent&gt; &lt;tnt:mainContents&gt; &lt;/tnt:mainContents&gt; &lt;/tnt:ToolPage&gt; &lt;/mvc:View&gt;</code></pre><H2 id="toc-hId-784968965">Shell Controller</H2><P>After the Shell has been loaded, we directly navigate to Feature List and render it into mainContents of the ToolPage.</P><P>In addition, we define two event listeners for the events "setShellBusy" and "hideShellBusy" for later communication.</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/utility/controller/BaseController", "sap/ui/model/json/JSONModel", "sap/ui/Device" ], function ( BaseController, JSONModel, Device ) { "use strict"; return BaseController.extend("app.shell.controller.Shell", { onInit: function () { const oViewModel = this._createViewModel(); this.setModel(oViewModel, "shellView"); this.getRouter().getRoute("shell").attachPatternMatched(this._onShellPatternMatched, this); this.getOwnerComponent().attachSetShellBusy(this._setShellBusy, this); this.getOwnerComponent().attachHideShellBusy(this._hideShellBusy, this); }, _onShellPatternMatched: function () { this._navToList(); }, _createViewModel: function () { return new JSONModel({ busy: false, delay: 50 }); }, _setShellBusy: function() { this.getModel("appView").setProperty("/busy", true); }, _hideShellBusy: function() { this.getModel("appView").setProperty("/busy", false); }, _navToList: function () { const bReplace = !Device.system.phone; this.getRouter().navTo("list", {}, bReplace); } }); });</code></pre><H2 id="toc-hId-588455460">Shell Manifest</H2><P>The Shell knows its features via "componentUsages" in the manifest.</P><P>In our example, we want to display the List feature as the first feature within the Shell.</P><P>For this, we define the List feature once as a component in "componentUsages" and create a route.<BR />The "controlAggregation" of List is the "mainContents" aggregation of the ToolPage in the view.</P><pre class="lia-code-sample language-json"><code>{ "_version": "1.32.0", "sap.app": { "id": "app.shell", "type": "application", "i18n": "i18n/i18n.properties", "applicationVersion": { "version": "0.0.1" }, "title": "{{appTitle}}", "description": "{{appDescription}}" }, "sap.ui": { "technology": "UI5", "fullWidth": true, "deviceTypes": { "desktop": true, "tablet": true, "phone": true } }, "sap.ui5": { "dependencies": { "minUI5Version": "1.120.0", "libs": { "sap.ui.core": {}, "sap.m": {}, "sap.f": {}, "sap.tnt": {} } }, "models": { "i18n": { "type": "sap.ui.model.resource.ResourceModel", "settings": { "bundleName": "app.shell.i18n.i18n" } } }, "componentUsages": { "list": { "name": "app.list", "lazy": false } }, "routing": { "config": { "routerClass": "sap.f.routing.Router", "viewType": "XML", "viewPath": "app.shell.view", "controlId": "layout", "controlAggregation": "beginColumnPages", "async": true, "bypassed": { "target": "shell" } }, "routes": [ { "pattern": "", "name": "shell", "target": "shell" }, { "pattern": "List", "name": "list", "target": ["shell", "list"] } ], "targets": { "shell": { "viewName": "Shell", "viewId": "shell", "viewLevel": 1 }, "list": { "type": "Component", "usage": "list", "parent": "shell", "controlId": "toolPage", "controlAggregation": "mainContents" } } }, "rootView": { "viewName": "app.shell.view.App", "type": "XML", "async": true, "id": "app" } } }</code></pre><H2 id="toc-hId-391941955">Shell Component</H2><P>Besides the usual router initialization, we only provide the two events already mentioned: setShellBusy and hideShellBusy.</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/shell/model/models", "sap/ui/core/UIComponent" ], function( models, UIComponent ) { "use strict"; return UIComponent.extend("app.shell.Component", { metadata: { manifest: "json", events: { setShellBusy: {}, hideShellBusy: {} } }, init: function () { UIComponent.prototype.init.apply(this, arguments); this.setModel(models.createDeviceModel(), "device"); this.getRouter().initialize(); }, destroy: function() { UIComponent.prototype.destroy.apply(this, arguments); }, getContentDensityClass: function() { if (this._sContentDensityClass === undefined) { if (document.body.classList.contains("sapUiSizeCozy") || document.body.classList.contains("sapUiSizeCompact")) { this._sContentDensityClass = ""; } else if (!Device.support.touch) { this._sContentDensityClass = "sapUiSizeCompact"; } else { this._sContentDensityClass = "sapUiSizeCozy"; } } return this._sContentDensityClass; } }); });</code></pre><H1 id="toc-hId-66345731">2. Feature List</H1><P>Feature components are also technically standalone UI5 applications and can be executed separately.</P><P>Similar to the Shell, we can link feature components (List/Detail) and also integrate smaller modules.</P><P>Feature List decides when to navigate to Detail.</P><P>In the case of a list/detail linking, we pass the necessary parameters during navigation. Apart from that, Feature List does not know any further details about Feature Detail.</P><H2 id="toc-hId--1085055">App View</H2><P>Structured similarly to the Shell. The FlexibleColumnLayout is important here for the interaction between List and Detail.</P><P>Layout handling is bound to the view model.</P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="app.list.controller.App" displayBlock="true" height="100%" xmlns="sap.m" xmlns:f="sap.f" xmlns:mvc="sap.ui.core.mvc"&gt; &lt;App id="app" height="100%"&gt; &lt;f:FlexibleColumnLayout id="layout" height="100%" layout="{appView&gt;/layout}" backgroundDesign="Translucent" /&gt; &lt;/App&gt; &lt;/mvc:View&gt;</code></pre><H2 id="toc-hId-149655797">App Controller</H2><P>Here we initialize the view model and add a new event listener for the component event setLayout.</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/utility/controller/BaseController", "sap/ui/model/json/JSONModel" ], function (BaseController, JSONModel) { "use strict"; return BaseController.extend("app.list.controller.App", { onInit: function () { const oViewModel = this._createViewModel(); this.setModel(oViewModel, "appView"); this.getOwnerComponent().attachSetLayout(this._onSetLayout, this); }, _onSetLayout: function (oEvent) { const sLayout = oEvent.getParameter("layout"); this.getModel("appView").setProperty("/layout", sLayout); }, _createViewModel: function() { const oViewModel = new JSONModel({ busy: false, delay: 0 }); return oViewModel; } }); });</code></pre><H2 id="toc-hId--46857708">List View</H2><P>For simplicity, a normal list with some dummy items:</P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="app.list.controller.List" xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" displayBlock="true"&gt; &lt;Page title="List Feature" class="sapUiContentPadding"&gt; &lt;content&gt; &lt;List id="demoList" mode="SingleSelectMaster" itemPress="onItemPress"&gt; &lt;StandardListItem title="Item 1000" description="1000000000" type="Active" /&gt; &lt;StandardListItem title="Item 2000" description="1000000001" type="Active" /&gt; &lt;StandardListItem title="Item 3000" description="1000000002" type="Active" /&gt; &lt;/List&gt; &lt;/content&gt; &lt;/Page&gt; &lt;/mvc:View&gt;</code></pre><H2 id="toc-hId--243371213">List Controller</H2><P>Without any major binding logic, we have direct navigation to the Detail feature here, passing the item ID as a parameter.</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/utility/controller/BaseController", "sap/ui/Device", "sap/ui/model/json/JSONModel" ], function (BaseController, Device, JSONModel) { "use strict"; return BaseController.extend("app.list.controller.List", { onInit: function () { const oViewModel = this._createViewModel(); this.setModel(oViewModel, "listView"); this.getRouter().getRoute("list").attachPatternMatched(this._onListMatched, this); }, _onListMatched: function () { // maybe further binding logic }, onItemPress: function (oEvent) { const oItem = oEvent.getParameter("listItem"); const sId = oItem.getDescription(); const bReplace = !Device.system.phone; this.getRouter().navTo("detail", { id: sId }, bReplace); }, _createViewModel: function () { const oViewModel = new JSONModel({ busy: false, delay: 0, noDataText: this.getResourceBundle().getText("noDataText") }); return oViewModel; } }); });</code></pre><H2 id="toc-hId--439884718">List Manifest</H2><P>As in the Shell, we integrate the next feature (Detail) in the manifest of the Feature List component.</P><P>Feature Detail also receives its own route.</P><pre class="lia-code-sample language-json"><code>{ "_version": "1.32.0", "sap.app": { "id": "app.list", "type": "component", "i18n": "i18n/i18n.properties", "applicationVersion": { "version": "0.0.1" }, "title": "{{appTitle}}", "description": "{{appDescription}}" }, "sap.ui": { "technology": "UI5", "fullWidth": true, "deviceTypes": { "desktop": true, "tablet": true, "phone": true } }, "sap.ui5": { "dependencies": { "minUI5Version": "1.120.0", "libs": { "sap.ui.core": {}, "sap.m": {}, "sap.f": {} } }, "componentUsages": { "detail": { "name": "app.detail", "lazy": false } }, "contentDensities": { "compact": true, "cozy": true }, "models": { "i18n": { "type": "sap.ui.model.resource.ResourceModel", "settings": { "bundleName": "app.list.i18n.i18n" } } }, "routing": { "config": { "routerClass": "sap.f.routing.Router", "viewType": "XML", "viewPath": "app.list.view", "controlId": "layout", "controlAggregation": "beginColumnPages", "async": true, "bypassed": { "target": "list" } }, "routes": [ { "pattern": "", "name": "list", "target": "list" }, { "pattern": "detail/{id}", "name": "detail", "target": "detail" } ], "targets": { "list": { "viewName": "List", "viewId": "list", "viewLevel": 1 }, "detail": { "type": "Component", "usage": "detail", "controlAggregation": "midColumnPages" } } }, "rootView": { "viewName": "app.list.view.App", "type": "XML", "async": true, "id": "app" } } }</code></pre><H1 id="toc-hId--342995216">3. Feature Detail</H1><P>Feature Detail also has an App view and an App controller, but in this example they do not contain any significant logic. Therefore, I omit them here. The same also applies to "Component.js".</P><H2 id="toc-hId--832911728">Detail View</H2><P>For this example, we would simply assume that the detail view is a simple ObjectPageLayout. Then, we embed additional modules in the individual sections, each within a ComponentContainer. For simplicity, I will only cover the Comments module.</P><P>The ComponentContainer automatically renders the Comments module which we specify in the usage property (“moduleComments” is the componentUsages name from the manifest).</P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="app.detail.controller.Detail" xmlns="sap.m" displayBlock="true" xmlns:mvc="sap.ui.core.mvc" xmlns:uxap="sap.uxap" xmlns:core="sap.ui.core"&gt; &lt;uxap:ObjectPageLayout id="objectPageLayout" showTitleInHeaderContent="true" showFooter="true" alwaysShowContentHeader="true" preserveHeaderStateOnScroll="false" headerContentPinnable="true" isChildPage="true" upperCaseAnchorBar="false" useIconTabBar="true" enableLazyLoading="true"&gt; &lt;uxap:headerTitle&gt; &lt;uxap:ObjectPageDynamicHeaderTitle id="objectPageDynamicHeaderTitle" areaShrinkRatio="5:1:1"&gt; &lt;uxap:heading&gt; &lt;Title id="objectPageDynamicHeaderTitleHeadingTitle" wrapping="true" text="Feature Detail: {detailView&gt;/id}"/&gt; &lt;/uxap:heading&gt; &lt;/uxap:ObjectPageDynamicHeaderTitle&gt; &lt;/uxap:headerTitle&gt; &lt;uxap:headerContent&gt; &lt;/uxap:headerContent&gt; &lt;uxap:sections&gt; &lt;!-- Modul Comments --&gt; &lt;uxap:ObjectPageSection title="Comments"&gt; &lt;uxap:subSections&gt; &lt;uxap:ObjectPageSubSection mode="Expanded"&gt; &lt;core:ComponentContainer id="moduleCommentsContainer" usage="moduleComments" async="true" componentCreated="onComponentCommentsCreated"/&gt; &lt;/uxap:ObjectPageSubSection&gt; &lt;/uxap:subSections&gt; &lt;/uxap:ObjectPageSection&gt; &lt;!-- Modul History --&gt; &lt;uxap:ObjectPageSection title="History"&gt; &lt;uxap:subSections&gt; &lt;uxap:ObjectPageSubSection mode="Expanded"&gt; &lt;!-- &lt;core:ComponentContainer id="moduleHistoryContainer" usage="moduleHistory" async="true" componentCreated="onComponentHistoryCreated"/&gt; --&gt; &lt;Text text="History"/&gt; &lt;/uxap:ObjectPageSubSection&gt; &lt;/uxap:subSections&gt; &lt;/uxap:ObjectPageSection&gt; &lt;!-- Modul Flow --&gt; &lt;uxap:ObjectPageSection title="Flow"&gt; &lt;uxap:subSections&gt; &lt;uxap:ObjectPageSubSection mode="Expanded"&gt; &lt;!-- &lt;core:ComponentContainer id="moduleFlowContainer" usage="moduleFlow" async="true" componentCreated="onComponentFlowCreated"/&gt; --&gt; &lt;Text text="Flow"/&gt; &lt;/uxap:ObjectPageSubSection&gt; &lt;/uxap:subSections&gt; &lt;/uxap:ObjectPageSection&gt; &lt;/uxap:sections&gt; &lt;uxap:footer&gt; &lt;OverflowToolbar&gt; &lt;/OverflowToolbar&gt; &lt;/uxap:footer&gt; &lt;/uxap:ObjectPageLayout&gt; &lt;/mvc:View&gt;</code></pre><H2 id="toc-hId--1029425233">Detail Controller</H2><P>There are multiple points here.</P><P>First, we implement a simple view binding (_bindView) where we bind the ID passed from List to our view.</P><P>Second, some functions for chapter 5 (communication) are already defined which trigger events in other components.<BR />For example, setting or clearing the busy state in the Shell (_setShellBusy and _hideShellBusy) or controlling the layout in List (_listSetLayout).</P><P>And lastly, there are handler functions for the componentCreated event of the module ComponentContainer.</P><P>Here I am already anticipating something:</P><P>As soon as the Comments module was created, the function onComponentCommentsCreated is triggered. In this function, we pass an API created by "_createCommentsApi" to the module, through which "Comments" can call functions of the parent component.</P><P>This is one possible way of communication between components.</P><P>Another way, which I will discuss later, is accessing the required component via a defined ID. In our example we use a helper function called "getComponent". More on this later.</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/utility/controller/BaseController", "sap/ui/model/json/JSONModel" ], function( BaseController, JSONModel ) { "use strict"; return BaseController.extend("app.detail.controller.Detail", { onInit: function() { const oViewModel = this._createViewModel(); this.setModel(oViewModel, "detailView"); this.getRouter().getRoute("detail").attachPatternMatched(this._handleRouteMatched, this); }, getEntityId: function() { return this.getModel("detailView").getProperty("/id"); }, _createViewModel: function() { const oViewModel = new JSONModel({ busy: false, delay: 50, id: "" }); return oViewModel; }, _handleRouteMatched: async function(oEvent) { try { this._listSetLayout("TwoColumnsMidExpanded"); const sId = oEvent.getParameter("arguments").id; this._setShellBusy(); await this._bindView(sId); await this._bindCommentsModule(sId); } catch(oError) { // Error handling } finally { this._hideShellBusy(); } }, _createCommentsApi: function () { return { getEntityId: this.getEntityId.bind(this), setShellBusy: this._setShellBusy.bind(this), hideShellBusy: this._hideShellBusy.bind(this) }; }, _bindView: async function(sId) { this.getModel("detailView").setProperty("/id", sId); return Promise.resolve(true); }, _bindCommentsModule: async function(sId) { const oComponent = this.byId("moduleCommentsContainer").getComponentInstance(); if(oComponent) { oComponent.fireSetEntityId({id: sId}); } return Promise.resolve(true); }, /* --- Shell Busy State Handling --- */ _setShellBusy: function() { try { this.getComponent("app.shell").fireSetShellBusy(); } catch (oError) { //error handling } }, _hideShellBusy: function() { try { this.getComponent("app.shell").fireHideShellBusy(); } catch (oError) { //error handling } }, /* --- Feature List Events --- */ _listSetLayout: function(sLayout) { try { this.getComponent("app.list").fireSetLayout({layout: sLayout}); } catch (oError) { //error handling } }, /* --- Module Creation Handling --- */ onComponentCommentsCreated: function(oEvent) { const oComponent = oEvent.getParameter("component"); oComponent.oAPI = this._createCommentsApi(); this._bindCommentsModule(this.getModel("detailView").getProperty("/id")); }, onComponentHistoryCreated: function(oEvent) { }, onComponentFlowCreated: function() { } }); });</code></pre><H2 id="toc-hId--1225938738">Detail Manifest</H2><P>The Detail feature can contain additional modules that are used within this feature.</P><P>In our example, these would be Comments, History, and Flow, for which we have already created the appropriate ComponentContainers in the view. The modules are automatically loaded into the respective ComponentContainers when opening the Detail feature.</P><pre class="lia-code-sample language-json"><code>{ "_version": "1.32.0", "sap.app": { "id": "app.detail", "type": "application", "i18n": "i18n/i18n.properties", "applicationVersion": { "version": "0.0.1" }, "title": "{{appTitle}}", "description": "{{appDescription}}", "dataSources": { "mainService": { "uri": "/sap/opu/odata/SERVICE", "type": "OData", "settings": { "localUri": "localService/metadata.xml", "odataVersion": "2.0" } } } }, "sap.ui": { "technology": "UI5", "fullWidth": true, "icons": { "icon": "", "favIcon": "", "phone": "", "phone@2": "", "tablet": "", "tablet@2": "" }, "deviceTypes": { "desktop": true, "tablet": true, "phone": true } }, "sap.ui5": { "flexEnabled": true, "dependencies": { "minUI5Version": "1.97.0", "libs": { "sap.ui.core": {}, "sap.m": {}, "sap.f": {}, "sap.uxap": {} } }, "componentUsages": { "moduleComments": { "name": "app.module.comments", "lazy": true }, "moduleHistory": { "name": "app.module.history", "lazy": true }, "moduleFlow": { "name": "app.module.flow", "lazy": true } }, "contentDensities": { "compact": true, "cozy": true }, "models": { "i18n": { "type": "sap.ui.model.resource.ResourceModel", "settings": { "bundleName": "app.detail.i18n.i18n" } } }, "routing": { "config": { "routerClass": "sap.f.routing.Router", "viewType": "XML", "viewPath": "app.detail.view", "controlId": "app", "controlAggregation": "pages", "async": true }, "routes": [ { "pattern": "Detail/{id}", "name": "detail", "target": [ "detail" ] } ], "targets": { "detail": { "viewName": "Detail", "viewLevel": 1, "viewId": "detail" } } }, "rootView": { "viewName": "app.detail.view.App", "type": "XML", "async": true, "id": "app" } } }</code></pre><H1 id="toc-hId--1129049236">4. Modules</H1><P>Modules are smaller, reusable components that are used exclusively (at least in our example) within a feature.</P><P>What is defined as a module depends on the specific use case.</P><P>In our projects, modules are small functional components that are embedded in multiple features.</P><P>For example, a UI5 application with a table for change history, notes (in our example Comments), etc.</P><P>The module takes care of its own UI and data retrieval.</P><P>The structure can remain very clear and compact:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="image-20260121-121142.png" style="width: 211px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364039i1E965F55798C5E67/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="image-20260121-121142.png" alt="image-20260121-121142.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H1 id="toc-hId--1325562741">5. Communication between Components</H1><P>This is the most important part.</P><P>For communication between components (e.g. passing data or triggering functions), there are multiple possibilities.</P><P>Some use the EventBus, others build an additional service class.</P><P>Here I want to explain two possibilities that we have used in our projects.</P><H2 id="toc-hId--1647295562">Event handling with sap.ui.core.ComponentRegistry</H2><P>We define custom events in Component.js for functions that we want to use in the respective component.</P><P><STRONG>The following scenario</STRONG>:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="image-20260121-141024.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364040iEB12FDB34B1FB073/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="image-20260121-141024.png" alt="image-20260121-141024.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>As soon as we click an entry in the list of Feature List, we want to see the details in Feature Detail on the right side.<BR />For that, we need to set the layout of the FlexibleColumnLayout in the App.controller.js of Feature List to "TwoColumnsMidExpanded".</P><P>We already prepared the necessary steps.</P><P>1. Define the event "setLayout" in the List component.</P><pre class="lia-code-sample language-json"><code>metadata: { manifest: "json", events: { setLayout: { parameters: { layout: {type: "string"} } } } }</code></pre><P>2. Define the event handler in App.controller.js (List).</P><pre class="lia-code-sample language-javascript"><code>onInit: function () { //... this.getOwnerComponent().attachSetLayout(this._onSetLayout, this); //... }, _onSetLayout: function (oEvent) { const sLayout = oEvent.getParameter("layout"); this.getModel("appView").setProperty("/layout", sLayout); },</code></pre><P>To be able to use this event in Feature Detail, we created a helper function getComponent in our example.</P><pre class="lia-code-sample language-javascript"><code>getComponent: function (sAppId) { let oResultComponent; const aComponentNames = Object.keys(sap.ui.core.ComponentRegistry.all()); aComponentNames.forEach((sComponentName) =&gt; { const oComponent = sap.ui.core.ComponentRegistry.all()[sComponentName]; if (oComponent.getManifestEntry("sap.app").id === sAppId) { oResultComponent = oComponent; } }); return oResultComponent; }</code></pre><P>We pass the app ID to this function, which we previously defined in the component’s manifest.json, and which identifies the component we want to access.</P><P>This function is reliable as long as app IDs are maintained properly and kept unique.</P><P>This helper can also be centralized. In our example, the function is located in the BaseController, which is outsourced into a separate Utility library.</P><pre class="lia-code-sample language-bash"><code>Utility/ └─ src/ └─ app/ └─ utility/ ├─ controller/ │ └─ BaseController.js ├─ library.js ├─ manifest.json ├─ package.json └─ ui5.yaml</code></pre><P>Since each feature inherits from the BaseController, Feature Detail can now trigger the layout event, for example.</P><pre class="lia-code-sample language-javascript"><code>_listSetLayout: function(sLayout) { try { this.getComponent("app.list").fireSetLayout({layout: sLayout}); } catch (oError) { //error handling } },</code></pre><P>The ID "app.list" is the ID that we defined in the manifest.json of Feature List.</P><pre class="lia-code-sample language-json"><code> "sap.app": { "id": "app.list"</code></pre><P>As soon as the event is triggered, Feature Detail opens in the right area of the FlexibleColumnLayout.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="image-20260121-140859.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/364041i07B826E0A363F8BD/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="image-20260121-140859.png" alt="image-20260121-140859.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><H2 id="toc-hId--1843809067">&nbsp;</H2><H2 id="toc-hId--2040322572">Passing an API</H2><P>Another possibility that we also like to use is passing an “API” to another component.</P><P>Here we create an object with callbacks and pass it to a newly created or rendered component.</P><P>We have already prepared this in Feature Detail as well:</P><pre class="lia-code-sample language-javascript"><code>_createCommentsApi: function () { return { getEntityId: this.getEntityId.bind(this), setShellBusy: this._setShellBusy.bind(this), hideShellBusy: this._hideShellBusy.bind(this) }; },</code></pre><P>As soon as the Comments module was created by the ComponentContainer, we pass the API with the callbacks.</P><pre class="lia-code-sample language-javascript"><code>onComponentCommentsCreated: function(oEvent) { const oComponent = oEvent.getParameter("component"); oComponent.oAPI = this._createCommentsApi(); this._bindCommentsModule(this.getModel("detailView").getProperty("/id")); }, </code></pre><P>This way, the Comments module is able to execute functions of the parent component.</P><P>A very rough example:</P><P>We set the busy state of the Shell before the view binding via the Feature Detail API in the function "_setEntityId" and clear it again afterwards.</P><P><STRONG>Comments Controller</STRONG>:</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "app/utility/controller/BaseController", "sap/ui/model/json/JSONModel" ], function ( BaseController, JSONModel ) { "use strict"; return BaseController.extend("app.module.comments.controller.Comments", { onInit: function () { const oViewModel = this._createViewModel(); this.setModel(oViewModel, "moduleView"); this.getOwnerComponent().attachSetEntityId(this._setEntityId, this); }, _setEntityId: function(oEvent) { this.getOwnerComponent().oAPI.setShellBusy(); this.getModel("moduleView").setProperty("/id", oEvent.getParameter("id")); this.getOwnerComponent().oAPI.hideShellBusy(); }, _createViewModel: function() { const oViewModel = new JSONModel({ busy: false, delay: 50, id: "" }); return oViewModel; } }); });</code></pre><H1 id="toc-hId--1943433070">6. Conclusion</H1><P>The examples shown here are intended only as an inspiration, and I therefore kept them intentionally rough and simple.</P><P>If you notice early on (or already know) that the application will grow significantly in the future, it can be worth thinking about a larger architecture already at this stage.</P><P>If you wait too long, dependencies quickly arise and the restructuring becomes unnecessarily complex and time-consuming.</P><P>A clean structure does not only pay off in readability, but also makes later maintenance easier.</P><P>&nbsp;</P> 2026-01-23T06:34:36.055000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/run-your-app-on-any-ui5-version/ba-p/14313421 Run your app on any UI5 version 2026-01-23T11:26:03.657000+01:00 kayur_goyal https://community.sap.com/t5/user/viewprofilepage/user-id/304198 <P>Here’s a quick, zero-deploy way to test your app against a specific SAPUI5 version right from the browser.</P><P>The hack<BR />- Append the UI5 version you want to your app URL:<BR />- If your URL has no query parameters: ?sap-ui-version=1.43.2<BR />- If it already has query parameters: &amp;sap-ui-version=1.43.2<BR />- Example: <A href="https://my-app.example.com/index.html?sap-ui-version=1.120.3" target="_blank" rel="noopener nofollow noreferrer">https://my-app.example.com/index.html?sap-ui-version=1.120.3</A></P><P>What it does<BR />- When your app bootstraps SAPUI5 from the SAP CDN, adding sap-ui-version in the URL instructs UI5 to load its resources from that version.<BR />- In practice, you can flip between versions to quickly check whether a bug is version-specific without touching your build or deployment pipeline.</P><P>How to confirm it worked<BR />- Open the browser console and run:<BR />- sap.ui.getVersionInfo().version<BR />- You should see the version you specified in the URL.</P><P>Great use cases<BR />- Reproducing customer issues on older UI5 versions<BR />- Validating if a regression disappears on a newer patch<BR />- Comparing behavior across minor versions without rebuilding</P><P>Important notes and caveats<BR />- It works when your app loads UI5 from the SAPUI5/OpenUI5 CDN (e.g., …/resources/sap-ui-core.js). If your app uses a self-hosted UI5 (e.g., from an ABAP front-end server), the parameter may not take effect.<BR />- Some apps explicitly pin a version in the bootstrap URL (e.g., …/1.108.3/resources/sap-ui-core.js). In that case, the pinned version will win.<BR />- Browser caching and accelerators can get in the way. If the version doesn’t seem to change, hard refresh the page (Ctrl/Cmd+Shift+R) or clear cache.<BR />- Don’t ship this as a “feature” for end users. It’s a developer convenience for testing and troubleshooting.<BR />- Compatibility isn’t guaranteed across major/minor versions. Always validate critical flows when switching versions.</P><P>Bonus: other handy UI5 URL flags<BR />- sap-ui-debug=true — loads non-minified sources for better stack traces<BR />- sap-ui-language=en — forces a language for quick i18n checks<BR />- sap-ui-theme=sap_fiori_3 — switches theme without redeploy<BR />- sap-ui-xx-componentPreload=off — disables component preload during debugging</P><P>Wrap-up<BR />Next time you need to answer “Is this a UI5 issue or my app?” save yourself a build and try the URL trick. A simple &amp;sap-ui-version=… can be the fastest path to clarity.</P><P>Thank you&nbsp;</P> 2026-01-23T11:26:03.657000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/sap-ux-q1-2026-update-part-2-sap-s-4hana-cloud-public-edition-2602-and-sap/ba-p/14316127 SAP UX Q1/2026 Update – Part 2: SAP S/4HANA Cloud Public Edition 2602 and SAP Fiori Launchpad 2026-01-28T09:57:22.998000+01:00 ThomasReiss https://community.sap.com/t5/user/viewprofilepage/user-id/149639 <P><STRONG>This second post in my series gives an overview of user experience innovations that become generally available with the SAP S/4HANA Cloud Public Edition 2602 release. Highlights are further AI-assisted features: for personalizing My Home, getting explanations for error messages, and Situation Handling recommendations. We also provide the often-requested capability to launch apps via transaction code or SAP Fiori ID, the new “required reading” feature for news in My Home, and mobile phone optimization – to name a few.</STRONG></P><P>Users in SAP S/4HANA Cloud Public Edition can use Joule to access their data: via web browser on desktop, or mobile, via SAP Mobile Start, which also provides access to applications directly. Read about innovations for Joule and SAP Mobile Start in the first blog post introducing the series, and providing information about SAP Build Work Zone:</P><UL><LI><SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-ux-q1-2026-update-part-1-ai-joule-sap-build-work-zone-sap-mobile-start/ba-p/14312110" target="_blank">SAP UX Q1/2026 Update – Part 1: AI, Joule, SAP Build Work Zone, SAP Mobile Start</A></SPAN>.</LI></UL><P>Below, you will get an overview of SAP S/4HANA Cloud Public Edition UX innovations in these areas:</P><P class="lia-indent-padding-left-30px" style="padding-left : 30px;"><STRONG>SAP Fiori launchpad</STRONG><BR />&nbsp; o.&nbsp; New shell bar now the default<BR />&nbsp; o.&nbsp; Launch apps directly via Transaction Code / Fiori-ID<BR />&nbsp; o.&nbsp; Making New Apps Available to Users<BR />&nbsp; &nbsp; &nbsp; -&nbsp; Activating new apps, since now delivered inactive<BR />&nbsp; &nbsp; &nbsp; -&nbsp; New Experience Switch<BR />&nbsp; &nbsp;o.&nbsp; Further improvements for the launchpad<BR /><STRONG>My Home<BR /></STRONG>&nbsp; o.&nbsp; Manage News app now with “must be read” feature<BR />&nbsp; o.&nbsp; Enhanced Personalization<BR />&nbsp; o.&nbsp; AI-Assisted UX Features:<BR />&nbsp; &nbsp; &nbsp; &nbsp;-&nbsp; AI-Assisted Smart Personalization of My Home for Applications<BR />&nbsp; &nbsp; &nbsp; &nbsp;-&nbsp; AI-Assisted Error Explanation<BR />&nbsp; &nbsp; &nbsp; &nbsp;-&nbsp; AI-Assisted Situation Handling<BR /><STRONG>SAP Fiori elements mobile phone optimization<BR /></STRONG><STRONG>UI Adaptation<BR /></STRONG><STRONG>Improvements in details<BR /></STRONG></P><P>Beyond these, we have a number of interesting new UX innovations for beta testing and early adopters with the 2602 release. I plan to cover these in my next blog post in the next few days.</P><P>Many of the UX improvements in details are enabled by our UI framework (SAPUI5 and SAP Fiori elements) and hence also available to customers and partners developing their own applications. Note however that the enabled AI-assisted features are only available for extensions built directly on the SAP S/4HANA Cloud Public Edition system.</P><P>Below you’ll see AI innovations from our UX framework as well as some selected application highlights. You can get a more comprehensive overview of the latest AI innovations in these blog posts, which also include application specific examples:</P><UL><LI><SPAN><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/my-highlights-of-the-sap-cloud-erp-2602-release/ba-p/14304712" target="_blank">Highlights of the SAP S/4HANA Cloud Public Edition 2602 release</A></SPAN>.</LI><LI><SPAN><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/ai-innovations-highlights-in-sap-cloud-erp-2602/ba-p/14302631" target="_blank">AI innovations highlights in SAP Cloud ERP 2602</A></SPAN>.</LI><LI><SPAN><A href="https://community.sap.com/t5/enterprise-resource-planning-blog-posts-by-sap/artificial-intelligence-in-sap-cloud-erp-2602/ba-p/14307849" target="_blank">Artificial Intelligence in SAP Cloud ERP 2602</A></SPAN>.</LI></UL><P>&nbsp;</P><H1 id="toc-hId-1659634178">SAP Fiori Launchpad</H1><P>Let’s look closer at these UX updates:</P><UL><LI>New shell bar now the default, with updated design of notifications and user menu.</LI><LI>Launch apps directly via Transaction Code / Fiori-ID.</LI><LI>Further improvements.</LI></UL><H2 id="toc-hId-1592203392"><SPAN>New shell bar now the default</SPAN></H2><P>We have made design improvements which are relevant for a consistent user experience across the entire SAP Business Suite. The new shell bar first became available as an opt-in choice for customers with SAP S/4HANA Cloud Public Edition 2508 as described in my <SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-ux-q3-2025-update-part-2-sap-s-4hana-cloud-public-edition-2508-and-sap/ba-p/14171291" target="_blank">blog post covering 2508</A></SPAN>. With the 2602 release, it is now activated by default.</P><H3 id="toc-hId-1524772606"><SPAN>Benefits of the New Shell Bar</SPAN></H3><P>The new shell bar has these benefits:</P><UL><LI>The product name is displayed prominently now to the right of the SAP logo. In this case the name is “S/4HANA Cloud”.</LI><LI>Updated notifications design.</LI><LI>Updated user menu design.</LI></UL><H3 id="toc-hId-1328259101"><SPAN>Product Name Displayed</SPAN></H3><P>The immediately visible difference with the new shell bar is that the product name is displayed to the right of the SAP Logo, in this case “S/4HANA Cloud” as you can see at the top of Figure 1. The logo and the name are all contained within a clickable area, which brings the user to the start page (i.e. My Home).</P><P><EM><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 1: New shell bar, now activated by default. Alt Text: A screenshot of Manage Sales Orders app, with the shell header highlighted, looking as described in the text." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366048iF38B5AEB2A05DC56/image-size/large?v=v2&amp;px=999" role="button" title="01 Shell bar.jpg" alt="Figure 1: New shell bar, now activated by default. Alt Text: A screenshot of Manage Sales Orders app, with the shell header highlighted, looking as described in the text." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 1: New shell bar, now activated by default. Alt Text: A screenshot of Manage Sales Orders app, with the shell header highlighted, looking as described in the text.</span></span></EM></P><H3 id="toc-hId-1131745596"><SPAN>Updated User Menu</SPAN></H3><P>The new user menu design shown in Figure 2 provides more space for the avatar of the user, and provides more space between items for easier selection. The settings are always at the top as the first entry, and the “Sign Out” option is separated from the actual navigation options in the list, to make it easier to find.</P><P>&nbsp;</P><P><EM><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 2: Updated design of user menu and notifications. Alt Text: On the left an image of the previous user menu and the updated user menu. On the right an image of the updated notifications list, showing four notifications in the group “Last Month”, each with a title, two lines of description and a line with the data and a “more” link." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366049iD157F6511FB5B449/image-size/large?v=v2&amp;px=999" role="button" title="02 User Menu and Notifications S4H 2508.jpg" alt="Figure 2: Updated design of user menu and notifications. Alt Text: On the left an image of the previous user menu and the updated user menu. On the right an image of the updated notifications list, showing four notifications in the group “Last Month”, each with a title, two lines of description and a line with the data and a “more” link." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 2: Updated design of user menu and notifications. Alt Text: On the left an image of the previous user menu and the updated user menu. On the right an image of the updated notifications list, showing four notifications in the group “Last Month”, each with a title, two lines of description and a line with the data and a “more” link.</span></span></EM></P><H3 id="toc-hId-935232091"><SPAN>Updated Notifications Design</SPAN></H3><P>The new simplified design of notifications shown on the right in Figure 2 &nbsp;fits better with the overall design of SAP S/4HANA Cloud Public Edition, and provides simplier interactions. By default notifications are sorted by date, with grouping by “Today”, “Yesterday”, “Last Week”, “Last Month”, as you can see in Figure 3 below. Users can also choose to sort by importance.</P><P><SPAN>&nbsp;<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 3: Updated design of notifications. Alt Text: A short video showing the user selecting to display the list of notifications in the groups “Today”, “Yesterday” and “This Week”." style="width: 586px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366050i75971FE7243A08CA/image-size/large?v=v2&amp;px=999" role="button" title="03 Notifications new (5fps).gif" alt="Figure 3: Updated design of notifications. Alt Text: A short video showing the user selecting to display the list of notifications in the groups “Today”, “Yesterday” and “This Week”." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 3: Updated design of notifications. Alt Text: A short video showing the user selecting to display the list of notifications in the groups “Today”, “Yesterday” and “This Week”.</span></span></SPAN><SPAN>&nbsp;</SPAN></P><H3 id="toc-hId-738718586"><SPAN>Recommendation: Check Test Automation and RPA</SPAN></H3><P><STRONG>We advise that you check any test automation and RPA (Robotic Process Automation) you may have</STRONG>, since you may need to make some adjustments – in particular if you access the user menu, for example to log off at the end of a test.</P><P>In case you need more time to do this, you can opt-out of the new shell bar for this release.You can switch it off via “Activate New Features”, Feature “/UI2/SHELL_BAR_TOGGLE”.</P><P><STRONG>Important</STRONG>: do switch it back on before next upgrade! There is no opt-out planned from 2608 onwards.</P><H3 id="toc-hId-542205081">Launch apps directly via Transaction Code</H3><P>Customers have been requesting this for quite a while now, so I am happy to announce that with the 2602 release, users can launch applications directly via the transaction code (t-code).</P><P>This is particularly helpful for those users who have experience with older SAP ERP systems, such as SAP ECC (ERP Central Component), and are used to launching transactions by entering the “t-code” (transaction code) in the SAP GUI for Windows shell bar. To launch a transaction VA01 (Create sales order) in SAP GUI for Windows, users enter “/nVA01” if they want it to open in the same window, and “/oVA01” if they want it to open in a new window. Lower case “/nva01” or “/ova01” also works. Furthermore, users could go to the start page by entering “/n” or “/o”, opening it either in the same window or in a new window respectively.</P><P>We have implemented the same logic in the search field in the launchpad shell bar, but with “/o” now creating a new browser tab rather than opening a new window.</P><P>We have also extended it, so that users can also enter the t-code after “/n” or “/o”. Figure 4 shows what it looks like, and Figure 5 shows a short video of it in action for an example using /nmm03 (Display Material) to open the classic UI and /nF217A (Customer – 360 View) to open a Fiori app.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 4: Launching a classic UI via the transaction code “va01”. Alt Text: The shell header is shown, with the search field showing “All” and the search term /nva01 entered." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366054iACD5C8E75B124946/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="04 T-Code launch va01 example.png" alt="Figure 4: Launching a classic UI via the transaction code “va01”. Alt Text: The shell header is shown, with the search field showing “All” and the search term /nva01 entered." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 4: Launching a classic UI via the transaction code “va01”. Alt Text: The shell header is shown, with the search field showing “All” and the search term /nva01 entered.</span></span></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 5: Launching applications directly via transaction code. Alt Text: A short video showing the user entering /nmm03 and /nF217A as described in the text." style="width: 960px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366055i87CFB7C91CCCFEE4/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="05 T-Code launch demo 2026-01-23 (2fps 50percent).gif" alt="Figure 5: Launching applications directly via transaction code. Alt Text: A short video showing the user entering /nmm03 and /nF217A as described in the text." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 5: Launching applications directly via transaction code. Alt Text: A short video showing the user entering /nmm03 and /nF217A as described in the text.</span></span></P><P>You can determine an application’s t-code via the <EM>About</EM> entry in the launchpad user menu. We have also assigned transaction codes to Fiori apps, they are often identical to the Fiori ID, but not always.</P><P>Read more about it in this blog post:</P><UL><LI><SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/navigate-sap-fiori-launchpad-with-ease-launch-apps-via-transaction-codes/ba-p/14303662" target="_blank">Navigate SAP Fiori launchpad with Ease: Launch Apps via Transaction Codes</A></SPAN>.</LI></UL><H2 id="toc-hId-216608857"><SPAN>Making New Apps Available to Users</SPAN></H2><P>New apps for business users can help them work more effectively, and get their work done more easily – but only if they actually use them! That means that they need to be activated and hence made available to users in the first place.</P><P>When one or more new applications are delivered that replace an existing application, users of the existing application need to be informed that a replacement is available to them. This is handled by the New Experience Switch. This is especially important if the existing application is about to be deprecated.</P><P>The next two sections cover these two topics in more detail: activating new apps, and the New Experience Switch.</P><H3 id="toc-hId-149178071"><SPAN>Activating new apps, since now delivered inactive</SPAN></H3><P><STRONG>With the 2602 release we no longer activate new apps automatically</STRONG>, so as not to disrupt existing users, as well as to ensure that users are not automatically granted authorizations that they should not have.</P><P>In order to activate an application, i.e. a Fiori app or a classic UI, the underlying IAM &nbsp;(Identity and Application Management) apps of type “App Authorization Variant” need to be activated. These particular IAM apps were introduced a while ago in order to allow finer-grained authorizations for applications.</P><P>For example, the <EM>My Inbox</EM> app has a standard view and an expert view. You will only be able to access the expert view if the corresponding IAM app for the expert view has been activated by your administrator. If your administrator has only activated the IAM app for the standard view, then you will be able to access the <EM>My Inbox</EM> app, but only its standard view.</P><P>Since new apps provided by SAP are no longer activated automatically, administrators need to activate the underlying IAM apps of type “App Authorization Variant”. Only then will users be able to find the apps via the launchpad search or in the App Finder, see the corresponding tiles on pages in the launchpad, and assign them as favorites in <EM>My Home</EM>.</P><P>Find out more in the documentation:</P><UL><LI><SPAN><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/53e36b5493804bcdb3f6f14de8b487dd/e2b39fb01689420393931a9eb3f627ec.html?locale=en-US&amp;version=2602.500" target="_blank" rel="noopener noreferrer">Work with IAM Apps (App Authorization Variants)</A></SPAN></LI><LI><SPAN><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/53e36b5493804bcdb3f6f14de8b487dd/61f5a1ac89e04b68aaf755c883f1bbac.html?locale=en-US&amp;version=2602.500" target="_blank" rel="noopener noreferrer">How to Activate or Deactivate IAM Apps (App Authorization Variants)</A></SPAN></LI></UL><H3 id="toc-hId--122566803"><SPAN>New Experience Switch</SPAN></H3><P>The New Experience Switch informs users that a replacement of the application that they have opened is available to them, and offers a toggle in the shell bar to directly switch to the new application. You can see what this looks like in the top left image in Figure 6.</P><P>If the existing, old version of the app is planned to be deprecated and removed in a future release beyond the next one, then the information icon in the shell bar next to the New Version toggle is replaced by a warning icon, as shown in the top right image in Figure 6.</P><P>Finally, if the existing, old version will be removed with the upgrade to the next release, an alert is shown instead of a warning, as shown in the bottom image in Figure 6.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 6: New Experience Switch. Clockwise from top left: Information, Warning and Alert pop-overs. Alt Text: Three screenshots showing the respective popovers, each with a button “Switch to New Version”. The first two include a carousel giving information about the benefits of the new version. The last one include in red “August 2025” as the month when the app is planned to be removed." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366056i5B95FC6B1E0D2568/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="06 New feature switch popovers.jpg" alt="Figure 6: New Experience Switch. Clockwise from top left: Information, Warning and Alert pop-overs. Alt Text: Three screenshots showing the respective popovers, each with a button “Switch to New Version”. The first two include a carousel giving information about the benefits of the new version. The last one include in red “August 2025” as the month when the app is planned to be removed." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 6: New Experience Switch. Clockwise from top left: Information, Warning and Alert pop-overs. Alt Text: Three screenshots showing the respective popovers, each with a button “Switch to New Version”. The first two include a carousel giving information about the benefits of the new version. The last one include in red “August 2025” as the month when the app is planned to be removed.</span></span></P><P>The three phases described above were introduced already with the 2508 release.</P><P>With the 2602 release, we also support cases where one application has multiple successors. Figure 7 shows an example.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 7: New Experience Switch warning for an application with multiple successors. Alt Text: Screenshot showing the popover containing two cards, one for each of the specialized versions available as successors." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366057i1E72911F93EE3AF7/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="07 New feature switch - multiple successors.jpg" alt="Figure 7: New Experience Switch warning for an application with multiple successors. Alt Text: Screenshot showing the popover containing two cards, one for each of the specialized versions available as successors." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 7: New Experience Switch warning for an application with multiple successors. Alt Text: Screenshot showing the popover containing two cards, one for each of the specialized versions available as successors.</span></span></P><P>Also new with 2602 is the option for users to provide feedback when the open up the new version of an application (Figure 8). When they select this, a survey is opened with one page of questions about the new version of the app.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 8: New Experience Switch welcome pop-over for new app, now with “Provide Feedback” button. Alt Text: Screenshot showing the pop-over, with a carousel giving information about the benefits of the new version." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366059i73D3739FFE895672/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="08 New feature switch - welcome with survey.jpg" alt="Figure 8: New Experience Switch welcome pop-over for new app, now with “Provide Feedback” button. Alt Text: Screenshot showing the pop-over, with a carousel giving information about the benefits of the new version." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 8: New Experience Switch welcome pop-over for new app, now with “Provide Feedback” button. Alt Text: Screenshot showing the pop-over, with a carousel giving information about the benefits of the new version.</span></span></P><P>To find out more, have a look at the documentation, or at the SAP note for the list of apps that currently have successors making use of the New Experience Switch:</P><UL><LI>Documentation: <SPAN><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/4fc8d03390c342da8a60f8ee387bca1a/3e8bbaec0b544b52bc4d1c9db2b1d1db.html?locale=en-US&amp;version=2602.500" target="_blank" rel="noopener noreferrer">Switching to a New App Version</A></SPAN></LI><LI>SAP Note <SPAN><A href="https://me.sap.com/notes/3545277" target="_blank" rel="noopener noreferrer">3545277 - New Experience Switch in SAP S/4HANA Cloud Public Edition: Overview of Apps</A></SPAN></LI></UL><H2 id="toc-hId--25677301"><SPAN>Further Improvements for the Launchpad</SPAN></H2><H3 id="toc-hId--515593813"><SPAN>Enterprise Search Shows All Smart Business Tiles</SPAN></H3><P>Business users can now find all their Smart Business tiles (typically insight tiles with KPIs and/or mini charts) using enterprise search in the launchpad shellbar. Since users can define their own tiles via Smart Business, with specific filter values, this means that they can also find these personal tiles using search.</P><P>Did you know that enterprise search supports a number of search operators that you can use when searching for apps, or indeed when searching for business objects? Operators such as OR, *, - and “”.</P><P>You can also further filter search results, export them, share them or save the search as a tile. Find out more in the documentation:</P><UL><LI><SPAN><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/4fc8d03390c342da8a60f8ee387bca1a/03000c73b2474a318bb2e9882d62676a.html?locale=en-US&amp;version=2602.500&amp;parentHref=https://help.sap.com/whats-new/7d3d11840a6543329e72391cf4d48e2d?locale=en-US%26Business_Area=User%2BExperience&amp;parentName=What%27s+New+Viewer+-+SAP+S/4HANA+Cloud+Public+Edition" target="_blank" rel="noopener noreferrer">Searching and Launching Apps or Business Objects</A></SPAN>.</LI></UL><H3 id="toc-hId--712107318"><SPAN>Administrators can Upload and Download Spaces</SPAN></H3><P>In the Manage Launchpad Spaces app of the SAP Fiori launchpad, administrators can download existing spaces together with their assigned pages and upload them again to another system.</P><P>This enables you to transfer spaces and their assigned pages to other systems, for example, when you can't use transports, meaning that you can improve productivity and consistency across systems. Find out more:</P><UL><LI>Documentation: <SPAN><A href="https://help.sap.com/docs/SAP_S4HANA_CLOUD/4fc8d03390c342da8a60f8ee387bca1a/f5055d2bf0054cd89721ff0f290f85df.html?locale=en-US&amp;version=2602.500&amp;parentHref=https://help.sap.com/whats-new/7d3d11840a6543329e72391cf4d48e2d?locale=en-US%26Business_Area=User%2BExperience&amp;parentName=What%27s+New+Viewer+-+SAP+S/4HANA+Cloud+Public+Edition" target="_blank" rel="noopener noreferrer">Downloading and Uploading Spaces</A></SPAN></LI></UL><P>&nbsp;</P><H1 id="toc-hId--321814809"><SPAN>My Home</SPAN></H1><P>We continue to enhance My Home to make it even more popular with users. In this release, we have:</P><UL><LI>Manage News app now with “must be read” feature</LI><LI>Enhanced Personalization</LI><LI>AI-assisted smart personalization of <EM>My Home</EM> for applications<BR />(this is covered in the next section <EM>AI-Assisted UX Innovations</EM>)</LI></UL><P>We are also providing the feature <A href="https://dam.sap.com/mac/u/v/tSpQrWC?rc=10&amp;doi=SAP1206840" target="_blank" rel="noopener noreferrer"><EM>AI-Assisted Smart Solution for Situations in&nbsp;My Home</EM></A> for beta-testing, which I will showcase in part 3 in this series of blog posts.</P><H2 id="toc-hId--811731321"><SPAN>Manage News App now with “Must be Read” Feature</SPAN></H2><P>News articles containing essential company information can now be designated as <STRONG>required reading</STRONG>. All users must acknowledge that they have read and understood the content.</P><P>The main benefits of this feature are:</P><UL><LI><STRONG>Strengthens accountability: </STRONG>When reading is mandatory, employees can be held accountable for understanding essential information. This practice helps ensure compliance with legal, safety, and policy-related requirements.</LI><LI><STRONG>Ensures consistent knowledge across the organization: </STRONG>All employees receive the same information, minimizing knowledge gaps and miscommunication.</LI><LI><STRONG>Enhances change management : </STRONG>During periods of transition—such as the introduction of new policies or process changes—required reading ensures that all employees understand the reasons behind and the impacts of those changes.</LI></UL><P>Figure 9 shows what this looks like for a user: a required reading pop-over appears on their screen. The article has also been flagged as “Containing Critical News”.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 9: An example of a news article that is required reading. Alt Text: A screenshot of My Home with a pop-over “Required Reading” that is also highlighted with “Contains Critical News”. The news article has an image at the top, followed by text showing the first part of the article. The “Done” button is greyed out, the “Remind me later” button is visible." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366060i11FDD06E0BCE8B75/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="09 Required reading (end-user).jpg" alt="Figure 9: An example of a news article that is required reading. Alt Text: A screenshot of My Home with a pop-over “Required Reading” that is also highlighted with “Contains Critical News”. The news article has an image at the top, followed by text showing the first part of the article. The “Done” button is greyed out, the “Remind me later” button is visible." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 9: An example of a news article that is required reading. Alt Text: A screenshot of My Home with a pop-over “Required Reading” that is also highlighted with “Contains Critical News”. The news article has an image at the top, followed by text showing the first part of the article. The “Done” button is greyed out, the “Remind me later” button is visible.</span></span></P><P>Figure 10 below shows the <EM>Manage News</EM> app, with the toggles <EM>Critical</EM> and the new <EM>Required Reading</EM> one.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 10: The Manage News app has a toggle to mark articles as “Required Reading”. Alt Text: Screenshot of the Manage News app with the toggles set to “Yes”, and some news text in the Description field." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366063i14B5515B79AE028D/image-size/large?v=v2&amp;px=999" role="button" title="10 Required reading (admin).jpg" alt="Figure 10: The Manage News app has a toggle to mark articles as “Required Reading”. Alt Text: Screenshot of the Manage News app with the toggles set to “Yes”, and some news text in the Description field." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 10: The Manage News app has a toggle to mark articles as “Required Reading”. Alt Text: Screenshot of the Manage News app with the toggles set to “Yes”, and some news text in the Description field.</span></span></P><H2 id="toc-hId--1008244826"><SPAN>Enhanced Personalization</SPAN></H2><P>A great strength of <EM>My Home</EM> is the degree to which users can personalize it to suit their needs. We have now enhanced personalization further by letting users control the visibility of <EM>News</EM> and <EM>Pages</EM> separately within the <EM>News and Pages</EM> section of <EM>My Home</EM>.</P><P>If they are not so interested in news, they can choose to hide it, with the additional benefit of having all the pages only take up one row of vertical space rather than two, as shown in Figure 11. Figure 12shows how users can do the personalization.</P><P>Note that even with the news hidden, they will still receive required reading: when they log on, the required reading appears as a pop-over that can only be closed by confirming that one has read and understood the content.</P><P>Alternatively, users can decide to only show news and not pages. They can of course also decide to show both news and pages, as before, or hide both.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 11: My Home with enhanced personalization, showing News deselected and Pages selected. Alt Text: A screenshot of My Home, with eight cards in one row in the Pages section, below the ToDos section showing one situation and the Apps section." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366064iFC4C949CF70F70F2/image-size/large?v=v2&amp;px=999" role="button" title="11 My Home pages without news.jpg" alt="Figure 11: My Home with enhanced personalization, showing News deselected and Pages selected. Alt Text: A screenshot of My Home, with eight cards in one row in the Pages section, below the ToDos section showing one situation and the Apps section." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 11: My Home with enhanced personalization, showing News deselected and Pages selected. Alt Text: A screenshot of My Home, with eight cards in one row in the Pages section, below the ToDos section showing one situation and the Apps section.</span></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 12: My Home Settings pop-over with the new selectors for News and Pages within the News and Pages section. Alt Text: A screenshot showing the Layout part of My Home Settings, where users can select or deselect sections. For the News and Pages section, News is deselected, Pages are selected." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366065i25D86015F0EB299E/image-size/large?v=v2&amp;px=999" role="button" title="12 My Home news and pages settings (smaller).jpg" alt="Figure 12: My Home Settings pop-over with the new selectors for News and Pages within the News and Pages section. Alt Text: A screenshot showing the Layout part of My Home Settings, where users can select or deselect sections. For the News and Pages section, News is deselected, Pages are selected." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 12: My Home Settings pop-over with the new selectors for News and Pages within the News and Pages section. Alt Text: A screenshot showing the Layout part of My Home Settings, where users can select or deselect sections. For the News and Pages section, News is deselected, Pages are selected.</span></span></P><P>&nbsp;</P><H1 id="toc-hId--911355324"><SPAN>AI-Assisted User Experience</SPAN></H1><P>When AI is embedded directly into the application UI to give users a better user experience, as opposed to being accessed via the Joule window, we talk about “AI-Assisted User Experience”.</P><P>In SAP S/4HANA Cloud Public Edition 2508, we distinguish between AI base features and premium features:</P><UL><LI><STRONG>Base features</STRONG> are free of charge, and only require that the customer signs the SAP AI Terms, which can be done via <SPAN><A href="https://sapit-iex-prod-lizard.launchpad.cfapps.eu10.hana.ondemand.com/9003c169-62d4-4285-b392-9e939ca5d535.workflowexternal.workflowexternal/preship/index.html#/create/1260" target="_blank" rel="noopener nofollow noreferrer">this link</A></SPAN>.</LI><LI><STRONG>Premium features</STRONG> require SAP AI Units to be in place. Those listed <SPAN><A href="https://discovery-center.cloud.sap/ai-catalog/?packages=baf5fc5e-55d1-4065-9c05-f31bcc3efabb" target="_blank" rel="noopener nofollow noreferrer">here</A></SPAN> also require Joule Premium for Financial Management. Customers can activate this via SAP for Me -&gt; Portfolio &amp; Products -&gt; Business AI.</LI></UL><P>The following are now generally available as premium features (they were both in beta testing with the 2508 release) and are described in more detail below:</P><UL><LI><STRONG>AI-assisted smart personalization of <EM>My Home</EM> for applications.</STRONG></LI><LI><STRONG>AI-assisted error explanation</STRONG>.</LI></UL><P>This feature is still in beta, but planned to become generally available with the 2602 release on March 12th:</P><UL><LI><STRONG>AI-assisted situation handling.<BR /></STRONG>See below for more details.</LI></UL><H2 id="toc-hId--1401271836"><SPAN>AI-Assisted Smart Personalization of My Home for Applications</SPAN></H2><P>You could already use natural language and AI to personalize your My Home by adding Insights Cards, since the 2508 release. Now, with 2602, you can use natural language to find and add applications and insights tiles.</P><P>To do this, use the AI button “Add Content” in the salutation bar at the top of My Home, as shown at the top of Figure X. Clicking on this brings a pop-over with the option to add Insights Cards or, as shown, Apps &amp; Insights Tiles. By describing what you want to do, the system will propose suitable applications and Insights Tiles. If you select them and click “Add”, they will be added, either as an entry in your Apps Favorites section, or in the Insights Tiles section. Figure 13 shows these main steps.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 13: AI-Assisted Smart Personalization of My Home for Applications. Alt Text: The image shows three screenshots: at the top an extract of My Home showing the blue salutation bar, with an AI button “Add Content” on the right. An arrow has been added linking it to the second screenshot, where a popover “Add Content” is shown, where the user has entered “I want to monitor sales orders what app should I use to do this”, and two of four results are listed and visualized: “Track Sales Orders” and “Manage Sales Orders” (shown as an insight tile). Below that a screenshot of the outcome in My Home: the former is added to Apps Favorites, the latter to Insights Tiles." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366066iAE6DD8426E873746/image-size/large?v=v2&amp;px=999" role="button" title="13 AI-Assisted Smart Personalization of My Home for Applications.jpg" alt="Figure 13: AI-Assisted Smart Personalization of My Home for Applications. Alt Text: The image shows three screenshots: at the top an extract of My Home showing the blue salutation bar, with an AI button “Add Content” on the right. An arrow has been added linking it to the second screenshot, where a popover “Add Content” is shown, where the user has entered “I want to monitor sales orders what app should I use to do this”, and two of four results are listed and visualized: “Track Sales Orders” and “Manage Sales Orders” (shown as an insight tile). Below that a screenshot of the outcome in My Home: the former is added to Apps Favorites, the latter to Insights Tiles." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 13: AI-Assisted Smart Personalization of My Home for Applications. Alt Text: The image shows three screenshots: at the top an extract of My Home showing the blue salutation bar, with an AI button “Add Content” on the right. An arrow has been added linking it to the second screenshot, where a popover “Add Content” is shown, where the user has entered “I want to monitor sales orders what app should I use to do this”, and two of four results are listed and visualized: “Track Sales Orders” and “Manage Sales Orders” (shown as an insight tile). Below that a screenshot of the outcome in My Home: the former is added to Apps Favorites, the latter to Insights Tiles.</span></span></P><P><EM>&nbsp;</EM>Watch a demo and read more:</P><UL><LI>Video: <SPAN><A href="https://dam.sap.com/mac/app/p/video/asset/preview/6Lr5YLQ?ltr=a&amp;rc=10&amp;doi=SAP1224308" target="_blank" rel="noopener noreferrer">AI-Assisted Smart Personalization of My Home for Applications in SAP S/4HANA Cloud Public Edition</A></SPAN> (1:07 min.)</LI><LI>SAP Discovery Center: <A href="https://discovery-center.cloud.sap/ai-feature/5205d1ac-b2a1-413b-8d5c-a01e22311cad/" target="_blank" rel="noopener nofollow noreferrer">Smart Personalization of My Home</A>.</LI></UL><H2 id="toc-hId--1597785341"><SPAN>AI-Assisted Error Explanation</SPAN></H2><P>New users who are unfamiliar with the system may find that the short error messages give too little information for them to fully understand the issue, and what to do about it. This is where the <EM>Summarizing</EM> and <EM>Guiding and Recommending</EM> AI-patterns can help: the AI-Assisted Error Explanation in SAP S/4HANA Cloud Public Edition uses these to summarize information which explains the error and provide a recommendation for how to resolve it. Figure 14 shows an example where a user has entered a Sold-to-Party ID which does not exist in the system.</P><P>Note that this feature is also only available for applications based on SAP Fiori elements.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 14: AI-Assisted Error Explanation in SAP S/4HANA Cloud Public Edition 2502. Alt Text: On the left part of a data entry screen is shown, with the field “Sold-to Party containing “ABC” marked red and below that a&nbsp; popover with an error message “No customer master record exists for sold-to-party ABC”, and in the text line below that the AI icon followed by “Generate Explanation”. A fat arrow leads from this text to the image on the right, which indicates what happens when you click on this text: you get a popover with an short Explanation section and a longer Resolution section with numbered actions." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366067iFA60A30B32FC7463/image-size/large?v=v2&amp;px=999" role="button" title="14 S4H AI-Assisted Error Explanation 2502 Beta.jpg" alt="Figure 14: AI-Assisted Error Explanation in SAP S/4HANA Cloud Public Edition 2502. Alt Text: On the left part of a data entry screen is shown, with the field “Sold-to Party containing “ABC” marked red and below that a&nbsp; popover with an error message “No customer master record exists for sold-to-party ABC”, and in the text line below that the AI icon followed by “Generate Explanation”. A fat arrow leads from this text to the image on the right, which indicates what happens when you click on this text: you get a popover with an short Explanation section and a longer Resolution section with numbered actions." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 14: AI-Assisted Error Explanation in SAP S/4HANA Cloud Public Edition 2502. Alt Text: On the left part of a data entry screen is shown, with the field “Sold-to Party containing “ABC” marked red and below that a&nbsp; popover with an error message “No customer master record exists for sold-to-party ABC”, and in the text line below that the AI icon followed by “Generate Explanation”. A fat arrow leads from this text to the image on the right, which indicates what happens when you click on this text: you get a popover with an short Explanation section and a longer Resolution section with numbered actions.</span></span></P><P>Have a look:</P><UL><LI>Video: <SPAN><A href="https://dam.sap.com/mac/app/e/video/embed/rzjZtK1?ltr=a&amp;rc=10&amp;doi=SAP1166518" target="_blank" rel="noopener noreferrer">AI-Assisted Error Explanation</A></SPAN> (45 seconds)</LI><LI>SAP Discovery Center: <A href="https://discovery-center.cloud.sap/ai-feature/a6e21835-397b-402d-be1a-e92846dcc47d/" target="_blank" rel="noopener nofollow noreferrer">SAP S/4HANA Cloud Public Edition, error explanation</A>.</LI></UL><H2 id="toc-hId--1626115155"><SPAN>AI-Assisted Situation Handling</SPAN></H2><P>The <SPAN><A href="https://pages.community.sap.com/topics/intelligent-situation-handling" target="_blank" rel="noopener noreferrer">Intelligent Situation Handling</A></SPAN> framework helps users deal with not-so-frequent business situations, by notifying them of the situation, giving them an overview of the situation and providing contextual information to help resolve it. Ideally, the system also offers recommendations on how to go about resolving it.</P><P>Currently still in beta testing, we plan to make AI-assisted situation handling generally available with the 2602.1 release on March 12th.</P><P>Figure 15 shows what this looks like for a situation where a purchase contract is going to expire soon. The system is able to evaluate the company policy, in this case provided in the form of a decision matrix, and informs the user that a Request for Proposals is not needed for this contract, since its contract value is below the threshold.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 15: AI-Assisted Situation Handling in SAP S/4HANA Cloud Public Edition 2602, showing how the user is notified in My Home and can navigate to the situation page and there generate a recommended solution. ALT Text: The To-Do card “The purchase contract will expire soon” is shown at the top, with an arrow leading to the image below of the situation page, showing a “General Info” section and a “Recommended Solution” section, which only contains the text “You haven’t generated a solution yet. Would you like to do that now”, along with a “Generate” button with the AI icon. From there an arrow leads to the bottom image which shows the recommended solution text, along with thumbs-up and thumbs-down feedback buttons." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366068i13BCC3A2195AED6F/image-size/large?v=v2&amp;px=999" role="button" title="15 Situation Handling GenAI recommendation.jpg" alt="Figure 15: AI-Assisted Situation Handling in SAP S/4HANA Cloud Public Edition 2602, showing how the user is notified in My Home and can navigate to the situation page and there generate a recommended solution. ALT Text: The To-Do card “The purchase contract will expire soon” is shown at the top, with an arrow leading to the image below of the situation page, showing a “General Info” section and a “Recommended Solution” section, which only contains the text “You haven’t generated a solution yet. Would you like to do that now”, along with a “Generate” button with the AI icon. From there an arrow leads to the bottom image which shows the recommended solution text, along with thumbs-up and thumbs-down feedback buttons." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 15: AI-Assisted Situation Handling in SAP S/4HANA Cloud Public Edition 2602, showing how the user is notified in My Home and can navigate to the situation page and there generate a recommended solution. ALT Text: The To-Do card “The purchase contract will expire soon” is shown at the top, with an arrow leading to the image below of the situation page, showing a “General Info” section and a “Recommended Solution” section, which only contains the text “You haven’t generated a solution yet. Would you like to do that now”, along with a “Generate” button with the AI icon. From there an arrow leads to the bottom image which shows the recommended solution text, along with thumbs-up and thumbs-down feedback buttons.</span></span></P><P>To see it in action, watch this:</P><UL><LI>Video: <SPAN><A href="https://dam.sap.com/mac/u/a/rGAEMr9?rc=10&amp;doi=SAP1166593" target="_blank" rel="noopener noreferrer">AI-Assisted Situation Handling in SAP S/4HANA Cloud Public Edition</A></SPAN> (1:00 minutes).</LI><LI>SAP Discover Center: <A href="https://discovery-center.cloud.sap/ai-feature/9a097c87-5da5-4a29-aeeb-0aea4abb132e/" target="_blank" rel="noopener nofollow noreferrer">AI-assisted Situation Handling</A>.</LI></UL><P>The extended framework for Situation Handling allows customers to define their own situations. It is very powerful, but we have received feedback that it can be challenging for first time customers and partners to get it up and running. Hence Angelika Salmen has published this blog post to help you get started:</P><UL><LI><SPAN><A href="https://community.sap.com/t5/technology-blog-posts-by-sap/my-first-situation-with-the-extended-framework/ba-p/14310008" target="_blank">My First Situation with the Extended Framework</A></SPAN>.</LI></UL><P>For further information about Situation Handling, have a look at the links at the end of the above blog post.<SPAN>&nbsp;</SPAN></P><P>&nbsp;</P><H1 id="toc-hId--1529225653">SAP Fiori elements mobile phone optimization</H1><P>We have made significant improvements in the overall user experience for accessing selected applications from your mobile phone, with layouts and interactions tailored specifically for smartphones.</P><P>The recommended way to access SAP Fiori web applications from your mobile phone is via SAP Mobile Start, which treats web applications the same way as native mobile applications: users can define favorites, and see all their apps, both web and native mobile, in the <EM>Apps</EM> view.</P><P>The currently supported SAP Fiori elements based applications are now rendered in a more user-friendly way, reducing complexity for users on the go by removing non-essential features, patterns and services that are not suited for mobile use.</P><P>This results in improved productivity and speed with simplified navigation, faster access to actions and optimized information density.</P><P>The new layout is provided by the mobile mode. Users can switch on the previous desktop mode in case they need access to any of the non-essential features hidden in the mobile mode. Figure 16 shows an example of a list report in the desktop mode (on the left) and what it now looks like rendered in the mobile mode (on the right).</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Figure 16: SAP Fiori elements mobile phone optimization. On the left a list report rendered using the previous desktop mode, on the right using the new mobile mode. Alt Text: screenshots of a list report as it would appear on a mobile phone. On the left with filter bar and filters visible, so that only one line of the list is shown; on the right they are hidden, so that three lines of content are shown." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366069i391FBDF0D14754AB/image-size/large?v=v2&amp;px=999" role="button" title="16 Mobile phone optimization.jpg" alt="Figure 16: SAP Fiori elements mobile phone optimization. On the left a list report rendered using the previous desktop mode, on the right using the new mobile mode. Alt Text: screenshots of a list report as it would appear on a mobile phone. On the left with filter bar and filters visible, so that only one line of the list is shown; on the right they are hidden, so that three lines of content are shown." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Figure 16: SAP Fiori elements mobile phone optimization. On the left a list report rendered using the previous desktop mode, on the right using the new mobile mode. Alt Text: screenshots of a list report as it would appear on a mobile phone. On the left with filter bar and filters visible, so that only one line of the list is shown; on the right they are hidden, so that three lines of content are shown.</span></span></P><P>Currently these five apps are supported:</P><UL><LI>My Home</LI><LI>Customer 360 View (<SPAN><A href="https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps('F2187A')/S35" target="_blank" rel="noopener nofollow noreferrer">F2187A</A></SPAN>)</LI><LI>Manage Direct Activity Allocation&nbsp;<SPAN><A href="https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/index.html#/detail/Apps(%27F3697A%27)/S35" target="_blank" rel="noopener nofollow noreferrer">(F3697A)</A></SPAN></LI><LI>Manage Material Serial Numbers&nbsp;<SPAN><A href="https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/index.html#/detail/Apps(%27F6879%27)/S35" target="_blank" rel="noopener nofollow noreferrer">(F6879)</A></SPAN></LI><LI>Manage Production Supply Areas&nbsp;<SPAN><A href="https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/index.html#/detail/Apps(%27F6935%27)/S35" target="_blank" rel="noopener nofollow noreferrer">(F6935)</A></SPAN></LI><LI>Serial Number History&nbsp;<SPAN><A href="https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/index.html#/detail/Apps(%27F7368%27)/S35" target="_blank" rel="noopener nofollow noreferrer">(F7368)</A></SPAN></LI></UL><P>We are eager to learn from our customers to see how well the current functionality meets users real-life needs, and where we could improve the experience. If you are interested in trying this out and providing feedback, please sign up for our <SPAN><A href="https://influence.sap.com/sap/ino/#campaign/4090" target="_blank" rel="noopener noreferrer">Early Adopter Care program</A></SPAN>.</P><P>&nbsp;</P><H1 id="toc-hId--1725739158"><SPAN>UI Adaptation</SPAN></H1><P>Adapt UI is a key user extensibility tool for making no-code / low-code, upgrade-safe adaptations and extensions to SAP Fiori apps. We have continuously enhanced its scope, so that it now supports most of the common changes.</P><P>A key user is a business expert who is authorized to make changes on behalf of other users – for example a team member who is interested in technology. The goal of key user extensibility is to empower business users to be able to adapt their working environment to suit their needs, without having to rely on system administrators.</P><P>Adapt UI is easy and safe to use: you can undo/redo changes, revert to a previously activated version or even revert to the original app.</P><P>Many Fiori apps contain object pages, that summarize information on a business object such as a sales order or a product. Adapt UI offers a large amount of options for adapting and extending object pages:</P><UL><LI>Hide/show SAP-defined fields</LI><LI>Add custom fields created with the SAP Fiori app Custom Fields (ID F1481)</LI><LI>Combine/split/rearrange/group/ungroup fields</LI><LI>Hide/show SAP-defined tabs, sections, tables, or action buttons</LI><LI>Change the configuration of tables (view settings, initial load, scrolling thresholds etc. – as featured in <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-ux-q3-2025-update-part-2-sap-s-4hana-cloud-public-edition-2508-and-sap/ba-p/14171291" target="_blank">my previous blog post</A>)</LI><LI>Change text arrangement (ID only, Text first, Text las) – also featured in <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-ux-q3-2025-update-part-2-sap-s-4hana-cloud-public-edition-2508-and-sap/ba-p/14171291" target="_blank">my previous blog post</A>.</LI><LI>Rename labels</LI><LI>Hide/show object header content</LI><LI>Embed web-based content in the object header or a section.</LI></UL><P>Key users who are authorized to use adapt UI can open it directly from their user menu in the launchpad. Once in started, Adapt UI shows you directly on the screen what changes you can make.</P><P>To find out more about this and watch a demo showing how it works and how easy it is, I thoroughly recommend this 13-minute video:</P><P><A href="https://learning.sap.com/videos/extending-sap-fiori-object-pages-with-adapt-ui" target="_self" rel="noopener noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Video 1: Extending SAP Fiori Object Pages with Adapt UI. Click on the image to go to the video." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/366238iC4310DEC78944E93/image-size/large?v=v2&amp;px=999" role="button" title="Video 1 (image) - Adapt UI.jpg" alt="Video 1: Extending SAP Fiori Object Pages with Adapt UI. Click on the image to go to the video." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Video 1: Extending SAP Fiori Object Pages with Adapt UI. Click on the image to go to the video.</span></span></A>The video shows you:</P><UL><LI>How to recognize an object page</LI><LI>What is Adapt UI</LI><LI>Prerequisites for Adapt UI</LI><LI>Demo</LI><LI>Tips for Working with Adapt UI</LI><LI>Key Takeaways</LI><LI>Resources worth knowing. These are:<UL><LI>SAP Design System - SAP Fiori:&nbsp;<A href="https://www.sap.com/design-system/fiori-design-web/v1-136/page-types/floorplans/object-page/" target="_blank" rel="noopener noreferrer">Object Page Floorplan</A></LI><LI>SAP Help - SAP Fiori Launchpad User Guide: ​<A href="https://help.sap.com/docs/ABAP_PLATFORM_NEW/a7b390faab1140c087b8926571e942b7/5c424437bf794f809087fdce391149f2.html" target="_blank" rel="noopener noreferrer">Adapting SAP Fiori UIs at Runtime – Key User Adaptation.</A></LI><LI>SAP Learning - Tutorials:&nbsp;<A href="https://developers.sap.com/mission.sapui5-key-user-adaptation.html" target="_blank" rel="noopener noreferrer">Key User Adaptation for SAPUI5 Applications</A>.</LI></UL></LI></UL><P>Note that the video was created for SAP S/4HANA Cloud Private Edition, so the transport step at the end is different in SAP S/4HANA Cloud Public Edition – all the rest is relevant also for public edition, which is why I included it here.</P><P>The Adapt UI functionality is powered by <A href="https://discovery-center.cloud.sap/serviceCatalog/ui5-flexibility-for-key-users?region=all" target="_blank" rel="noopener nofollow noreferrer">SAPUI5 Flexibility</A> technology.</P><P>&nbsp;</P><H1 id="toc-hId--1922252663"><SPAN>Improvements in Details</SPAN></H1><P>User research has shown that it is easier to work with tables that contain hundreds of entries when the tables are implemented as a grid table rather than a responsive table.</P><P>Hence, with the 2602 release, we have looked at apps containing tables that typically have hundreds of entries and have changed the type of table used from a responsive table to a grid table.</P><H2 id="toc-hId-1882798121">For the full list of enhancements, have a look at the What’s New Viewer:</H2><UL><LI><SPAN><A href="https://help.sap.com/whats-new/7d3d11840a6543329e72391cf4d48e2d?locale=en-US&amp;Business_Area=User+Experience" target="_blank" rel="noopener noreferrer">UX in What’s New Viewer for&nbsp; SAP S/4HANA Cloud Public Edition 2602</A></SPAN>.</LI></UL><P>&nbsp;</P><H1 id="toc-hId-1979687623"><SPAN>Continue reading</SPAN></H1><P>I hope you enjoyed this second part, covering what is now generally available with SAP S/4HANA Cloud Public Edition 2602 and SAP Fiori launchpad. Here's a link to part 3, covering exciting new UX innovations available for beta testing with the S/4HANA Cloud Public Edition 2602 release:</P><UL><LI><A class="" href="https://community.sap.com/t5/technology-blog-posts-by-sap/sap-ux-q1-2026-update-part-3-ai-beta-innovations-in-sap-s-4hana-cloud/ba-p/14320120" target="_blank">SAP UX Q1/2026 Update – Part 3: AI (Beta) Innovations in SAP S/4HANA Cloud Public Edition 2602</A>.</LI></UL><P>Do keep posting your experiences and recommendations yourself in the SAP Community, with the SAP Fiori and/or the User Experience tag! In case you are wondering how to get a list of the most recent blog posts on SAP Fiori and User Experience in our new SAP Community, use these links:</P><UL><LI><SPAN><A href="https://community.sap.com/t5/c-khhcw49343/SAP+Fiori/pd-p/73554900100700000977" target="_blank">Latest blog posts on SAP Fiori in the SAP Community</A></SPAN>.</LI><LI><SPAN><A href="https://community.sap.com/t5/c-khhcw49343/User+Experience/pd-p/4616d815-f39e-45c8-b13b-5a2d6679778f" target="_blank">Latest blog posts on User Experience in the SAP Community</A></SPAN>.</LI></UL> 2026-01-28T09:57:22.998000+01:00 https://community.sap.com/t5/technology-blog-posts-by-members/fix-pdfmake-table-width-issues-with-long-continuous-text-and-special/ba-p/14303195 Fix pdfmake Table Width Issues with Long Continuous Text and Special Characters (SAP UI5) 2026-01-29T05:49:14.250000+01:00 DakshKumarSingh https://community.sap.com/t5/user/viewprofilepage/user-id/2203550 <P><STRONG>Introduction</STRONG><BR />I recently hit a really specific edge case while building a PDF feature for our SAP UI5 app. We were using pdfmake, and honestly, everything looked great during standard testing. But then the backend sent over some real-world data—specifically these massive, continuous strings of text—and the whole layout just fell apart.<BR />Even though we had set fixed column widths, the table completely ignored them. The text refused to wrap, spilled right over the borders, and basically ruined the document structure. It’s one of those issues you don't usually catch until you throw messy, non-standard data at your code. In this post, I’ll break down exactly why pdfmake struggles with this and the simple workaround we found to keep the layout intact without changing how the text looks.<BR /><BR /><STRONG><BR />Problem Description<BR /></STRONG><SPAN>We get a specific edge case while building a PDF feature in our SAP UI5 app using pdfmake(pdf playground). Everything looked fine until the backend sent over a massive string of text, which is&nbsp; mix of special characters. Even though we had fixed-width columns, the table ignored them—text wouldn't wrap, it spilled over the cell borders, and basically broke the whole layout</SPAN>.<BR /><BR /><SPAN>The issue became easier to reproduce when the input text had:<BR />&nbsp; &nbsp; &nbsp; &nbsp; Very long continuous strings<BR />&nbsp; &nbsp; &nbsp; &nbsp; No spaces<BR />&nbsp; &nbsp; &nbsp; &nbsp; A lot of special characters<BR /></SPAN></P><P><SPAN>Example:<BR /><A href="mailto:ssssssssss@@@@@@@@@@@@@@@uuuuuuuuuuuuuiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" target="_blank" rel="noopener nofollow noreferrer">ssssssssss@@@@@@@@@@@@@@@uuuuuuuuuuuuuiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii</A><BR /><BR />Even&nbsp; noWrap: false was already set, it didn’t help.&nbsp;<BR /><BR />The reason became evident after studying the text analyzing logic of pdfmake.</SPAN><SPAN><BR /><BR /></SPAN><SPAN>When the text consists of a single, continuous word, pdfmake determines the whole length of the string from considering it as a single, unbreakable unit.<BR /></SPAN><SPAN><BR /></SPAN><SPAN>Both of the font issue and the PDFmake bug aren't the root cause of this. Wrap points can't be put into continuous text; additionally fonts like Roboto or NotoSans only aid in character rendering.</SPAN><SPAN><BR /><BR /></SPAN><SPAN>This will fix when we insert invisible wrap points into the string before sending it to pdfmake.<BR /><BR /></SPAN><SPAN>We used a zero-width space character that is (\u200B), which is invisible for human eye but allows PDF engines to wrap text safely</SPAN></P><P><STRONG>Code:</STRONG></P><pre class="lia-code-sample language-javascript"><code>function safeBreak(str) { return (str || '').replace(/(.{1})/g, "$1\u200B"); }</code></pre><P>&nbsp;</P><P><SPAN>This will not change the text looks in the PDF, but it gives pdfmake to break point to respect the table width and wrap content correctly.</SPAN></P><P><SPAN>The user can view the PDF in exactly the same manner. So as for pdfmake is properly wrap the text so the table width, this simply adds a few break points.</SPAN></P><P>This turned out to be a <SPAN>invisible (for human eye)&nbsp;</SPAN>edge case when generating PDFs with dynamic data. Adding zero-width spaces solved the problem without affecting visible output.</P><P><BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-01-13 135444.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361050i89E24DC44ABB54B5/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Screenshot 2026-01-13 135444.png" alt="Screenshot 2026-01-13 135444.png" /></span><BR />Before Applying the Fix<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sun Life Form (2)_page-0001.jpg" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361054iF45B86E68E31DD75/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="Sun Life Form (2)_page-0001.jpg" alt="Sun Life Form (2)_page-0001.jpg" /></span><BR /><BR />After Applying the Fix<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="2 (2).jpg" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/361057i664CE5743F8F4F0E/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="2 (2).jpg" alt="2 (2).jpg" /></span><BR /><STRONG>Try the Working Demo</STRONG><BR /><SPAN>If you want to see how this project, we put together a complete SAP UI5 sample app. It covers the basic pdfmake setup and shows exactly how to handle those long strings inside table cells so they don't break your layout. You can check out the code&nbsp; here:&nbsp;</SPAN><SPAN>&nbsp;</SPAN><A href="https://github.com/dakshrajput100/PDF_SAP" target="_blank" rel="noopener nofollow noreferrer">PDF_SAP</A><SPAN>&nbsp;<BR /><BR /></SPAN></P><P><STRONG>Conclusion</STRONG><BR />Long, unbroken strings are definitely one of those hidden edges when you're generating PDFs with dynamic data. Since pdfmake needs explicit breakpoints to know when to wrap a line, it will break your table layout if it doesn't find them.<BR />We went with the zero-width space (\u200B) in the end because, frankly, it just works. It makes the text wrap properly so the table widths doesn't get destroyed, but visually, you can't tell the difference, this annoying bug can be easily and safely fixed.</P><P><SPAN>&nbsp;</SPAN></P> 2026-01-29T05:49:14.250000+01:00 https://community.sap.com/t5/community-corner-blog-posts/ask-me-anything-with-sap-champions-monthly-series-session-1/ba-p/14310542 Ask Me Anything with SAP Champions – Monthly Series (Session 1) 2026-02-02T08:07:05.474000+01:00 smarchesini https://community.sap.com/t5/user/viewprofilepage/user-id/125739 <P><STRONG>Hello Community,</STRONG></P><P>My name is <STRONG>Sebastiano Marchesini</STRONG>, SAP Champion and SAP BTP architect. I moved from Italy to the US and currently work as a technology lead consultant, focusing on SAP BTP and enterprise architectures. I also love <A href="https://community.sap.com/t5/community-corner-blog-posts/sap-community-hobbies-series-from-stress-to-strength-running-as-my-lifelong/ba-p/14233040" target="_self">running</A> <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></P><P>From my experience at <STRONG>SAP TechEd</STRONG>, learning sessions, and live events, the most valuable moments are always the same:</P><H3 id="toc-hId-1917624771"><STRONG>Asking questions, any kind of question, and getting direct, honest answers from experts.</STRONG></H3><P>For this reason, the <STRONG>SAP Champions are launching a new monthly AMA (Ask Me Anything) series starting in 2026</STRONG>.</P><P>Each month, a <STRONG>different SAP Champion with a specific area of expertise</STRONG> (BTP, architecture, integration, development, automation, career growth, and more) will answer questions from the community, both <STRONG>technical and non-technical</STRONG>.</P><P>This blog post is the <STRONG>first of the series</STRONG>.</P><P>You are invited to ask <STRONG>any questions you have</STRONG>, openly or <STRONG>anonymously</STRONG>, using the form below.<BR />No topic is off-limits: architecture doubts, product choices, real-life SAP challenges, or even questions about working in the SAP ecosystem, running (lol).</P><P>Let’s make this a space for <STRONG>open discussion, real experience, and practical answers</STRONG>.<BR /><BR />You can ask anonymously here</P><H3 id="toc-hId-1721111266"><BR /><A href="https://forms.office.com/e/w1ruFctkQQ" target="_blank" rel="noopener nofollow noreferrer">AMA Champion Anonymously Questions</A></H3><P>(or below as anwer of the blog)&nbsp;</P><P><EM>Submit your questions by early February and get answers in our upcoming SAP Communities video and blog recap.</EM></P><P><BR /><BR />This AMA session will be open for questions until <STRONG>the last week of February</STRONG>.<BR /><BR />After that, selected questions will be answered in a <STRONG>recorded video published on the SAP Communities YouTube channel</STRONG>.<BR />A <STRONG>blog recap</STRONG> summarizing the questions and answers will also be published for the community.<BR /><BR /></P><BLOCKQUOTE><P>Only questions submitted during this window will be considered.</P></BLOCKQUOTE><P><BR /><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="ChatGPT Image Jan 19, 2026, 07_18_52 PM.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/363094i6FCAF1CA3465F544/image-size/large/is-moderation-mode/true?v=v2&amp;px=999" role="button" title="ChatGPT Image Jan 19, 2026, 07_18_52 PM.png" alt="ChatGPT Image Jan 19, 2026, 07_18_52 PM.png" /></span></P><P>&nbsp;</P> 2026-02-02T08:07:05.474000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/declarative-ui-integration-cards-consistent-framework-with-flexible-content/ba-p/14320024 Declarative UI Integration Cards - Consistent Framework With Flexible Content Capabilities 2026-02-02T16:34:17.547000+01:00 daniel_vladinov https://community.sap.com/t5/user/viewprofilepage/user-id/198422 <H1 id="toc-hId-1660377989">Introduction</H1><P>Since the early announcement of the UI Integration Cards paradigm (as described in <A href="https://community.sap.com/t5/technology-blog-posts-by-sap/integration-cards/ba-p/13419040" target="_blank">this article</A>), we have been busy expanding the functionality of Cards, resulting in an ever-growing adoption across almost all SAP products.</P><P>Today, Cards are utilized in a range of solutions and scenarios, from the SAP S/4HANA home page with “To-Do” and Insights Cards, to SAP Build Work Zone, where Cards from any SAP&nbsp;product can be easily assembled together, to Joule, and also on mobile-native screens.&nbsp;</P><P>&nbsp;</P><TABLE border="1" width="100%"><TBODY><TR><TD width="25%"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_2-1770042983149.png" style="width: 152px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368153i8F551055D0A6A195/image-dimensions/152x305?v=v2" width="152" height="305" role="button" title="daniel_vladinov_2-1770042983149.png" alt="daniel_vladinov_2-1770042983149.png" /></span></TD><TD width="25%"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_4-1770043358952.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368156iECCDF6A08408B795/image-size/medium?v=v2&amp;px=400" role="button" title="daniel_vladinov_4-1770043358952.png" alt="daniel_vladinov_4-1770043358952.png" /></span><P>&nbsp;</P></TD><TD width="50%"><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_3-1770042997774.png" style="width: 367px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368154i80C90A47DAE08235/image-dimensions/367x379?v=v2" width="367" height="379" role="button" title="daniel_vladinov_3-1770042997774.png" alt="daniel_vladinov_3-1770042997774.png" /></span></TD></TR></TBODY></TABLE><P>&nbsp; &nbsp; &nbsp;&nbsp;</P><P>The fundamental principles that underpin this technology, as detailed on our <A href="https://ui5.sap.com/test-resources/sap/ui/integration/demokit/cardExplorer/webapp/index.html" target="_blank" rel="noopener noreferrer">Card Explorer landing page</A>, are that it is designed to be Declarative, Easy to consume, and Consistent.</P><OL><LI><STRONG>Declarative</STRONG>: configured by&nbsp;a&nbsp;manifest.json&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ==&gt;<STRONG> Low to No-code skills required </STRONG>(AI-gen@<EM>UI5 MCP server</EM>)</LI><LI><STRONG>Easy to consume</STRONG>: product and UI-technology agnostic&nbsp; &nbsp; &nbsp; ==&gt;<STRONG>&nbsp;</STRONG><STRONG>Mobile-ready /</STRONG> <STRONG>Integration-ready via configuration</STRONG></LI><LI><STRONG>Consistent</STRONG>: <U>predefined</U> card types in Fiori/Horizon design ==&gt;&nbsp;<STRONG>Thoughtfully designed / Product Standards covered</STRONG></LI></OL><H2 id="toc-hId-1592947203">Key Concepts and Capabilities</H2><P>In this article, we’ll detail the key qualities and advantages of the Declarative Integration Cards. Declarative Card types offer a range of display options and minimalist interactions, including List Card, Table Card, Timeline, Calendar, Analytical, and Object Card with input elements.</P><P>Declarative Card types enable the fast and easy creation of “Home page” and “Overview” pages, where Card developers can integrate data from various sources to display relevant business data for any SAP or non-SAP product.</P><H2 id="toc-hId-1396433698">Declarative and Consistent</H2><P>Integration Cards are straightforwardly defined using a single JSON descriptor file called manifest.json. This file is all you need to get a fully functional and nicely rendered representation of business data in the form of a Card. Once you have this descriptive manifest.json file, our Card Runtime can process it and render the card in a web browser or any mobile-native device, regardless of the UI framework. Hence – declarative!&nbsp;&nbsp;</P><H1 id="toc-hId-1070837474">Anatomy of Declarative UI Integration Cards</H1><P>Examining the Card's structure, we can identify three separate parts:<span class="lia-inline-image-display-wrapper lia-image-align-right" image-alt="daniel_vladinov_5-1770043508459.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368157i6E95926A72837282/image-size/medium?v=v2&amp;px=400" role="button" title="daniel_vladinov_5-1770043508459.png" alt="daniel_vladinov_5-1770043508459.png" /></span></P><P>The <STRONG>Card Header</STRONG> provides the card's identity, typically represented by a title, subtitle, and icon or avatar/image attributes. It can also display more dynamic or complex data, such as the special Numeric Header, which prominently features a key numeric indicator, or an extended Header.</P><UL><LI>The <STRONG>Content / Data</STRONG> is the main part of the card that displays relevant business data in a selected type of view. These data types can be List, Table, Object, Analytical, Timeline, or Calendar.</LI><LI>The <STRONG>Footer area</STRONG> is usually designated for intended Card interaction elements, such as action buttons or links for navigation.</LI></UL><P>The general structure of a <STRONG>declarative manifest.json</STRONG> looks like this:&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-json"><code>{ "sap.app": { "id": "my.card.id", "type": "card", "applicationVersion": { "version": "1.0.0" } }, "sap.card": { "type": "List", // Card type: List, Table, Object, Analytical, Timeline, Calendar "header": { // Header configuration }, "content": { // Content configuration specific to card type }, "footer": { // Optional footer with actions } } }</code></pre><P>&nbsp;</P><H2 id="toc-hId-1003406688">The Card Header</H2><P>The card header displays essential information about the card by grouping a set of card attributes and presenting them at the top of the card. The "sap.card"/header section is mandatory. Providing it together with the title is essential for proper screen reader support and other functionalities.</P><P>Integration Cards support three header configurations:</P><OL><LI><STRONG>Default Header</STRONG> - Standard header with title, subtitle, icon, status</LI><LI><STRONG>Numeric Header</STRONG> - KPI-focused header with prominent numeric indicators</LI><LI><STRONG>Extended Header</STRONG> <STRONG>with Info Section </STRONG>- flexible layout within either kind of header, capable of accommodating various components</LI></OL><P>All headers support actions, data timestamps, visibility control, and text wrapping options.&nbsp;</P><H3 id="toc-hId-935975902">Default Header</H3><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_6-1770043609378.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368158iE9A8F29F11E12CD2/image-size/medium?v=v2&amp;px=400" role="button" title="daniel_vladinov_6-1770043609378.png" alt="daniel_vladinov_6-1770043609378.png" /></span></P><P>The standard header displays general information about the card.&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-json"><code>"header": { "title": "An example title", "subtitle": "Some subtitle", "icon": { "src": "sap-icon://business-objects-experience", "visible": true }, "status": { "text": "5 of 20" } }</code></pre><P>&nbsp;</P><H3 id="toc-hId-739462397"><BR /><SPAN>Numeric Header</SPAN></H3><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_7-1770043791254.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368159iA5A1EB4D0FBF729D/image-size/medium?v=v2&amp;px=400" role="button" title="daniel_vladinov_7-1770043791254.png" alt="daniel_vladinov_7-1770043791254.png" /></span></P><P>The Numeric Header is optimized for displaying KPIs, metrics, and quantitative data with prominent visualization.&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-json"><code>"header": { "type": "Numeric", "title": "Project Cloud Transformation", "subtitle": "Revenue", "unitOfMeasurement": "EUR", "mainIndicator": { "number": "44", "unit": "%", "trend": "Down", "state": "Critical" }, "details": "Some details", "sideIndicators": [ { "title": "Target", "number": "17", "unit": "%" }, { "title": "Deviation", "number": "5", "unit": "%" } ] }</code></pre><P>&nbsp;</P><H3 id="toc-hId-542948892"><BR />Extended Header with Info Section</H3><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_8-1770044248795.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368160i43FEB42446CDA075/image-size/medium?v=v2&amp;px=400" role="button" title="daniel_vladinov_8-1770044248795.png" alt="daniel_vladinov_8-1770044248795.png" /></span></P><P>Both header types can be extended with an info section for additional grouped information.</P><P>Currently, only items of type "Status" are available.</P><P>&nbsp;</P><pre class="lia-code-sample language-json"><code>"header": { "title": "Project Cloud Transformation", "subtitle": "Revenue", "infoSection": { "rows": [ { "items": [ { "type": "Status", "value": "On Track", "state": "Success", "inverted": true, "showStateIcon": true }, { "type": "Status", "value": "OKR Relevant", "state": "None", "inverted": true, "showStateIcon": true }, … ] } ] } }</code></pre><P>&nbsp;</P><H2 id="toc-hId-217352668">The Card Content Area</H2><P>The Card content area defines how data is rendered by selecting a declarative card type, each offering predefined structures and configuration options to visualize business information in a consistent and self-contained way.</P><UL><LI><STRONG>List Card</STRONG> – Displays multiple items in a vertical list with flexible layouts to present titles, descriptions, and multiple attributes per item.</LI><LI><STRONG>Table Card</STRONG> – Visualizes structured data in rows and columns with configurable formatting, alignment, grouping, and item count to support clear data comparison and summarization.</LI><LI><STRONG>Object Card</STRONG> – Presents information about a single object in configurable groups, supporting rich attribute types, visibility control, and consistent usage across SAP products.</LI><LI><STRONG>Analytical Card</STRONG> – Displays analytical business data using chart visualizations with defined dimensions to ensure accurate and meaningful insight representation.</LI><LI><STRONG>Timeline Card</STRONG> – Shows time-related information as a sequence of events arranged chronologically with clear visual flow and contextual details.</LI><LI><STRONG>Calendar Card</STRONG> – Displays agenda or timetable data for an entity with interactive date selection, semantic legends, and dynamic data updates.</LI></UL><P>Stay tuned for future articles with details for every Card type.&nbsp;</P><H2 id="toc-hId-20839163">The Card Footer</H2><P>The card footer, located at the bottom of the card, is used for important or routine actions that directly impact the card functionality, such as "Approve" or "Submit" actions. It serves as the designated area where users can interact with or respond to the card's content through an Actions Strip—a flexible and responsive overflow toolbar that accommodates a variety of interactive elements.<BR /><BR /></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_0-1770046814495.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/368179iE5301E8C971F711B/image-size/medium?v=v2&amp;px=400" role="button" title="daniel_vladinov_0-1770046814495.png" alt="daniel_vladinov_0-1770046814495.png" /></span></P><P>The Actions Strip supports diverse interaction patterns through three primary element types: Buttons for actionable operations, Links for navigation purposes, and Labels for displaying contextual information. Additionally, ToolbarSpacer elements provide precise control over the visual arrangement of actions, enabling left-aligned, right-aligned, or centered positioning within the footer.</P><P>Each action element can be extensively configured to match specific use cases. Buttons offer multiple visual styles (Accept, Reject, Transparent, Emphasized) and can display either text, icons, or both—with the ability to prefer icon-only display in space-constrained layouts. All elements support standard properties including tooltips, visibility conditions, accessibility attributes, and overflow behavior through <STRONG><FONT face="courier new,courier">overflowPriority</FONT></STRONG> settings (<STRONG><FONT face="courier new,courier">High, Low, NeverOverflow, AlwaysOverflow</FONT></STRONG>), which determine how actions adapt to different screen sizes.</P><P>Actions themselves can be of various types: Navigation actions for opening URLs or triggering deep links, Submit actions for posting data to backend services, Custom actions that invoke extension methods. Each action can be configured with parameters and enabled/disabled states, often bound to dynamic data for context-sensitive behavior.</P><P>Here's an example demonstrating the versatility of footer configurations:</P><P>&nbsp;</P><pre class="lia-code-sample language-json"><code>"footer": { "actionsStrip": [ { "type": "Link", "text": "View Details", "icon": "sap-icon://detail-view", "actions": [ { "type": "Navigation", "parameters": { "url": "{detailsUrl}" } } ] }, { "type": "ToolbarSpacer" }, { "text": "Approve", "buttonType": "Accept", "overflowPriority": "High", "actions": [ { "type": "Submit", "url": "/api/approve", "method": "POST" } ] }, { "buttonType": "Transparent", "icon": "sap-icon://email", "preferIcon": true, "text": "Contact", "tooltip": "Send an email", "actions": [ { "type": "Navigation", "parameters": { "url": "mailto:{company/email}?subject={company/emailSubject}" } } ] } ] }</code></pre><P>&nbsp;</P><P>Beyond the Actions Strip, the footer supports additional built-in features that enhance usability. It provides built-in pagination/scrolling support through the "Show More" button, which elegantly handles content overflow by opening cards in an resizable Dialog view. When cards are displayed within dialogs, the footer automatically manages a "Close" button, streamlining the user experience without requiring explicit configuration. This is achieved by defining <STRONG><FONT face="courier new,courier">paginator</FONT> </STRONG>element in the&nbsp;<STRONG><FONT face="courier new,courier">footer</FONT></STRONG> declaration, which automatically will show an extra "Show More" button, next to custom-defined actions. See it in action in <A href="https://ui5.sap.com/test-resources/sap/ui/integration/demokit/cardExplorer/webapp/index.html#/explore/pagination/clientactions" target="_blank" rel="noopener noreferrer">this sample</A>.&nbsp;</P><pre class="lia-code-sample language-json"><code> "footer": { "paginator": { "totalCount": "{/itemsCount}", "pageSize": 5 } }</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="daniel_vladinov_0-1770282293533.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/369120i2A77D15C9DD94F30/image-size/medium?v=v2&amp;px=400" role="button" title="daniel_vladinov_0-1770282293533.png" alt="daniel_vladinov_0-1770282293533.png" /></span></P><P>&nbsp;</P><P>The footer's visibility can be controlled dynamically through binding expressions, allowing it to appear only when specific conditions are met:&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-json"><code>"footer": { "visible": "{= ${/itemsCount} &gt; 0}", "actionsStrip": [ /* actions configuration */ ] }</code></pre><P>&nbsp;</P><H1 id="toc-hId-464983022">Final Thoughts</H1><P>You can use Declarative UI Integration Cards as an entry point to an app or if you want the user to focus on a single object, topic, or group of objects.</P><P>Avoid building complex, small apps within the card that require navigation flows. Instead of being complex or multifunctional, each card is designed to serve a specific role or display business data in a clear manner.</P><P>Clarity and simplicity are prioritized in Declarative UI Integration Cards, enabling users to easily understand and integrate the cards without unnecessary complexity or ambiguity.</P><P>The architecture's three-part structure—header, content, and footer—establishes a predictable framework that accelerates development while ensuring consistency across diverse use cases. This structured approach not only simplifies the creation process but also enhances the end-user experience through familiar, intuitive patterns that work seamlessly across all SAP solutions.</P><P>By embracing the declarative paradigm, developers can rapidly compose powerful, data-driven cards without deep technical expertise, while maintaining the enterprise-grade quality and accessibility standards that users expect. The result is a flexible, scalable solution that transforms how business information is surfaced and acted upon across the digital workplace.</P><P>&nbsp;</P> 2026-02-02T16:34:17.547000+01:00 https://community.sap.com/t5/frontend-ui5-sap-fiori-blog-posts/reading-excel-and-displaying-data-in-sapui5-tables-static-vs-dynamic/ba-p/14314993 Reading Excel and Displaying Data in SAPUI5 Tables – Static vs Dynamic Columns 2026-02-04T10:37:07.641000+01:00 Hari_Kumar_Talari https://community.sap.com/t5/user/viewprofilepage/user-id/178384 <P><SPAN>Hi,&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>While working on SAPUI5 application,&nbsp;i&nbsp;came across a common business requirement where user needed to&nbsp;upload&nbsp;excel files and review the data before further processing&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>SAPUI5 does not provide built in support for reading excel files on the client side, so&nbsp;i&nbsp;explored a&nbsp;client side&nbsp;solution&nbsp;that allows users to upload an excel, reads content and display it in the UI</SPAN><SPAN>&nbsp;<BR /><BR /><STRONG>Introduction</STRONG><BR /></SPAN><SPAN>&nbsp;In many UI5 applications there will be a common scenario to allow users to upload Excel files and review the data before proceeding with further business process. since SAPUI5 does not provide built in support for reading excel files, developers need to rely on external libraries. So in this blog i will read excel data using xlsx library and display it in the SAPUI5 table.</SPAN></P><P><STRONG><SPAN>Pre-requisites</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><OL><LI><SPAN>Basic knowledge of SAPUI5</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>Sapui5 project setup</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>Xlsx.js file should be added in compinent.js file</SPAN><SPAN>&nbsp;</SPAN></LI></OL><P><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Component.js file</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-javascript"><code>var jQueryScript = document.createElement('script'); jQueryScript.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.10.0/jszip.js'); document.head.appendChild(jQueryScript); var jQueryScript = document.createElement('script'); jQueryScript.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.10.0/xlsx.js'); document.head.appendChild(jQueryScript);</code></pre><P><STRONG><SPAN>What we are going to do here:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><OL><LI><SPAN>Upload excel file using&nbsp;FileUploader&nbsp;</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>Read the file using&nbsp;javascript&nbsp;fileReader</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>Excel file will be parsed using XLSX library</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>Extract the sheet from workbook</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>Will convert sheet data into&nbsp;json&nbsp;format</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>We will set&nbsp;json&nbsp;data to JSON Model</SPAN><SPAN>&nbsp;</SPAN></LI></OL><OL><LI><SPAN>We will bind&nbsp;json&nbsp;model to table in the UI</SPAN><SPAN>&nbsp;</SPAN></LI></OL><P><STRONG>Note: the column names in the excel should be same as the properties used in the SAPUI5 table&nbsp;</STRONG></P><P><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Controller.js File</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><DIV><DIV><SPAN><SPAN><SPAN>&nbsp;&nbsp;</SPAN></SPAN></SPAN></DIV></DIV><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/m/Column", "sap/m/Text", "sap/m/ColumnListItem" ], /** * {typeof sap.ui.core.mvc.Controller} Controller */ function (Controller, MessageToast, JSONModel, Column, Text, ColumnListItem) { "use strict"; return Controller.extend("project1.controller.View1", { onInit: function () { }, onUpload:function(oEvent){ debugger var file = oEvent.getParameter("files")[0] var that = this var reader = new FileReader() reader.onload= function(e){ debugger var data = e.target.result; var workbook = XLSX.read(data, { type: "binary" }); // Take first sheet var sheetName = workbook.SheetNames[0]; var sheet = workbook.Sheets[sheetName]; // Convert to JSON var jsonData = XLSX.utils.sheet_to_json(sheet); // Get your existing model from manifest.json var oModel = that.getView().getModel("empModel"); // Replace empDetails with Excel data oModel.setProperty("/empDetails", jsonData); } reader.readAsBinaryString(file); }, }); });</code></pre><P>&nbsp;</P><P><STRONG><SPAN>XML View</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="project1.controller.View1" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns:unified="sap.ui.unified" xmlns="sap.m"&gt; &lt;Page id="page" title="{i18n&gt;title}"&gt; &lt;content&gt; &lt;unified:FileUploader fileType="XLSX,xlsx" change="onUpload" id="FileUploaderId" sameFilenameAllowed="true" iconOnly="false" buttonOnly="true" icon="sap-icon://upload" iconFirst="true" style="Emphasized" &gt; &lt;/unified:FileUploader&gt; &lt;!-- &lt;Button text="Upload" press="onPress" &gt;&lt;/Button&gt; --&gt; &lt;Table items="{empModel&gt;/empDetails}"&gt; &lt;columns&gt; &lt;Column&gt; &lt;Text text="Name" /&gt; &lt;/Column&gt; &lt;Column&gt; &lt;Text text="Location" /&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="Age"/&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="Salary"/&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="EmployeeID"/&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="Nationality"/&gt; &lt;/Column&gt; &lt;/columns&gt; &lt;items&gt; &lt;ColumnListItem&gt; &lt;cells&gt; &lt;Text text="{empModel&gt;Name}"&gt;&lt;/Text&gt; &lt;Text text="{empModel&gt;Location}"&gt;&lt;/Text&gt; &lt;Text text="{empModel&gt;Age}"&gt;&lt;/Text&gt; &lt;Text text="{empModel&gt;Salary}"&gt;&lt;/Text&gt; &lt;Text text="{empModel&gt;EmployeeID}"&gt;&lt;/Text&gt; &lt;Text text="{empModel&gt;Nationality}"&gt;&lt;/Text&gt; &lt;/cells&gt; &lt;/ColumnListItem&gt; &lt;/items&gt; &lt;/Table&gt; &lt;!-- &lt;Table id="idTable" inset="false"/&gt; --&gt; &lt;/content&gt; &lt;/Page&gt; &lt;/mvc:View&gt;</code></pre><P>&nbsp;</P><P><STRONG><SPAN>Output</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-02-02 133036.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367885iBEC893DE71C1A729/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-02-02 133036.png" alt="Screenshot 2026-02-02 133036.png" /></span></SPAN></P><P><STRONG><SPAN>Dynamic&nbsp;excel&nbsp;upload and table generation in UI5</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>In above approach, that solution works only when the excel file&nbsp;contains&nbsp;fixed and known column&nbsp;names</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>In real scenarios, excels may have different structure, may have different column names, and may have&nbsp;different number of rows</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>So&nbsp;to handle that situation</SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Read excel data dynamically</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Extract columns headers from excel</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Will create UI5 columns at run-time</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Then bind table rows dynamically</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Controller.js file&nbsp;</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Step1:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_0-1769514545499.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365786iA38A91AD2BD79132/image-size/medium?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_0-1769514545499.png" alt="Hari_Kumar_Talari_0-1769514545499.png" /></span></P><P>&nbsp;</P><UL><LI><SPAN>Fetch the uploaded excel file</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Initializes&nbsp;Filereader&nbsp;to read file content</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step2:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_1-1769514569557.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365787iB969EF8FA7CF7F93/image-size/medium?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_1-1769514569557.png" alt="Hari_Kumar_Talari_1-1769514569557.png" /></span></P><UL><LI><SPAN>Conver binary excel data into a workbook object</SPAN><SPAN>&nbsp;<BR /></SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step3:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_2-1769514583333.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365788i058F083A69A09E76/image-size/medium?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_2-1769514583333.png" alt="Hari_Kumar_Talari_2-1769514583333.png" /></span></P><UL><LI><SPAN>Always reads the first sheet from excel</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step4:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_3-1769514608118.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365789iDAD0921897ECAFDF/image-size/medium?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_3-1769514608118.png" alt="Hari_Kumar_Talari_3-1769514608118.png" /></span></P><UL><LI><SPAN>Converts excel rows into&nbsp;json&nbsp;objects</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Default&nbsp;</SPAN></STRONG><SPAN>-&gt; ensures empty cells are not skipped</SPAN><SPAN>&nbsp;<BR /></SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step5:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_4-1769514621121.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365790iF9D56A541BB009FC/image-size/medium?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_4-1769514621121.png" alt="Hari_Kumar_Talari_4-1769514621121.png" /></span></P><UL><LI><SPAN>Extract&nbsp;cloumns&nbsp;names from excel first row</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>These headers will used to create table columns</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step6:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_5-1769514664755.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365792i2192A2FAC0F74F23/image-size/medium?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_5-1769514664755.png" alt="Hari_Kumar_Talari_5-1769514664755.png" /></span></P><UL><LI><SPAN>Clears the previously created columns before adding new ones</SPAN><SPAN>&nbsp;<BR /></SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step7:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_6-1769514681529.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365793iDFFB3A687E39F454/image-size/medium?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_6-1769514681529.png" alt="Hari_Kumar_Talari_6-1769514681529.png" /></span></P><UL><LI><SPAN>Iterated through headers</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Creates&nbsp;Coulmns&nbsp;dynamically for each excel columns</SPAN><SPAN>&nbsp;<BR /></SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step8:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_7-1769514779793.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365795i20772C849848091C/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_7-1769514779793.png" alt="Hari_Kumar_Talari_7-1769514779793.png" /></span></P><UL><LI><SPAN>Stores excel data in the model</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Enables data binding to table rows</SPAN><SPAN>&nbsp;<BR /></SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step9:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_8-1769514816747.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365796i37056661E68E848F/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_8-1769514816747.png" alt="Hari_Kumar_Talari_8-1769514816747.png" /></span></P><UL><LI><SPAN>Creates cells dynamically&nbsp;</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Each cell will bind to corresponding excel fields</SPAN><SPAN>&nbsp;<BR /></SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>Step10:</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Hari_Kumar_Talari_9-1769514852871.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/365798i9537A50B5E10068C/image-size/medium/is-moderation-mode/true?v=v2&amp;px=400" role="button" title="Hari_Kumar_Talari_9-1769514852871.png" alt="Hari_Kumar_Talari_9-1769514852871.png" /></span></P><UL><LI><SPAN>Binds excel rows to table</SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><SPAN>Tables&nbsp;renders&nbsp;automatically with dynamic structure</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><STRONG><SPAN>&nbsp;Controller Code</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "sap/ui/core/mvc/Controller", "sap/m/MessageToast", "sap/ui/model/json/JSONModel", "sap/m/Column", "sap/m/Text", "sap/m/ColumnListItem" ], /** * {typeof sap.ui.core.mvc.Controller} Controller */ function (Controller, MessageToast, JSONModel, Column, Text, ColumnListItem) { "use strict"; return Controller.extend("project1.controller.View1", { onInit: function () { }, onUpload: function (oEvent) { debugger var file = oEvent.getParameter("files")[0]; var that = this; var reader = new FileReader(); reader.onload = function (e) { debugger var data = e.target.result; var workbook = XLSX.read(data, { type: "binary" }); // Take first sheet var sheetName = workbook.SheetNames[0]; var sheet = workbook.Sheets[sheetName]; // Convert to JSON with empty values for blank cells var jsonData = XLSX.utils.sheet_to_json(sheet, { defval: "" }); // Extract headers dynamically var headers = Object.keys(jsonData[0]); // Get table reference var oTable = that.byId("idTable"); oTable.removeAllColumns(); // Build columns dynamically headers.forEach(function (header) { oTable.addColumn(new sap.m.Column({ header: new sap.m.Text({ text: header }) })); }); var oModel = that.getView().getModel("empModel"); oModel.setProperty("/empDetails", jsonData); // Create template row var oTemplate = new sap.m.ColumnListItem({ cells: headers.map(function (header) { return new sap.m.Text({ text: "{empModel&gt;" + header + "}" }); }) }); // Bind items oTable.bindItems({ path: "empModel&gt;/empDetails", template: oTemplate }); }; reader.readAsBinaryString(file); } }); });</code></pre><P>&nbsp;</P><P><STRONG><SPAN>XML View&nbsp;</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="project1.controller.View1" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns:unified="sap.ui.unified" xmlns="sap.m"&gt; &lt;Page id="page" title="{i18n&gt;title}"&gt; &lt;content&gt; &lt;unified:FileUploader fileType="XLSX,xlsx" change="onUpload" id="FileUploaderId" sameFilenameAllowed="true" iconOnly="false" buttonOnly="true" icon="sap-icon://upload" iconFirst="true" style="Emphasized" &gt; &lt;/unified:FileUploader&gt; &lt;Table id="idTable" inset="false"/&gt; &lt;/content&gt; &lt;/Page&gt; &lt;/mvc:View&gt;</code></pre><P>&nbsp;</P><P><STRONG><SPAN>Output</SPAN></STRONG><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Screenshot 2026-02-02 132836.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/367880i67851B8F8B30D6AA/image-size/large?v=v2&amp;px=999" role="button" title="Screenshot 2026-02-02 132836.png" alt="Screenshot 2026-02-02 132836.png" /></span></P><P><STRONG>Conclusion</STRONG></P><P>in this blog we discussed how to upload and read excel files in SAPUI5 application using a client side approach. We covered both fixed column excel handling and dynamic solution that adapts varying excel structures</P><P>&nbsp;</P> 2026-02-04T10:37:07.641000+01:00 https://community.sap.com/t5/technology-blog-posts-by-sap/the-new-geomap-control-in-mdc-library-bring-sap-geomap-into-your-ui5-apps/ba-p/14325044 The New Geomap Control in MDC Library: Bring SAP Geomap Into Your UI5 Apps 2026-02-10T15:19:04.206000+01:00 vvelinov https://community.sap.com/t5/user/viewprofilepage/user-id/7600 <P><STRONG><SPAN>Intro<BR /></SPAN></STRONG><SPAN class="">We’re excited to announce the release of an experimental MDC control for displaying interactive maps — visualizing&nbsp;GeoJSON data, integrating vector and raster tiles from various providers, enabling features like clustering and selection, or handling user interaction —&nbsp;</SPAN>sap.ui.mdc.Geomap <SPAN class="">offers a native UI5 interface to achieve this without building custom wrappers. </SPAN><SPAN class="">&nbsp;</SPAN></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="vvelinov_0-1770715345534.png" style="width: 940px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/370803i57142CB195B6DF51/image-dimensions/940x268/is-moderation-mode/true?v=v2" width="940" height="268" role="button" title="vvelinov_0-1770715345534.png" alt="vvelinov_0-1770715345534.png" /></span></P><P>&nbsp;</P><P><STRONG><SPAN>What’s&nbsp;in it for you</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><STRONG><SPAN>Faster integration:</SPAN></STRONG><SPAN> </SPAN><SPAN>Directly embed a production-grade map&nbsp;component&nbsp;into your UI5 applications,&nbsp;leveraging&nbsp;familiar UI5 properties, events, and aggregations. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Flexible data &amp; visualization:</SPAN></STRONG><SPAN> </SPAN><SPAN>Seamlessly combine various tile providers (ESRI,&nbsp;MapBox, Azure maps, SAP HSS, etc.) with&nbsp;GeoJSON&nbsp;data sources. Manage diverse layers like fill, line, circle, heatmap, or symbol layers directly from your application’s model. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Native UI5 behavior:</SPAN></STRONG><SPAN> </SPAN><SPAN>Benefit from standard UI5 control lifecycle, data binding, and composition, ensuring the map integrates smoothly with your existing UI5 patterns. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Build-friendly:</SPAN></STRONG><SPAN> </SPAN><SPAN>This library is specifically designed for UI5 runtime consumption. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>SAP HANA&nbsp;Spatial Services (HSS) integration:&nbsp;</SPAN></STRONG><SPAN>Allows to run with data provided from HSS.</SPAN></LI></UL><P><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>What’s included</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><STRONG><SPAN>A new experimental control in MDC library</SPAN></STRONG><SPAN>:</SPAN><SPAN> </SPAN><SPAN>&nbsp;sap.ui.mdc.Geomap </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Core building blocks</SPAN></STRONG><SPAN>: The main</SPAN><SPAN> </SPAN><SPAN>Geomap</SPAN><SPAN> </SPAN><SPAN>control, alongside specialized controls for providers, data sources, various layer types (such as,</SPAN><SPAN> </SPAN><SPAN>GeomapFillLayer,</SPAN><SPAN> </SPAN><SPAN>GeomapLineLayer,</SPAN><SPAN> </SPAN><SPAN>GeomapHeatmapLayer,</SPAN><SPAN> </SPAN><SPAN>GeomapSymbolLayer), and common map UI controls (such as,</SPAN><SPAN> </SPAN><SPAN>GeomapNavigationControl,</SPAN><SPAN> </SPAN><SPAN>GeomapScaleControl,</SPAN><SPAN> </SPAN><SPAN>GeomapSelectionControl,</SPAN><SPAN> </SPAN><SPAN>GeomapCopyrightControl). </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Full capabilities&nbsp;including:</SPAN></STRONG><SPAN>&nbsp;Rendering</SPAN><STRONG><SPAN>&nbsp;</SPAN></STRONG><SPAN>a dynamic, responsive map canvas with pan, zoom, rotate, and tilt capabilities. Supports smooth animations, full-screen mode, and adapts to all screen sizes, including mobile devices.  Built on WebGL for&nbsp;high-performance rendering. Map view can be programmatically controlled or manipulated by the user .</SPAN></LI></UL><P><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Multi-provider support</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Integrates with multiple map data providers for maximum flexibility. Out-of-the-box support for ESRI,&nbsp;HERE, OpenStreetMap&nbsp;and SAP HANA Spatial Services. Easily switch providers or configure custom styles and tokens. </SPAN><SPAN>&nbsp;</SPAN></P><P>&nbsp;</P><P><STRONG><SPAN>Rich layer system</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Visualize geospatial data using multiple layer types, each&nbsp;optimized&nbsp;for different visualization needs. </SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><STRONG><SPAN>Fill Layer:</SPAN></STRONG><SPAN>&nbsp;Colored polygons for regions, zones, or districts. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Circle Layer:</SPAN></STRONG><SPAN>&nbsp;Point markers with customizable radius and color. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Line Layer:</SPAN></STRONG><SPAN>&nbsp;Draw paths, routes, or connections. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Heatmap Layer:</SPAN></STRONG><SPAN>&nbsp;Visualize data density or clustering. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><UL><LI><STRONG><SPAN>Symbol Layer:</SPAN></STRONG><SPAN>&nbsp;Display icons and text labels for feature identification. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Advanced clustering</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN>Automatically groups nearby features to reduce clutter and improve performance. </SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Dynamic cluster sizing, customizable properties, and click-through expansion. Especially useful for large datasets. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Drawing &amp; selection tools</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Enables users to select or annotate map features using lasso, circle,&nbsp;polygon&nbsp;or rectangle tools. Supports multi-feature selection. </SPAN><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>GeoJSON data support</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN> Loads and visualizes standard&nbsp;GeoJSON&nbsp;data, including points, lines, polygons, and multi-geometries. </SPAN><SPAN>&nbsp;</SPAN></LI><LI><SPAN>Supports dynamic updates, feature properties, and optimized rendering for large datasets. Clustering available for point data. </SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Comprehensive controls</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><UL><LI><SPAN> Built-in UI controls for navigation, scale,&nbsp;fullscreen, copyright, and&nbsp;selection. </SPAN></LI><LI><SPAN> Controls can be positioned, enabled/disabled.</SPAN><SPAN>&nbsp;</SPAN></LI></UL><P><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Event-driven architecture</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Rich event system for application integration and interactive workflows. Listens for map lifecycle, user interaction, feature, drawing, and data update events. </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>Styling &amp; theming</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Professional, customizable styling with support for multiple color palettes, icons, and themes.  Data-driven styling rules, CSS variable integration, and dark/light theme support. </SPAN><SPAN>&nbsp;</SPAN></P><P>&nbsp;</P><P><STRONG><SPAN>Performance optimization</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Designed for enterprise-scale performance. Automatic tile-based rendering, lazy loading, efficient clustering, and WebGL acceleration.&nbsp;</SPAN><SPAN>&nbsp;</SPAN><SPAN>&nbsp;</SPAN></P><P>&nbsp;</P><P><STRONG><SPAN>Internationalization</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Inclusive design with keyboard navigation, screen reader compatibility, and multi-language support. ARIA labels,&nbsp;high contrast&nbsp;mode, and full i18n support. </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>  </SPAN><SPAN>&nbsp;</SPAN></P><P><STRONG><SPAN>How to use it</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>The</SPAN><SPAN> </SPAN><SPAN>sap.ui.mdc.Geomap</SPAN><SPAN> </SPAN><SPAN>library is delivered as part of OpenUI5.&nbsp;It’s&nbsp;designed to be included and consumed through your standard UI5&nbsp;build&nbsp;processes or runtime distribution. </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Link to samples:&nbsp;<A href="https://sapui5.hana.ondemand.com/#/entity/sap.ui.mdc.Geomap" target="_blank" rel="noopener nofollow noreferrer">https://sapui5.hana.ondemand.com/#/entity/sap.ui.mdc.Geomap</A></SPAN></P><P><SPAN>MDC tutorial:&nbsp;<A href="https://github.com/SAP-samples/ui5-mdc-json-tutorial/tree/main/u2/ex1" target="_blank" rel="noopener nofollow noreferrer">https://github.com/SAP-samples/ui5-mdc-json-tutorial/tree/main/u2/ex1</A></SPAN></P><P>&nbsp;</P><P><STRONG><SPAN>Get started</SPAN></STRONG><SPAN> </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>As an experimental library, OpenUI5&nbsp;Geomap&nbsp;is in its&nbsp;initial&nbsp;stages, and its API is not yet&nbsp;finalized. This presents a unique opportunity for you to contribute to its evolution by providing feedback and suggestions. Be aware that some APIs may change or be removed in future updates.  </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>We are excited to see the innovative applications you will create using the SAPUI5&nbsp;Geomap&nbsp;library.  </SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Happy geocoding! </SPAN><SPAN>&nbsp;</SPAN></P> 2026-02-10T15:19:04.206000+01:00 https://community.sap.com/t5/frontend-ui5-sap-fiori-blog-posts/problems-hiding-and-showing-table-columns/ba-p/14327225 Problems hiding and showing table columns 2026-02-12T15:00:28.444000+01:00 Andrei_Lysak https://community.sap.com/t5/user/viewprofilepage/user-id/2101129 <P>Hello experts, I have a problem.<BR />I need to hide table columns based on certain conditions.<BR />I get the table object by ID and then all the columns.<BR />Then I use oColumn.setVisible(), and everything works. However, the problem is that in the table personalization, this column is marked as selected, with a check mark.<BR />Is it possible to affect the table settings after changing the column properties?<BR />All tables are described on the backend and Table Type&nbsp;</P><DIV><DIV><SPAN>ResponsiveTable(sap.ui.mdc.table)</SPAN></DIV></DIV> 2026-02-12T15:00:28.444000+01:00