https://raw.githubusercontent.com/ajmaradiaga/feeds/main/scmt/topics/JavaScript-blog-posts.xml SAP Community - JavaScript 2024-05-20T11:10:54.240580+00:00 python-feedgen JavaScript blog posts in SAP Community https://community.sap.com/t5/technology-blogs-by-members/cds-ts-repository-simplify-sap-cap-entity-persistance-with-baserepository/ba-p/13572903 CDS-TS-Repository: Simplify SAP CAP Entity persistance with BaseRepository 2023-11-16T09:31:09+01:00 Dragolea https://community.sap.com/t5/user/viewprofilepage/user-id/170147 <P>Hi all,<BR /><BR />Many of us are leveraging SAP CAP (Node.js with TypeScript) for our projects.<BR /><BR />While CAP provides an abstract mechanism to derive <STRONG>RESTful oData</STRONG> services given a CDS data model there's always room for more efficiency and improvement in the implementing communication between <EM><STRONG>business logic, service, and persistence layer.</STRONG></EM><BR /><BR /><SPAN class="">It shall be noted at this point, that improvements have been attempted with regard to the service layer communication using <A href="https://www.npmjs.com/package/cds-routing-handlers" target="_blank" rel="noopener nofollow noreferrer">cds-routing-handler</A> However, this framework does not appear <EM><STRONG>t</STRONG><STRONG>o be maintained, especially for newer versions of CAP.</STRONG></EM></SPAN></P><H3 id="toc-hId-1092983821">-</H3><H3 id="toc-hId-896470316"><STRONG>Blog series</STRONG></H3><P><STRONG>1.&nbsp;<A title="CDS-TS-Dispatcher: Simplifying SAP CAP TypeScript Development" href="https://community.sap.com/t5/technology-blogs-by-members/cds-ts-dispatcher-simplifying-sap-cap-typescript-development/ba-p/13572824" target="_blank">CDS-TS-Dispatcher: Simplifying SAP CAP TypeScript Development&nbsp;</A></STRONG></P><P><STRONG>2.&nbsp;<A title="CDS-TS-Repository: Simplify SAP CAP Entity persistance with BaseRepository" href="https://community.sap.com/t5/technology-blogs-by-members/cds-ts-repository-simplify-sap-cap-entity-persistance-with-baserepository/ba-p/13572903" target="_self">CDS-TS-Repository: Simplify SAP CAP Entity persistance with BaseRepository</A></STRONG></P><P><STRONG>3.&nbsp;<A title="SAP CAP: Controller - Service - Repository design pattern" href="https://community.sap.com/t5/blogs/blogworkflowpage/blog-id/technology-blog-members/article-id/166975?prePageCrumb=BlogDashboardPage" target="_blank">SAP CAP: Controller - Service - Repository design pattern</A>&nbsp;</STRONG></P><P><EM>Crafted by developers for developers b</EM><EM>y&nbsp;</EM><STRONG><EM><A href="https://www.abs-gmbh.de/" target="_blank" rel="noopener nofollow noreferrer">ABS &amp; DxFrontier team</A></EM></STRONG></P><H3 id="toc-hId-699956811">-</H3><P><STRONG>In this blog, we will concentrate on the CDS-TS-Repository.</STRONG></P><H3 id="toc-hId-503443306">&nbsp;</H3><H3 id="toc-hId-306929801"><STRONG>Prerequisites</STRONG></H3><P>Before we dive in, you should have a basic understanding of SAP CAP NodeJS and TypeScript. If you need a refresher, here are some helpful resources&nbsp;<A href="https://cap.cloud.sap/docs/node.js/typescript" target="_blank" rel="noopener nofollow noreferrer">Official SAP CAP TypeScript</A></P><P>&nbsp;</P><H3 id="toc-hId-110416296"><STRONG>Section 1: Introduction to <A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository</A></STRONG></H3><P>The goal of <STRONG><A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository</A></STRONG><STRONG>&nbsp;- </STRONG><STRONG>BaseRepository</STRONG> is to significantly reduce the boilerplate code required to implement data access layers for persistence entities by providing out-of-the-box actions on the <STRONG>database</STRONG>.</P><H3 id="toc-hId--86097209"><STRONG>Section 2: Benefits of <A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository</A></STRONG></H3><UL><UL><LI><STRONG>Common actions</STRONG> for the most used persistence actions to the DB <STRONG>:</STRONG></LI></UL></UL><UL><UL><UL><LI><STRONG>create - </STRONG>Create a new entry in the table.</LI><LI><STRONG>createMany -&nbsp;</STRONG>create many entries in the table.</LI><LI><STRONG>update - </STRONG>Update entry in the table.</LI><LI><STRONG>delete - </STRONG>Delete an entry from the table.</LI><LI><STRONG>exists - </STRONG>Check the existence of an entry in the table.</LI><LI><STRONG>findOne - </STRONG>Find one entry in the table.</LI><LI><STRONG>find - </STRONG>Find entries in the DB.</LI><LI><STRONG>deleteMany</STRONG> - Delete many entries from the table.</LI><LI><STRONG>deleteAll</STRONG> - clear a table</LI><LI><STRONG>getAll</STRONG> - Fetch all entries from the table.</LI><LI>... and many more</LI></UL></UL></UL><UL><UL><LI><STRONG>Support draft actions</STRONG> by using <STRONG>BaseRepositoryDraft class and Mixin.</STRONG></LI></UL></UL><P>&nbsp;</P><H3 id="toc-hId--282610714"><STRONG>Section 3: Getting Started with <A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository</A></STRONG></H3><P><BR />Now, let's get started with <STRONG><A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository</A></STRONG>. We'll walk you through the steps to integrate it into your SAP CAP TypeScript project, providing code snippets and configuration examples for a smooth onboarding.<BR /><BR /><STRONG>1:</STRONG> Install cds-ts-repository NPM package.</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>npm install @dxfrontier/cds-ts-repository</code></pre><P>&nbsp;</P><P><STRONG>2</STRONG>. Generate type entities using cds-typer</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>npx -js/cds-typer "*" --outputDirectory ./srv/util/types/entities</code></pre><P>&nbsp;</P><P>The&nbsp;<A href="https://www.npmjs.com/package/@cap-js/cds-typer" target="_blank" rel="noopener noreferrer nofollow">cds-typer</A> offers a way to derive TypeScript definitions from a CDS model to give you enhanced code completion and a certain degree of type safety when implementing services.</P><UL><UL><LI>Target folder :./srv/util/types/entities&nbsp;- Change to your location destination folder.</LI></UL></UL><P><STRONG>3:</STRONG>&nbsp;<STRONG>MyRepository</STRONG> class<BR />Start by creating a <STRONG>MyRepository</STRONG> class, which will extend the <STRONG>BaseRepository</STRONG>&nbsp;to handle operations for your entity</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>import { BaseRepository } from '@dxfrontier/cds-ts-repository'; import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION'; class MyRepository extends BaseRepository&lt;MyEntity&gt; { constructor() { super(MyEntity) // a CDS Typer entity type } aMethod() { // BaseRepository predefined methods using 'MyEntity' entity // All methods parameters will allow only keys/values of type MyEntity const result1 = await this.create(...) const result2 = await this.createMany(...) const result5 = await this.getAll() const result6 = await this.getAllAndLimit(...) const result7 = await this.find(...) const result8 = await this.findOne(...) const result9 = await this.delete(...) const result10 = await this.update(...) const result11 = await this.updateLocaleTexts(...) const result12 = await this.exists(...) const result13 = await this.count() const results14 = await this.getLocaleTexts() const results15 = await this.builder().find(...).orderAsc([...]).getExpand(['...']).execute() // ... } }</code></pre><P>&nbsp;</P><H3 id="toc-hId--479124219">&nbsp;</H3><H3 id="toc-hId--675637724"><STRONG>Section 4: Examples</STRONG></H3><P>Let's see how CDS-TS-Repository simplifies common tasks.</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>import { BaseRepository } from '@dxfrontier/cds-ts-repository'; import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION'; class MyRepository extends BaseRepository&lt;MyEntity&gt; { constructor() { super(MyEntity); } public async aMethod() { const created = await this.create({ name : 'a new entry', description : 'description' }) // update const keys = { ID : '34' } const fieldsToUpdate = { name : 'updated entry with a new name' } // updated will return boolean (true if updated, false otherwise) const updated = await this.update(keys, fieldsToUpdate) // exists will return boolean (true if exists, false otherwise) const exists = await this.exists({ID : '84c67833-a9cb-450c-ae02-a32c3a7a6f6b'}) // results will contain an Array of your entity object const results = await this.getAll(); // delete will contain true if delete operation is successfull, and false otherwise const deleted1 = await this.delete({ name: 'Customer' }); const deleted2 = await this.delete({ ID: '2f12d711-b09e-4b57-b035-2cbd0a02ba19' }); // count the number of items const itemsLength = await this.count(); }</code></pre><P>&nbsp;</P><P>&nbsp;</P><H3 id="toc-hId--947382598"><STRONG>Section 5: Draft compatible using BaseRepositoryDraft and Mixin</STRONG></H3><P>BaseRepositoryDraft repository provides a clear separation of methods for&nbsp;<STRONG>working with active entities</STRONG>&nbsp;and&nbsp;<STRONG>draft instances.</STRONG></P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>import { BaseRepository, BaseRepositoryDraft, Mixin} from '@dxfrontier/cds-ts-repository' import { MyEntity } from 'YOUR_CDS_TYPER_ENTITIES_LOCATION' class MyRepository extends Mixin(BaseRepository&lt;MyEntity&gt;, BaseRepositoryDraft&lt;MyEntity&gt;) { constructor() { super(MyEntity) } // ... define custom CDS-QL actions if BaseRepository ones are not satisfying your needs ! }</code></pre><P>&nbsp;</P><P>Use&nbsp;<STRONG>BaseRepository</STRONG>&nbsp;methods when dealing with&nbsp;<STRONG>active entity instances.</STRONG></P><UL><UL><LI><STRONG>update</STRONG></LI><LI><STRONG>delete</STRONG></LI><LI><STRONG>create</STRONG></LI><LI><STRONG>createMany</STRONG></LI><LI><STRONG>... and many more</STRONG></LI></UL></UL><P>Use&nbsp;<STRONG>BaseRepositoryDraft</STRONG>&nbsp;methods when working with&nbsp;<STRONG>draft entity instances.</STRONG></P><UL><UL><LI><STRONG>updateDraft</STRONG></LI><LI><STRONG>deleteDraft</STRONG></LI><LI><STRONG>findOneDraft</STRONG></LI><LI><STRONG>findDrafts</STRONG></LI><LI><STRONG>... and many more</STRONG></LI></UL></UL><P><IMG src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/11/2023-11-24_13-36-50.png" border="0" /></P><P>Mixin BaseRepository and BaseRepositoryDraft</P><P><BR />For more info about CDS-TS-Repository visit the following <STRONG><A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">GitHub</A></STRONG></P><H3 id="toc-hId--1143896103"><STRONG>Conclusion</STRONG></H3><P><BR />In conclusion,&nbsp;<STRONG><A href="https://github.com/dxfrontier/cds-ts-dispatcher" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Dispatcher</A></STRONG>&nbsp;<STRONG>combined with&nbsp;<A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository</A>&nbsp;</STRONG>is a powerful tool that can speed up your SAP CAP TypeScript projects by eliminating repetitive code and being a better fit for common team architecture setups.<BR /><BR />Whether you're starting a new project or enhancing an existing one, integrating CDS-TS-Repository is a significant step towards a more efficient and productive development journey.</P><H3 id="toc-hId--1340409608"><STRONG>Additional Resources</STRONG></H3><P><BR />Find an example of usage of the <STRONG><A href="https://github.com/dxfrontier/cds-ts-samples" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Samples GitHub</A></STRONG><BR /><BR /><SPAN class="">Like</SPAN></P> 2023-11-16T09:31:09+01:00 https://community.sap.com/t5/technology-blogs-by-sap/a-shared-language-to-talk-about-technical-debt/ba-p/13571697 A Shared Language to talk about Technical Debt 2023-11-17T13:40:59+01:00 haeuptle https://community.sap.com/t5/user/viewprofilepage/user-id/40145 Everyone has heard of financial crisis where the economy is in trouble and where people and companies are overwhelmed with financial debts.&nbsp;The consequences can be disastrous.&nbsp;Did you know that such crisis happen often in software development?&nbsp;Technical debt is a sneaky plague that can hurt projects,&nbsp;deliveries,&nbsp;people,&nbsp;and sometimes an entire company.&nbsp;This blog is the first in a series of blogs on technical debt on what it is,&nbsp;where it comes from,&nbsp;and how to deal with it.&nbsp;It will shed light on something we too often ignore,&nbsp;but can take control over by being proactive!<BR /> <BR /> The intention of this blog is to define the term and to provide a shared language to talk about technical debt.<BR /> <H2 id="what-is-technical-debt" id="toc-hId-963868711">What is Technical Debt?</H2><BR /> Ward Cunningham: "Although immature code may work fine and be completely acceptable to the&nbsp;customer,&nbsp;excess quantities will make a program unmasterable,&nbsp;leading to extreme&nbsp;specialization of programmers and finally an inflexible product.&nbsp;Shipping first-time&nbsp;code is like going into debt.&nbsp;A little debt speeds development so long as it is paid&nbsp;back promptly with a rewrite...&nbsp;The danger occurs when the debt is not repaid.&nbsp;Every minute spent on not-quite-right code counts as interest on that debt.&nbsp;Entire&nbsp;engineering organizations can be brought to a standstill under the debt load of an&nbsp;unconsolidated implementation."<BR /> <BR /> Technical debt,&nbsp;a term coined by Ward Cunningham,&nbsp;is a concept in software development that reflects the implied cost of additional rework caused by choosing a quick and easy solution now instead of using a better approach that would take longer.&nbsp;It's a metaphor that equates software development to financial debt,&nbsp;illustrating the trade-off between short-term and long-term benefits.&nbsp;When a development team takes shortcuts,&nbsp;such as skipping parts of the coding process to meet deadlines or opting for a less optimal solution to save time,&nbsp;they incur technical debt.&nbsp;While these shortcuts might speed up the development process in the short term,&nbsp;they often lead to more work in the future as the team has to go back and fix the issues that arise from these shortcuts.&nbsp;This is similar to how taking on financial debt can provide immediate funds but requires repayment with interest over time.&nbsp;Technical debt can be categorized into two types:&nbsp;intentional and unintentional.&nbsp;Intentional technical debt is a strategic decision made with the understanding of the future cost.&nbsp;Unintentional technical debt,&nbsp;on the other hand,&nbsp;is accrued due to lack of knowledge or oversight,&nbsp;and is often discovered later when issues arise.&nbsp;It's important to manage technical debt effectively.&nbsp;If left unchecked,&nbsp;it can accumulate&nbsp;'interest'&nbsp;in the form of decreased productivity,&nbsp;increased complexity,&nbsp;and reduced code quality.&nbsp;This can lead to a slower pace of development,&nbsp;making it harder to implement new features or fix bugs.&nbsp;However,&nbsp;not all technical debt is bad.&nbsp;Sometimes,&nbsp;incurring technical debt can be a strategic decision.&nbsp;For instance,&nbsp;a startup might choose to incur some technical debt to speed up development and get their product to market faster.&nbsp;The key is to manage it effectively and pay it off regularly by refactoring code,&nbsp;improving documentation,&nbsp;and investing in automated testing.&nbsp;Technical debt is an inherent part of software development that needs to be managed strategically.&nbsp;By understanding its implications and making informed decisions,&nbsp;development teams can balance the need for speed with the importance of code quality and long-term maintainability.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/11/DilbertTechnicalDebt.jpg" /></P><BR /> <P class="image_caption" style="text-align:center;font-style:italic;, Arial, sans-serif">Technical Debt - <SPAN class="css-901oao css-16my406 r-poiln3 r-bcqeeo r-qvutc0"> Dilbert by Scott Adams&nbsp;</SPAN></P><BR /> <BR /> <H2 id="types-of-technical-debt" id="toc-hId-767355206">Types of Technical Debt</H2><BR /> There are various forms of technical debt.&nbsp;The following list is not exhaustive,&nbsp;but it covers the most common types of technical debt.<BR /> <OL><BR /> <LI><STRONG>Code Debt</STRONG>: A prevalent form of technical debt, code debt stems from shortcuts or subpar coding practices, resulting in inefficient or disorganized code, which is hard to read, maintain and test. This make all development and maintenance tasks slower and will degrade the "enjoyment" of writing code while demotivating engineers. More details on how to deal with code debt can be found in the blog post&nbsp;<A href="https://blogs.sap.com/2022/12/21/clean-code-writing-maintainable-readable-and-testable-code/" target="_blank" rel="noopener noreferrer">Clean Code: Writing maintainable, readable and testable code</A></LI><BR /> <LI><STRONG>Design Debt</STRONG>: Design debt emerges when the system design is compromised, often due to time pressures or inadequate foresight during the design phase, resulting in a system that's challenging to maintain and expand. Due to this kind of debt developers can’t get confidence quickly that their code will not break existing functionality and dependencies.</LI><BR /> <LI><STRONG>Testing Debt</STRONG>: Testing debt arises from insufficient testing, including a lack of unit, integration, or system tests. This can lead to unnoticed bugs and software issues. Another important type of testing are flaky tests. More details on how to deal with testing debt tests can be found in the blog posts:<BR /> <UL><BR /> <LI><A href="https://ecosystem4engineering.substack.com/p/shared-language-for-talking-about" target="_blank" rel="nofollow noopener noreferrer">Shared Language for talking about Test Strategy with a focus on the Test Pyramid</A></LI><BR /> <LI><A href="https://ecosystem4engineering.substack.com/p/the-hidden-costs-of-flaky-tests" target="_blank" rel="nofollow noopener noreferrer">The Hidden Costs of Flaky Tests: Why You Need to Fix Them Now</A>,</LI><BR /> <LI><A href="https://ecosystem4engineering.substack.com/p/how-to-improve-testability" target="_blank" rel="nofollow noopener noreferrer">How to improve Testability</A></LI><BR /> <LI><A href="https://ecosystem4engineering.substack.com/p/get-higher-quality-and-productivity" target="_blank" rel="nofollow noopener noreferrer">Get Higher Quality and Productivity by tackling the Broken Window Effect</A></LI><BR /> </UL><BR /> </LI><BR /> <LI><STRONG>Documentation Debt</STRONG>: Documentation debt occurs when system documentation is incomplete or outdated, hindering new team members' understanding and slowing down system maintenance and expansion.</LI><BR /> <LI><STRONG>Infrastructure Debt</STRONG>: Infrastructure debt arises when the supporting infrastructure, such as the operating system, database, development environment, build systems, and CI/CD pipelines, are outdated or inefficient.</LI><BR /> <LI><STRONG>Architectural Debt</STRONG>: Architectural debt occurs when the system's architecture fails to meet current or future functional and non-functional requirements, making the system difficult to maintain, adapt, or scale.</LI><BR /> <LI><STRONG>Knowledge Debt</STRONG>: Team lacks necessary expertise or the knowledge is not spread among the team, leading to "knowledge silos" and bottlenecks that can slow development and increase risk. This may be due to staffing gaps and turnover or inherited orphaned code/projects.</LI><BR /> <LI><STRONG>Dependency Debt</STRONG>: Dependency debt occurs when a system depends on outdated or unsupported third-party libraries or services. The Dependencies are unstable or rapidly changing. Teams are unable to take advantage of new improvements and remain vulnerable to security problems. This also can slow down the onboarding of new hires and cause frustration for current developers who are forced to work with older versions. More details on how to deal with dependency debt can be found in the blog post&nbsp;<A href="https://ecosystem4engineering.substack.com/p/security-fixing-vulnerabilities-keeping" target="_blank" rel="nofollow noopener noreferrer">Keeping Dependencies Up To Date</A></LI><BR /> <LI><STRONG>Migration Debt:</STRONG>&nbsp;Migration is needed or in progress: This may be motivated by the need to scale, license issues, costs, to reduce dependencies, or to avoid deprecated technology. It can also occur when a migration was poorly executed or abandoned: This may have resulted in maintaining two versions.</LI><BR /> <LI><STRONG>Unused features / Dead Code Debt</STRONG>&nbsp;Dead and/or abandoned code: Code/features/projects were replaced or superseded but not removed. More features creates more conditions and more edge cases that developers have to design around, which erodes the delivery speed.</LI><BR /> <LI><STRONG>Reliability or Performance Debt:</STRONG>&nbsp;These can affect the customer experience as well as the ability to scale the business.</LI><BR /> <LI><STRONG>Tool Debt</STRONG>&nbsp;Inefficient tools (both proprietary and third-party) can introduce friction or overhead for developers, slowing delivery speed.</LI><BR /> <LI><STRONG>Manual Process Debt</STRONG>&nbsp;When a part of the product delivery isn't automated, it requires more manual time and effort.</LI><BR /> <LI><STRONG>Automated deployments Debt</STRONG>&nbsp;Automated deployment workflows enable the ability to deliver features to customers continuously and at will.</LI><BR /> <LI><STRONG>Coupling Debt</STRONG>&nbsp;Coupling between modules or services leads to teams potentially blocking each other, slowing down delivery speed.&nbsp;<A href="https://ecosystem4engineering.substack.com/p/domain-driven-design-curated-resources" target="_blank" rel="nofollow noopener noreferrer">Domain Driven Design</A>&nbsp;is a great methodology to deal with this kind of debt.</LI><BR /> <LI><STRONG>Duplication Debt</STRONG>&nbsp;Duplication of code, services, or functionality can lead to wasted effort and increased maintenance costs. On the other hand should duplication be balanced with the need for autonomy and independence of teams. Reduction of duplication can cause dependencies between teams, which can slow down delivery speed. So it is important to consider the trade-offs.</LI><BR /> </OL><BR /> <H2 id="conclusion" id="toc-hId-570841701">Conclusion</H2><BR /> In conclusion,&nbsp;the legacy codebase,&nbsp;while being a significant asset,&nbsp;also presents a formidable challenge due to the inherent technical debt.&nbsp;This debt,&nbsp;if not addressed promptly,&nbsp;can become a substantial obstacle in delivering high-quality solutions and adapting to the ever-evolving market needs.&nbsp;Therefore,&nbsp;it is important to implement measures to manage and reduce this technical debt effectively.&nbsp;In the forthcoming year,&nbsp;I will publish a series of blog posts,&nbsp;each focusing on different aspects of technical debt.&nbsp;These posts will not only shed light on the complexities of the issue but also propose potential strategies,&nbsp;tools,&nbsp;practices,&nbsp;behaviorus,&nbsp;constraints and other approaches to deal with it.<BR /> <H2 id="resources" id="toc-hId-374328196">Resources</H2><BR /> <UL><BR /> <LI><A href="https://ieeexplore.ieee.org/document/10109339" target="_blank" rel="nofollow noopener noreferrer">Defining, Measuring, and Managing Technical Debt</A></LI><BR /> <LI><A href="https://martinfowler.com/articles/bottlenecks-of-scaleups/01-tech-debt.html#TypicalTypesOfDebt" target="_blank" rel="nofollow noopener noreferrer">Bottleneck Tech Debt</A></LI><BR /> </UL> 2023-11-17T13:40:59+01:00 https://community.sap.com/t5/technology-blogs-by-members/ui5-goes-mad-in-dorset/ba-p/13577650 UI5 Goes Mad in Dorset! 2023-12-07T05:54:23+01:00 hardyp180 https://community.sap.com/t5/user/viewprofilepage/user-id/13778 Here we have a random incoherent steam of thought, in regard to reading the first few chapters of “Clean UI5” from SAP Press. On the front page is a picture of a leaf, as in “take a leaf out of my book”.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Clean-UI5-Book.png" /></P><BR /> <P class="image_caption" style="text-align:center;font-style:italic;, Arial, sans-serif">Clean UI5 Book</P><BR /> <A href="https://www.sap-press.com/clean-sapui5_5479/" target="_blank" rel="nofollow noopener noreferrer">https://www.sap-press.com/clean-sapui5_5479/</A><BR /> <BR /> In the beginning was “Clean Code” by Robert Martin, whereby most of the examples were in Java, but the basic principles were timeless. That is, that the clearer the code is, the easier it is to understand, and thus maintain, and thus reduce costs and speed up development time.<BR /> <BR /> Then we had the “Clean ABAP” book, backed up by an online Style Guide on GitHub, and also on GitHub a set of ATC checks you could download and use to analyse the custom code in your system. Lars sort of beat SAP to that with his “Open ABAP checks” on GitHub a few years earlier, but anyway, he likes the Clean ABAP style guide that is on GitHub, and so do I. Not everyone is going to agree with everything, but that is to be expected.<BR /> <BR /> <STRONG>How NOT to a do a Book Review</STRONG><BR /> <BR /> Sometimes SAP Press gives me a free online copy of a book so I can do a review. In such cases I sort of try to write a book review, but it comes out as nonsense more often than not, a sort of rant about the concepts the book (whatever it may be) is talking about. I don’t know why they keep doing this, I suppose any publicity is good publicity as long as they spell your name right as P.T. Barnum once said. The musical about P.T. Barnum with Hugh Jackman was ten billion times better than the one with Michael Crawford from about 25 years earlier, by the way. I bought the Clean UI5 book myself.<BR /> <BR /> The Clean ABAP book got a lot of grief, but I liked it, it was a bit incoherent, but then so I am, people are going to disagree with a lot of it, but that is the whole point, to get people thinking. Put another way if you say “Do XYZ” to make code cleaner people are going to say, no that is madness you need to do the OPPOSITE to make code cleaner. The point is that suddenly people are thinking about the best way to make ABAP code cleaner when they were not before.<BR /> <BR /> <STRONG>James Joyce</STRONG><BR /> <BR /> So, what I did, was to read the first two chapters of the book, wrote down all the notes I made, and then pretended I would sort them out later. In real life I will just present the notes in the order that I wrote them, and since I read the pages in sequential order, you would hope the notes would come out logically as a result. The only thing that could prevent that is if the book wandered all over the place at random, like I do in my blogs.<BR /> <BR /> <STRONG>JavaScript Things</STRONG><BR /> <BR /> You can declare a variable twice (or many times) within a “scope” (that is, within a function or whatever) but only the first one counts. In ABAP you get a hard “that has already been declared” error.<BR /> <BR /> I am getting the feeling that the keyword VAR defines a global variable, no matter where it is declared. That is a bit like in ABAP when you define what you think is a local variable in a PBO/PAI module but in fact it is global.<BR /> <BR /> The LET and CONST statements define local variables, which are even more local than ABAP, because they only exist within a programming block, a loop or some such. Apart from that it is just like ABAP – you cannot access the variable before you declare it. As I have mentioned CONST is a bit like a variable in ABAP. Declaring a variable that way is more like an intention that you don’t want the value to change, but you can change it if you want. The syntax check will tell you that if you are doing naughty things like that use LET instead.<BR /> <BR /> <STRONG>The Style Guide Council</STRONG><BR /> <BR /> On GitHub there are “style guides” that talk about clean code, and the SAP Press books are expansions on this. At least, I thought that was the idea.<BR /> <BR /> At the start of the UI5 book it seems to be teaching me a lot more about the very basics of JavaScript than the basic JavaScript book I have been reviewing. Whilst that is good in some senses, that seems the wrong way around to me, the basic book should explain the basics, the Clean UI5 book should assume the reader knows the basics and concentrate on readability/maintainability matters.<BR /> <BR /> Sometimes you get two options, and it is not 100% clear which one is the “clean” option. You could have two options one after the other which do the same thing. In my mind the option that looks most like Plain English always wins, the option that looks less like machine code. That is words trump symbols.<BR /> <BR /> <STRONG>Back to JavaScript Basics Again</STRONG><BR /> <BR /> Template literals – here, when building up a string, you use a &amp;1 type variable as opposed to concatenation. The syntax is of course different between ABAP and JavaScript, but the concept is the same. I wonder if ABAP copied this concept from JavaScript or the other way around. Probably the former.<BR /> <BR /> Promises - I was informed, years ago, that a big thing in JavaScript was “promises” where you could write the code so it looked it was synchronous (which made the code easier to read) but really you could control when it paused to wait for the result of something else.<BR /> <BR /> The first example I saw of this (in this book) made me wonder where the pause was. It turns out the word THEN has a subtly different meaning to IF/THEN. In the case it means wait for something THEN do something else.<BR /> <BR /> The subject moved on to “tag fragments” and my notes were “I don’t understand any of this” probably because I am an idiot.<BR /> <BR /> Spread Syntax - two examples of two different ways to do the same thing with the quote “<EM>if you implement the same example using the spread syntax, the code is much easier to read</EM>” to which my notes were “Is it? Both examples read like gobbledegook”. There is really a lot to be said for verbose languages like ABAP. Why should someone have to guess that three dots (periods) in a row have a special meaning, as opposed to someone having a sneezing fit and pressing the period key three times by accident?<BR /> <BR /> A little later we have to more examples that look almost exactly the same and the second one has the three dots which “makes the purpose explicit” but I don’t think it does.<BR /> <BR /> Moving onto arrays we now have an example of an iterable object which is NOT an array called ARGUMENTS. Is this the right room for the ARGUMENTS?<BR /> <BR /> In the example this is used in a function with the word ARRAY in its name even though it was just stated this is not an array. I suppose because the result looks like an array? Thank god for internal tables I all I can say.<BR /> <BR /> <STRONG>Lovely Rita, PARAMETER Maid</STRONG><BR /> <BR /> Now we come to the subject of parameters in function calls (as opposed to method calls of objects). The recommendation is the same as in Clean ABAP which is to limit the number of parameters. The reason given here is that because parameters have no names in the caller, it is easy to forget one, or pass the wrong value in. Which is why I presume in ABAP methods the caller is forced to name the parameters if there is more than one going in or out.<BR /> <BR /> The recommendation in JavaScript seems to be not to have a BOOLEAN parameter, and then the recommendation instantly changes (in the next sentence) to use a meaningful name for the function call, so you know what the BOOLEAN parameter means. The example given is a standard SAP UI5 function into which you pass the value TRUE and there is no way in ten billion years you could guess what that function does, so I think they are saying to wrap the standard SAP function with the obscure name in a function with a meaningful name. I think.<BR /> <BR /> Some SAP functions have Hungarian prefixes to say what type of value you should pass in (this is more important in JavaScript than in ABAP due to the lack of type checking) e.g., V for a string, or M for an object (stands for “member” I imagine). I remember when I first started seeing UI5 code on the official SAP websites (long time back now) and I was vaguely surprised at all the Hungarian Notation.<BR /> <BR /> At long last I am going to say something positive. When calling a function like a message box you can pass in another function as a parameter to be called after an event like ONCLOSE. In some ways this is just like the 1999 function module REUSE_ALV_LIST_DISPLAY where you passed in the name of a FORM routine to be dynamically called but much more explicit.<BR /> <BR /> I am going to change subject for no apparent reason. Possibly because the book has so many authors, it seems to be unsure what it’s purpose is. Sometimes it is explaining the basics of JavaScript, sometimes explain very complex techniques, sometimes explaining how the language has evolved over time, sometimes even talking about Clean Code. All mixed together, seemingly at random. Now, back to JavaScript.<BR /> <BR /> I may have got this wrong, but I think the book is talking about having the view in the MVC model in JavaScript, instead of just the controller. Lots of web programmers like doing just that but I understood that with UI5 the SAP recommendation was to have the view in XML. That enforced the separation of concerns by having the model, view and controller all in different languages.<BR /> <BR /> <STRONG>Frank Sidebottom Effect</STRONG><BR /> <BR /> It looks like certain types of functions have parameters passed by reference, so the function can alter the passed in object, thus causing side effects. I thought the whole point of a functional language was not to have side effects.<BR /> <BR /> Next, we move onto the subject of regular expressions. I have always thought that regular expressions are no fun, and they are also not very “clean” in that they are written in what to all intents and purposes is a secret code rather than Plain English.<BR /> <BR /> As an aside I had written a note saying “things really need to follow on from one another in a logical manner” by which I meant the book is rambling all over the place. I am guilty of that very crime in my blogs, this very blog being a case in point, bit you would think a book would have a more logical structure.<BR /> <BR /> There is quite a good and long section explaining what “promises” are, why they are good, and how not to use them, and how to use them properly. This is very important given the SAP direction in UI5 is that more that is asynchronous the better, but once again, is the books purpose to explain complex JavaScript concepts, or to assume the reader already is conversant with JavaScript and focus more on making the code clearer?<BR /> <BR /> On page 69 we see the term PARADIGM SHIFT which makes the whole audience do a Mexican Wave.<BR /> <BR /> I have an admission to make. One day I was reading the book mid-afternoon and read the explanation of what the AWAIT keyword does. Four hours later I was on a video call with Lars Petersen, and he showed me some JavaScript code which was peppered with AWAIT statements. He asked me if I understood what that code was doing, and I said “of course” as if I had known all about the concept for years.<BR /> <BR /> In the basic JavaScript book I have been writing about, all the examples are to do with music, in the same way my examples are about monsters, which makes the concepts easier to grasp. Some crazy people use aircraft and flights for examples.<BR /> <BR /> Some examples in the Clean UI5 book are a bit too generic using the good old A, B, C for variables and the DO_SOMETHING names for functions.<BR /> <BR /> Once again in my notes I write “This whole thing is more to do with advanced JavaScript concepts than Clean Code”.<BR /> <BR /> <STRONG>Back to Parameters</STRONG><BR /> <BR /> It is mentioned that as of JavaScript ES6 you can define default values for incoming parameters. I had written in my notes “Well Done!” because of course I have been used to default values in ABAP for a very long time, starting with function modules. Apparently, the rules for default values in JavaScript are a lot more complicated, so much so the book says you need to read the online documentation to really get your head around the concept. So, that is not really “clean” as “clean” concepts should be easy to understand and the code should do what you expect it to do. That is a JavaScript problem, the book is just pointing it out.<BR /> <BR /> You can only have mandatory parameters in JavaScript if you use TypeScript. There is a corker of an example of how to get around this, which is also an excellent example of passing in a function as a parameter. Remember, you can pass in any function as a parameter, a function that can do anything. In this case the default parameter is a function that throws an error. Unless an actual value is passed in to the parameter then the function that is the default parameter is passed in and executes, throwing an error saying a real parameter needs to be passed in.<BR /> <BR /> Now we move onto classes in JavaScript and the examples talk about Function A and Class A, which are a bit generic to say the least. Come back SFLIGHT, all is forgiven.<BR /> <BR /> Here is a quote “None of the properties and methods of SAPUI5 classes are truly private”.<BR /> <BR /> To get around this you are encouraged to follow some naming conventions which in theory should scare off any other developers from changing your private methods and attributes, if they are familiar with those naming conventions and know what they mean, but I have always found that naming conventions are not substitute for the language actually enforcing rules. A JavaScript person would probably say rules are for losers. As an example, I prefer working in Australia to Germany as there are 95% less rules to follow, and I feel that makes me more productive.<BR /> <BR /> <STRONG>I GET so emotional, baby</STRONG><BR /> <BR /> As we all know, programmers tend to get very emotional about what outsiders would see as very trivial matters. These range from mild disagreements to chopping up the programmers who disagrees with an axe.<BR /> <BR /> One such area is the use of GETTERS and SETTERS to write and read values from an object instance. Some people say that is the only way to do this, some say it is nonsense. In some standard SAP ABAP classes there are GET and SET methods and they even have a special icon that appears next to them, though if a “customer” like me creates such a method no such icon appears. In any event, sticking with the ABAP example, which of course I am doing in a blog nominally about JavaScript, I have tended to use read-only attributes to simulate GET methods, and use SET methods when the attribute is private. For twenty years I have been waiting for a use case whereby I can say I am using a SET and/or GET because there is extra logic to be executed when reading or writing a value. I have not found just a use case yet, but one day it will come, I am sure.<BR /> <BR /> In UI5 I am informed that the library automatically generates SET and GET functions for each attribute, but I think you can also directly read and write the values as well.<BR /> <BR /> In JQUERY everything goes BONKERS. Youi have one function and if you do not pass an optional parameter then it acts a GET method, but if you pass in a value that same function acts as a SET method. Oh dear, that is a clear violation of the “a method should only do one thing” rule, and a wonderful example of why optional parameters are a Bad Thing.<BR /> <BR /> One interesting thing in JavaScript is you can do some jiggery pokery whereby the define the extra logic in SET and GET functions, and then those functions are automatically called when the code does a direct assignment, or a direct read. That is nice, but I hate black magic code, that is something which is not there when you look at the code but gets executed anyway. Logical Databases in ABAP were like that. I was hoping they would be gone in S/4HANA but I think they are still there which is a pity.<BR /> <BR /> At this point I think the author has changed, because instead of very generic examples with classes and methods called A, B and C, suddenly we have much better really specific examples and use cases. Naturally I far prefer the latter.<BR /> <BR /> I think the pattern (in the text of the book) now has changed to first talking about a feature in JavaScript in general, then how UI5 implements that feature in its own particular way, and then some suggestions on how to write the code in a “clean” way. That pattern seems logical and sensible to me, but not all the book is like that, and in the Clean ABAP book the vast majority of the text is talking about Clean Code.<BR /> <BR /> <STRONG>Patricia the Type Scripter</STRONG><BR /> <BR /> We now move onto Typescript, which UI5 supports. Indeed, in the example in the hands-on session at ASUG TECHCONNECT 2023 many of the files were written in Typescript.<BR /> <BR /> Incidentally, apropos of nothing, JavaScript has the same concept as the ABAP PRAGMA, that is you can write a comment to disable a warning that the static syntax checker (LINTER) would normally give for a specific area of code.<BR /> <BR /> One point raised is that once you have strong typing, then the IDE suddenly becomes ten billion times better when it comes to code autocompletion e.g., if it knows a variable is typed as certain class then it can suggest method names and the like.<BR /> <BR /> It also looks like there is no “interface” concept in JavaScript (in the SE24 sense of the word) but there is in TypeScript.<BR /> <BR /> I cannot be 100% sure but I think this book is recommending Visual Studio Code to write Typescript files. At ASUG naturally we used the BTP, and SAP BUILD CODE, but that has only just been released and thus was not there when the Clean UI5 book was written.<BR /> <BR /> <STRONG>Annotate, Annotate, Baker Man</STRONG><BR /> <BR /> Now we come to the concept of “decorators”. You will know this in CDS views by the term “annotation” and indeed in Typescript you invoke a “decorator” by putting an “@” sign at the start, just like with annotations in CDS views.<BR /> <BR /> I think annotations is the better name, because “decorator” makes me think of the “decorator” design pattern, which is a different thing altogether. IT’S A DIFFERENT THING!<BR /> <BR /> I could not help but note that the summary contains the phrase PARADIGM SHIFT which once again makes the audience scream and do a Mexican wave.<BR /> <BR /> OK, I have got to the end of chapter 2, which is not very far at all, really. But I am going to have to leave it there for now, as I have a book to write myself!<BR /> <BR /> Cheersy Cheers<BR /> <BR /> Paul 2023-12-07T05:54:23+01:00 https://community.sap.com/t5/technology-blogs-by-members/effortless-material-creation-using-excel-sheets-via-fiori-app-e-g/ba-p/13577470 Effortless Material Creation using Excel Sheets via Fiori App (e.g., SolidWorks' CAD-Driven Data) 2023-12-11T19:46:45+01:00 mertuytun https://community.sap.com/t5/user/viewprofilepage/user-id/173555 <H1 id="toc-hId-834962747"><STRONG>Introduction</STRONG></H1><BR /> Material creation in SAP is simpler using Fiori and Excel, we can tweak data before uploading, even integrating info from CAD software like SolidWorks or any other data source. This connection makes creating new materials in SAP a smoother process. Let's dive into how this system works seamlessly.<BR /> <BR /> <!-- notionvc: 2f61233b-bdda-4141-ab80-978211feadd7 --><BR /> <H3 id="toc-hId-896614680">Summary</H3><BR /> In this blog, we're diving into a streamlined material creation process within SAP. We'll explore the creation of a Fiori app and an OData service designed to seamlessly integrate data from external sources for material creation. For this blog, we will be using data comes from SolidWorks automation.<BR /> <BR /> &nbsp;<BR /> <H3 id="toc-hId-700101175">Pre-requisites:</H3><BR /> <UL><BR /> <LI>S/4 HANA System</LI><BR /> <LI>VSCode with SAP Fiori Tools - Extension Pack</LI><BR /> </UL><BR /> <H3 id="toc-hId-503587670">Steps:</H3><BR /> <UL><BR /> <LI><STRONG>Step 1</STRONG> - Preparing Excel sheet from data source</LI><BR /> <LI><STRONG>Step 2</STRONG> - Creating oData service for material creation<BR /> <UL><BR /> <LI><STRONG>Step 2.1</STRONG> - Creating oData service</LI><BR /> <LI><STRONG>Step 2.2</STRONG> - Registering oData service</LI><BR /> </UL><BR /> </LI><BR /> <LI><STRONG>Step 3</STRONG> - Creating Fiori app for excel upload and material creation<BR /> <UL><BR /> <LI><STRONG>Step 3.1</STRONG> - Creating Fiori app</LI><BR /> <LI><STRONG>Step 3.2</STRONG> - Adding libraries that will be used for excel import</LI><BR /> <LI><STRONG>Step 3.3</STRONG> - Creating global model</LI><BR /> <LI><STRONG>Step 3.4</STRONG> - Preparing View</LI><BR /> <LI><STRONG>Step 3.5</STRONG> - Preparing Controller</LI><BR /> </UL><BR /> </LI><BR /> <LI><STRONG>Step 4</STRONG> - Use app and check system to ensure creating of materials<!-- notionvc: c7813624-3e4b-4422-a0c0-f9eeae38394d --></LI><BR /> </UL><BR /> &nbsp;<BR /> <H3 id="toc-hId-307074165"><SPAN class="notion-enable-hover" data-token-index="0"><STRONG>Step 1</STRONG> - </SPAN>Preparing Excel sheet from any data source<!-- notionvc: dfb90f6f-6e61-42ea-81fe-1225a1b621ae --></H3><BR /> We'll kick off our material creation process by preparing an Excel sheet. Here, we'll utilize data obtained from SolidWorks using a basic automation method. It's crucial to note that this automation setup serves as an example, keeping things straightforward. In real-world scenarios, the process would involve selecting multiple assembly files or engaging in more intricate procedures for efficiency. This basic demonstration helps us understand the fundamental integration of SolidWorks data with our material creation system in SAP. The emphasis here is on demonstrating the foundational steps, which can be further expanded upon in practical implementations for more complex workflows.<BR /> <BR /> This is the automation that helped to obtain data from Solidworks:<BR /> <BR /> <IFRAME width="560" height="315" src="https://www.youtube.com/embed/tOt_5Jc3CCc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></IFRAME><BR /> <BR /> It's important to note that the showcased automation is just a basic starting point and calls for improvement. While I used this simple automation, keep in mind that data retrieval from SolidWorks isn't limited to automation alone. This automation is just for streamlining the process. You have the flexibility to gather information from SolidWorks manually or SolidWorks PDM, as well as from a wide array of any other data sources.<BR /> <BR /> &nbsp;<BR /> <H3 id="toc-hId-110560660"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Step 2</SPAN></STRONG> - Creating oData service for material creation<!-- notionvc: d521c5c9-5a75-418d-979f-cf95400ddfde --></H3><BR /> We'll create an oData service—a key connection allowing outside data, to smoothly enter SAP. This service acts as a bridge, ensuring easy communication between SAP and different external sources, vital for material creation within SAP.<BR /> <H3 id="toc-hId--85952845"><SPAN class="notion-enable-hover" data-token-index="0"><STRONG>Step 2.1</STRONG> - </SPAN>Creating oData service<!-- notionvc: 19e203a6-9e61-49c4-b7a0-25bded6540ed --></H3><BR /> Open your S/4 system and transaction SEGW<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-1.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-2.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-3.png" /></P><BR /> <BR /> <TABLE style="font-size: 1rem;width: 629px"><BR /> <THEAD><BR /> <TR><BR /> <TH style="width: 121px;text-align: center">Name</TH><BR /> <TH style="width: 10px;text-align: center">Key</TH><BR /> <TH style="width: 105px;text-align: center">Edm Core Type</TH><BR /> <TH style="width: 34px;text-align: center">Prec</TH><BR /> <TH style="width: 40px;text-align: center">Scale</TH><BR /> <TH style="width: 45px;text-align: center">Max</TH><BR /> <TH style="width: 293px;text-align: center">Abap Field Name</TH><BR /> </TR><BR /> </THEAD><BR /> <TBODY><BR /> <TR><BR /> <TD style="width: 121px;text-align: center">matDescTR</TD><BR /> <TD style="width: 10px;text-align: center"></TD><BR /> <TD style="width: 105px;text-align: center">Edm.String</TD><BR /> <TD style="width: 34px;text-align: center">0</TD><BR /> <TD style="width: 40px;text-align: center">0</TD><BR /> <TD style="width: 45px;text-align: center">40</TD><BR /> <TD style="width: 293px;text-align: center">MATDESCTR</TD><BR /> </TR><BR /> <TR><BR /> <TD style="width: 121px;text-align: center">matDescEN</TD><BR /> <TD style="width: 10px;text-align: center"></TD><BR /> <TD style="width: 105px;text-align: center">Edm.String</TD><BR /> <TD style="width: 34px;text-align: center">0</TD><BR /> <TD style="width: 40px;text-align: center">0</TD><BR /> <TD style="width: 45px;text-align: center">40</TD><BR /> <TD style="width: 293px;text-align: center">MATDESCEN</TD><BR /> </TR><BR /> <TR><BR /> <TD style="width: 121px;text-align: center">baseUOM</TD><BR /> <TD style="width: 10px;text-align: center"></TD><BR /> <TD style="width: 105px;text-align: center">Edm.String</TD><BR /> <TD style="width: 34px;text-align: center">0</TD><BR /> <TD style="width: 40px;text-align: center">0</TD><BR /> <TD style="width: 45px;text-align: center">3</TD><BR /> <TD style="width: 293px;text-align: center">BASEUOM</TD><BR /> </TR><BR /> <TR><BR /> <TD style="width: 121px;text-align: center">matType</TD><BR /> <TD style="width: 10px;text-align: center"></TD><BR /> <TD style="width: 105px;text-align: center">Edm.String</TD><BR /> <TD style="width: 34px;text-align: center">0</TD><BR /> <TD style="width: 40px;text-align: center">0</TD><BR /> <TD style="width: 45px;text-align: center">4</TD><BR /> <TD style="width: 293px;text-align: center">MATTYPE</TD><BR /> </TR><BR /> <TR><BR /> <TD style="width: 121px;text-align: center">indSector</TD><BR /> <TD style="width: 10px;text-align: center"></TD><BR /> <TD style="width: 105px;text-align: center">Edm.String</TD><BR /> <TD style="width: 34px;text-align: center">0</TD><BR /> <TD style="width: 40px;text-align: center">0</TD><BR /> <TD style="width: 45px;text-align: center">4</TD><BR /> <TD style="width: 293px;text-align: center">INDSEC</TD><BR /> </TR><BR /> <TR><BR /> <TD style="width: 121px;text-align: center">materialNum</TD><BR /> <TD style="width: 10px;text-align: center">X</TD><BR /> <TD style="width: 105px;text-align: center">Edm.String</TD><BR /> <TD style="width: 34px;text-align: center">0</TD><BR /> <TD style="width: 40px;text-align: center">0</TD><BR /> <TD style="width: 45px;text-align: center">40</TD><BR /> <TD style="width: 293px;text-align: center">MATNR</TD><BR /> </TR><BR /> </TBODY><BR /> </TABLE><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-4-2.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-5.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-6.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-7.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-odata-service-8.png" /></P><BR /> <BR /> <PRE class="language-abap" style="height: 400px"><CODE>DATA: ls_headdata TYPE bapimathead,<BR /> ls_clientdata TYPE bapi_mara,<BR /> ls_clientdatax TYPE bapi_marax,<BR /> ls_materialdesc TYPE bapi_makt,<BR /> lt_materialdesc TYPE TABLE OF bapi_makt,<BR /> ls_return TYPE bapiret2.<BR /> <BR /> DATA: ls_request_input_data TYPE zcl_zxxx_mpc=&gt;ts_creatematerial,<BR /> lo_message_container TYPE REF TO /iwbep/if_message_container.<BR /> <BR /> <BR /> CALL METHOD me-&gt;/iwbep/if_mgw_conv_srv_runtime~get_message_container<BR /> RECEIVING<BR /> ro_message_container = lo_message_container.<BR /> <BR /> """""""""""""""""""""" READ REQUEST DATA """"""""""""""""""""""""""""""""""""""""<BR /> <BR /> io_data_provider-&gt;read_entry_data( IMPORTING es_data = ls_request_input_data ).<BR /> <BR /> <BR /> """"""""""""""" INSERT DATA TO TABLE WITH BAPI """""""""""""""""""""""""<BR /> <BR /> <BR /> * Populate material data structure<BR /> ls_headdata-material = ls_request_input_data-matnr.<BR /> ls_headdata-ind_sector = ls_request_input_data-indsec.<BR /> ls_headdata-matl_type = ls_request_input_data-mattype.<BR /> ls_headdata-basic_view = 'X'.<BR /> <BR /> ls_clientdata-base_uom = ls_request_input_data-baseuom.<BR /> ls_clientdatax-base_uom = 'X'.<BR /> <BR /> IF ls_request_input_data-matdescen IS NOT INITIAL.<BR /> ls_materialdesc-langu = 'EN'.<BR /> ls_materialdesc-matl_desc = ls_request_input_data-matdescen.<BR /> APPEND ls_materialdesc TO lt_materialdesc.<BR /> ENDIF.<BR /> <BR /> IF ls_request_input_data-matdesctr IS NOT INITIAL.<BR /> ls_materialdesc-langu = 'TR'.<BR /> ls_materialdesc-matl_desc = ls_request_input_data-matdesctr.<BR /> APPEND ls_materialdesc TO lt_materialdesc.<BR /> ENDIF.<BR /> <BR /> * Call BAPI to create material<BR /> CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA'<BR /> EXPORTING<BR /> headdata = ls_headdata<BR /> clientdata = ls_clientdata<BR /> clientdatax = ls_clientdatax<BR /> IMPORTING<BR /> return = ls_return<BR /> TABLES<BR /> materialdescription = lt_materialdesc.<BR /> <BR /> * Check for errors and return message<BR /> IF ls_return IS NOT INITIAL.<BR /> CALL METHOD lo_message_container-&gt;add_message_from_bapi<BR /> EXPORTING<BR /> is_bapi_message = ls_return<BR /> iv_add_to_response_header = abap_true " Flag for adding or not the message to the response header<BR /> iv_message_target = CONV string( ls_return-field ).<BR /> EXIT.<BR /> ELSE.<BR /> CALL METHOD lo_message_container-&gt;add_message<BR /> EXPORTING<BR /> iv_msg_type = /iwbep/cl_cos_logger=&gt;error<BR /> iv_msg_id = 'ZTEST'<BR /> iv_msg_number = '000'<BR /> iv_msg_text = `An error occured.`<BR /> iv_add_to_response_header = abap_true. "add the message to the header<BR /> EXIT.<BR /> ENDIF.<BR /> <BR /> <BR /> er_entity = ls_request_input_data. "Fill Exporting parameter ER_ENTITY</CODE></PRE><BR /> <H3 id="toc-hId--282466350"><SPAN class="notion-enable-hover" data-token-index="0"><STRONG>Step 2.2</STRONG> - </SPAN>Registering oData service<!-- notionvc: e881d88d-9fe7-4ab6-974f-c9ef85f9a223 --></H3><BR /> Register the service in the backend on Tcode – /n/IWFND/MAINT_SERVICE<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/registering-odata-service-1.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/registering-odata-service-2.png" /></P><BR /> You registered service if you want you can test your service by clicking ‘2 - SAP Gateway Client’.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/registering-odata-service-3.png" /></P><BR /> <BR /> <H3 id="toc-hId--478979855"><SPAN class="notion-enable-hover" data-token-index="0"><STRONG>Step 3</STRONG> - </SPAN>Creating Fiori app for excel upload and material creation<!-- notionvc: a39e2c70-df61-4e8a-9c19-515f1742e3fd --></H3><BR /> <H3 id="toc-hId--675493360"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Step 3.1</SPAN></STRONG> - Creating Fiori app<!-- notionvc: b9788609-058a-41da-baed-9c4952cb33fe --></H3><BR /> We'll construct a Fiori app using VSCode alongside the Fiori extension.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-fiori-app-1.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-fiori-app-2.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-fiori-app-3.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-fiori-app-3-1.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-fiori-app-4.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/create-fiori-app-5.png" /></P><BR /> <BR /> <H3 id="toc-hId--947238234"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Step 3.2</SPAN></STRONG> - Adding libraries that will be used for excel import<!-- notionvc: f89d31d1-1137-4285-a254-8e3b89e7148d --></H3><BR /> We need to download the necessary JavaScript libraries for Excel import. Get the files from the links below:<BR /> <UL><BR /> <LI><A href="https://github.com/SheetJS/sheetjs/blob/github/xlsx.js" target="_blank" rel="nofollow noopener noreferrer">xlsx.js</A></LI><BR /> <LI><A href="https://github.com/Stuk/jszip/blob/main/dist/jszip.js" target="_blank" rel="nofollow noopener noreferrer">jszip.js</A></LI><BR /> </UL><BR /> Once downloaded, create a folder named "libs" in the "webapp" directory and place these files inside it. These libraries are crucial for handling Excel imports in your Fiori app.<BR /> <BR /> <!-- notionvc: 2f02be0a-7672-44c6-b2a4-7297c7038344 --><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/add-libraries.png" /></P><BR /> <BR /> <H3 id="toc-hId--1143751739"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Step 3.3</SPAN></STRONG> - Creating global model in manifest.json<!-- notionvc: 97dc2c3d-6b9b-4a41-a8f5-bb3bbaee6a18 --></H3><BR /> We'll establish a JSON model in the manifest.json file to hold the data obtained from Excel and other sources. This model acts as a storage space within your Fiori app.<BR /> <PRE class="language-javascript"><CODE>//...<BR /> "models": {<BR /> "i18n": {<BR /> "type": "sap.ui.model.resource.ResourceModel",<BR /> "settings": {<BR /> "bundleName": "&lt;-- your namespace --&gt;.i18n.i18n"<BR /> }<BR /> },<BR /> "globalModel": {<BR /> "type": "sap.ui.model.json.JSONModel",<BR /> "settings": {},<BR /> "preload": true<BR /> },<BR /> "": {<BR /> "dataSource": "mainService",<BR /> "preload": true,<BR /> "settings": {<BR /> "useBatch": false<BR /> }<BR /> }<BR /> },<BR /> //...</CODE></PRE><BR /> <H3 id="toc-hId--1340265244"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Step 3.4</SPAN></STRONG> - Preparing View<!-- notionvc: 9e4248b6-4c6f-4b0a-b841-03539f5f7df5 --></H3><BR /> <P style="overflow: hidden;margin-bottom: 0px">We'll set up the user interface (UI) for our app using the provided code below. Code includes binding the previously created model to this view. This binding process ensures that the data stored in the model is displayed and accessible within the user interface.<IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/preparing-view.png" /></P><BR /> <BR /> <PRE class="language-markup" style="height: 400px"><CODE>&lt;mvc:View<BR /> controllerName="&lt;-- your namespace --&gt;.controller.Main"<BR /> xmlns:mvc="sap.ui.core.mvc"<BR /> displayBlock="true"<BR /> xmlns="sap.m"<BR /> xmlns:u="sap.ui.unified"<BR /> xmlns:t="sap.ui.table"<BR /> xmlns:l="sap.ui.layout"<BR /> &gt;<BR /> &lt;Page<BR /> id="page"<BR /> title="{i18n&gt;title}"<BR /> class="sapUiContentPadding sapUiContentPadding--header sapUiResponsivePadding--header sapUiResponsivePadding--subHeader sapUiResponsivePadding--content sapUiResponsivePadding--footer"<BR /> showNavButton="false"<BR /> showHeader="true"<BR /> &gt;<BR /> &lt;!-- ========================== --&gt;<BR /> &lt;!-- Excel Bar FlexBox --&gt;<BR /> &lt;!-- ========================== --&gt;<BR /> &lt;FlexBox<BR /> id="_IDGenFlexBox1"<BR /> wrap="Wrap"<BR /> class="sapUiMediumMarginTop"<BR /> justifyContent="Center"<BR /> &gt;<BR /> &lt;Input<BR /> id="searchInput"<BR /> width="100%"<BR /> editable="false"<BR /> value="{globalModel&gt;/excelFileName}"<BR /> &gt;<BR /> &lt;layoutData&gt;<BR /> &lt;FlexItemData<BR /> id="_IDGenFlexItemData1"<BR /> growFactor="6"<BR /> /&gt;<BR /> &lt;/layoutData&gt;<BR /> &lt;/Input&gt;<BR /> &lt;FlexBox id="_IDGenFlexBox2"&gt;<BR /> &lt;u:FileUploader<BR /> id="FileUploaderId"<BR /> class="sapUiSmallMarginBegin"<BR /> buttonText="Excel Upload"<BR /> sameFilenameAllowed="true"<BR /> iconOnly="false"<BR /> buttonOnly="true"<BR /> fileType="XLSX,xlsx"<BR /> icon="sap-icon://excel-attachment"<BR /> multiple="false"<BR /> iconFirst="true"<BR /> style="Emphasized"<BR /> change="handleImportData"<BR /> /&gt;<BR /> &lt;/FlexBox&gt;<BR /> &lt;/FlexBox&gt;<BR /> &lt;!-- ============= --&gt;<BR /> &lt;!-- Table --&gt;<BR /> &lt;!-- ============= --&gt;<BR /> &lt;t:Table<BR /> id="table"<BR /> rows="{<BR /> path:'globalModel&gt;/materialsExcel',<BR /> sorter: {<BR /> path: 'SIMILARITY',<BR /> descending: true<BR /> }<BR /> }"<BR /> selectionMode="MultiToggle"<BR /> ariaLabelledBy="title"<BR /> visibleRowCount="20"<BR /> class="sapUiMediumMarginTop"<BR /> &gt;<BR /> &lt;t:extension&gt;<BR /> &lt;OverflowToolbar<BR /> id="_IDGenOverflowToolbar1"<BR /> style="Clear"<BR /> &gt;<BR /> &lt;Title<BR /> id="title"<BR /> text="Materials"<BR /> /&gt;<BR /> &lt;ToolbarSpacer id="_IDGenToolbarSpacer1" /&gt;<BR /> &lt;Button<BR /> id="uploadButton"<BR /> type="Emphasized"<BR /> enabled="false"<BR /> icon="sap-icon://upload"<BR /> press="onPressUpload"<BR /> text="Upload"<BR /> class="sapUiSmallMarginBegin"<BR /> ariaDescribedBy="acceptButtonDescription genericButtonDescription"<BR /> /&gt;<BR /> &lt;/OverflowToolbar&gt;<BR /> &lt;/t:extension&gt;<BR /> &lt;t:rowSettingsTemplate&gt;<BR /> &lt;t:RowSettings<BR /> id="rowSettings"<BR /> highlight="{globalModel&gt;status}"<BR /> highlightText="{globalModel&gt;statusText}"<BR /> /&gt;<BR /> &lt;/t:rowSettingsTemplate&gt;<BR /> &lt;t:columns&gt;<BR /> &lt;t:Column<BR /> width="8rem"<BR /> id="_IDGenColumn1"<BR /> &gt;<BR /> &lt;Label<BR /> id="_IDGenLabel1"<BR /> text="Status"<BR /> /&gt;<BR /> &lt;t:template&gt;<BR /> &lt;Text<BR /> busy="{globalModel&gt;isBusy}"<BR /> id="_IDGenText2"<BR /> text="{globalModel&gt;statusText}"<BR /> wrapping="false"<BR /> /&gt;<BR /> &lt;/t:template&gt;<BR /> &lt;/t:Column&gt;<BR /> &lt;/t:columns&gt;<BR /> &lt;/t:Table&gt;<BR /> &lt;/Page&gt;<BR /> &lt;/mvc:View&gt;</CODE></PRE><BR /> <H3 id="toc-hId--1536778749"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Step 3.5</SPAN></STRONG> - Preparing Controller<!-- notionvc: 03b19504-6df8-4c93-8e68-d9dbec601b11 --></H3><BR /> In this part, while preparing the controller, our aim is to import data from Excel dynamically creating columns based on the Excel data. Subsequently, we'll use this data to generate materials within our system, ensuring a smooth transfer of information from Excel to our system for material creation.<BR /> <PRE class="language-javascript" style="height: 400px"><CODE>sap.ui.define([<BR /> "sap/ui/core/mvc/Controller",<BR /> "../libs/xlsx",<BR /> "../libs/jszip",<BR /> ],<BR /> /**<BR /> * @param {typeof sap.ui.core.mvc.Controller} Controller<BR /> */<BR /> function (Controller, xlsx, jszip) {<BR /> "use strict";<BR /> <BR /> return Controller.extend("&lt;-- your namespace --&gt;.controller.Main", {<BR /> onInit: function () {<BR /> <BR /> },<BR /> handleImportData: function (oEvent) {<BR /> this._import(oEvent.getParameter("files") &amp;&amp; oEvent.getParameter("files")[0]);<BR /> },<BR /> _import: function (file) {<BR /> var that = this;<BR /> var excelData = {};<BR /> if (file &amp;&amp; window.FileReader) {<BR /> that.getView().getModel('globalModel').setProperty("/excelFileName", file.name);<BR /> var reader = new FileReader();<BR /> reader.onload = function (e) {<BR /> var data = e.target.result;<BR /> var workbook = XLSX.read(data, {<BR /> type: 'binary'<BR /> });<BR /> <BR /> for (var i in workbook.SheetNames) {<BR /> excelData = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[workbook.SheetNames[i]]);<BR /> break;<BR /> }<BR /> <BR /> for (let index = 0; index &lt; excelData.length; index++) {<BR /> excelData[index].statusText = 'Ready to Upload';<BR /> excelData[index].status = 'Information';<BR /> excelData[index].isBusy = false;<BR /> }<BR /> <BR /> that.getView().getModel('globalModel').setProperty("/materialsExcel", excelData);<BR /> that._prepareTableColumns()<BR /> <BR /> };<BR /> reader.onerror = function (ex) {<BR /> console.log(ex);<BR /> };<BR /> reader.readAsBinaryString(file);<BR /> }<BR /> },<BR /> onPressUpload: function (oEvent) {<BR /> var oTable = this.getView().byId('table');<BR /> var aSelectedIndex = oTable.getSelectedIndices();<BR /> var aMaterials = this.getView().getModel('globalModel').getProperty("/materialsExcel");<BR /> var aUploadMaterial = [];<BR /> <BR /> <BR /> aSelectedIndex.map(element =&gt; {<BR /> var aSelectedMat = aMaterials[element];<BR /> <BR /> <BR /> var oCreateData = {<BR /> "materialNum": aSelectedMat['PartNo'],<BR /> "matDescEN": aSelectedMat['DESCRIPTION'],<BR /> "baseUOM": aSelectedMat['BaseUOM'],<BR /> "matType": aSelectedMat['MatType'],<BR /> "indSector": aSelectedMat['IndustrialSector']<BR /> }<BR /> <BR /> this.getView().getModel('globalModel').setProperty(`/materialsExcel/${element}/isBusy`, true);<BR /> <BR /> this.getOwnerComponent().getModel().create("/createMaterialSet", oCreateData, {<BR /> success: $.proxy(function (data, resp) {<BR /> this.getView().getModel('globalModel').setProperty(`/materialsExcel/${element}/isBusy`, false);<BR /> this.getView().getModel('globalModel').setProperty(`/materialsExcel/${element}/status`, 'Success');<BR /> this.getView().getModel('globalModel').setProperty(`/materialsExcel/${element}/statusText`, 'Material Created');<BR /> },<BR /> this),<BR /> error: $.proxy(function (oError) {<BR /> this.getView().getModel('globalModel').setProperty(`/materialsExcel/${element}/isBusy`, false);<BR /> this.getView().getModel('globalModel').setProperty(`/materialsExcel/${element}/status`, 'Error');<BR /> this.getView().getModel('globalModel').setProperty(`/materialsExcel/${element}/statusText`, 'An error Occured');<BR /> }, this)<BR /> });<BR /> <BR /> });<BR /> <BR /> },<BR /> _prepareTableColumns: function () {<BR /> var oTable = this.getView().byId('table');<BR /> var oTableExtPoint = new sap.ui.table.extensions.Pointer(oTable);<BR /> var aTableCatalog = Object.keys(this.getView().getModel('globalModel').getProperty("/materialsExcel")[0])<BR /> <BR /> aTableCatalog.map(aElement =&gt; {<BR /> if (aElement !== 'statusText' &amp;&amp; aElement !== 'status' &amp;&amp; aElement !== 'isBusy') {<BR /> var oLabel = new sap.m.Title({<BR /> text: aElement<BR /> });<BR /> <BR /> var oTemplate = new sap.m.Text({ text: `{globalModel&gt;${aElement}}`, wrapping: false })<BR /> var oColumn = new sap.ui.table.Column({<BR /> label: oLabel,<BR /> width: "10rem",<BR /> template: oTemplate,<BR /> filterProperty: aElement,<BR /> defaultFilterOperator: "Contains"<BR /> })<BR /> <BR /> oTable.addColumn(oColumn);<BR /> }<BR /> })<BR /> <BR /> var aTableColumns = oTable.getColumns();<BR /> for (var i = aTableColumns.length; i &gt;= 0; i--) {<BR /> oTableExtPoint.doAutoResizeColumn(i);<BR /> }<BR /> <BR /> this.getView().byId('FileUploaderId').setEnabled(false);<BR /> this.getView().byId('uploadButton').setEnabled(true);<BR /> <BR /> },<BR /> });<BR /> });</CODE></PRE><BR /> <H3 id="toc-hId--1733292254"><SPAN class="notion-enable-hover" data-token-index="0"><STRONG>Step 4</STRONG> - </SPAN>Use app and check system to ensure creating of materials<!-- notionvc: fec67026-dbf9-456a-807e-b502d44dd2a0 --></H3><BR /> In last step, it's time to utilize the app to create data within your system. Before doing so, it's crucial to emphasize the need to review and tailor the entire system according to your specific requirements. Once revised, proceed to use the app to generate data within your system. This step enables you to validate and ensure the smooth creation of materials as intended in your system.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/use-and-test-app-1.png" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/use-and-test-app-2.png" /></P><BR /> <BR /> <H2 id="toc-hId--1636402752">Conclusion</H2><BR /> To wrap up, using Fiori and Excel to create materials in SAP brings a smoother process to the table. This blog's steps show how to seamlessly integrate data from other data sources, such as SolidWorks. If you want to dive deeper, let’s chat in the comments!<BR /> <BR /> <!-- notionvc: fbeeaf6d-a080-4d6a-8551-fb57bd4def58 --><BR /> <BR /> <!-- notionvc: 06b037bc-9922-492a-805b-d12c38a483ea --><BR /> <BR /> <!-- notionvc: 66749d7e-0deb-4301-a3ae-a41719c91887 --> 2023-12-11T19:46:45+01:00 https://community.sap.com/t5/technology-blogs-by-members/typescript-for-javascript-developers-a-guide/ba-p/13579213 TypeScript for JavaScript developers: A guide 2023-12-13T22:37:28+01:00 martinkoch https://community.sap.com/t5/user/viewprofilepage/user-id/8443 Welcome, JavaScript developers! Today we're diving into the world of TypeScript - a powerful language extension for JavaScript that helps you build larger and more complex applications. TypeScript is like JavaScript, but with superpowers, and here's why.<BR /> <DIV><BR /> <DIV><BR /> <DIV><BR /> <DIV><BR /> <DIV data-testid="conversation-turn-7"><BR /> <DIV><BR /> <DIV><BR /> <DIV><BR /> <DIV><BR /> <DIV></DIV><BR /> </DIV><BR /> </DIV><BR /> </DIV><BR /> </DIV><BR /> </DIV><BR /> </DIV><BR /> </DIV><BR /> </DIV><BR /> </DIV><BR /> <H2 id="toc-hId-964102943">What is TypeScript?</H2><BR /> TypeScript, developed by Microsoft, is an open source language tool based on JavaScript. It offers optional static typing, which means you can assign types to your variables, functions and objects to detect errors earlier in the development process.<BR /> <H2 id="toc-hId-767589438">Why TypeScript?</H2><BR /> <UL><BR /> <LI><STRONG>Earlier error detection</STRONG><BR /> TypeScript catches errors during the development phase, long before they end up in production code.</LI><BR /> <LI><STRONG>Better tool support</STRONG><BR /> Autocomplete, refactoring tools and IntelliSense in IDEs are much more sophisticated for TypeScript.</LI><BR /> <LI><STRONG>Easier scaling</STRONG><BR /> Type systems make your code safer and easier to understand, which is particularly beneficial for large projects.</LI><BR /> </UL><BR /> <H2 id="toc-hId-571075933"><STRONG>Why TypeScript is Important for SAP Fiori Developers</STRONG></H2><BR /> As SAP Fiori evolves to deliver more sophisticated and user-friendly applications, the need for robust development practices becomes essential. This is where TypeScript enters the scene for Fiori developers. It's not just another technology to learn; it's a strategic investment in code quality, maintainability, and productivity.<BR /> <BR /> TypeScript offers Fiori developers the ability to write more predictable and self-documenting code, which is crucial when dealing with complex UIs and integrations that Fiori applications often require. The static typing system catches errors at compile time, reducing runtime issues that can be costly in a business environment.<BR /> <BR /> Moreover, TypeScript's compatibility with modern JavaScript features and its ability to transpile to a version compatible with SAPUI5's core, which is based on older JavaScript versions, ensures that developers are not restricted by the platform's limitations. They can use the latest features of the language without worrying about browser compatibility.<BR /> <BR /> Lastly, TypeScript's tooling and IntelliSense features integrated into development environments like SAP Business Application Studio greatly enhance the developer experience. This leads to a more efficient development process, allowing for quicker iterations and a focus on creating value through innovative features rather than fixing bugs.<BR /> <BR /> Embracing TypeScript is a forward-looking approach for SAP Fiori developers focused on building robust, enterprise-grade applications.<BR /> <H2 id="toc-hId-374562428">Basic concepts of TypeScript</H2><BR /> <H3 id="toc-hId-307131642">Types</H3><BR /> TypeScript supports both primitive types (such as string, number, boolean) and complex types.<BR /> <PRE class="language-javascript"><CODE>let message: string = "Hallo, Welt!";<BR /> let total: number = 120;<BR /> let isLoggedIn: boolean = false;<BR /> </CODE></PRE><BR /> <H3 id="toc-hId-110618137">Interfaces</H3><BR /> You can use interfaces to define the form of objects, which helps with object orientation.<BR /> <PRE class="language-javascript"><CODE>interface User {<BR /> name: string;<BR /> age: number;<BR /> }<BR /> <BR /> let user: User = { name: "Max", age: 30 };</CODE></PRE><BR /> <H3 id="toc-hId--85895368">Classes and inheritance</H3><BR /> TypeScript supports modern OO principles, including classes and inheritance.<BR /> <PRE class="language-javascript"><CODE>class Person {<BR /> name: string;<BR /> constructor(name: string) {<BR /> this.name = name;<BR /> }<BR /> }<BR /> <BR /> class Employee extends Person {<BR /> employeeId: number;<BR /> constructor(name: string, employeeId: number) {<BR /> super(name);<BR /> this.employeeId = employeeId;<BR /> }<BR /> }<BR /> </CODE></PRE><BR /> <H3 id="toc-hId--282408873">Generics</H3><BR /> Generics offer a way to create reusable components.<BR /> <PRE class="language-javascript"><CODE>function insertAtBeginning&lt;T&gt;(array: T[], value: T) {<BR /> return [value, ...array];<BR /> }<BR /> <BR /> let demoArray = [1, 2, 3];<BR /> let updatedArray = insertAtBeginning(demoArray, 0); // [0, 1, 2, 3]<BR /> </CODE></PRE><BR /> <H2 id="toc-hId--608005097">Getting Started with TypeScript in Visual Studio Code</H2><BR /> Visual Studio Code (VS Code) is a popular editor among developers, and it's well-equipped for TypeScript. If you're transitioning from JavaScript, you'll find VS Code's TypeScript support incredibly helpful. Here's how to set up TypeScript in VS Code.<BR /> <H3 id="toc-hId--675435883">Step 1: Install Node.js and npm</H3><BR /> Before you work with TypeScript, you need to have Node.js and npm installed. They come bundled together, and you can download them from the official <A href="https://nodejs.org/" target="_new" rel="noopener nofollow noreferrer">Node.js website</A>.<BR /> <H3 id="toc-hId--947180757">Step 2: Install TypeScript</H3><BR /> With npm installed, you can now install TypeScript globally on your machine by running the following command in your terminal:<BR /> <PRE class="language-perl"><CODE>npm install -g typescript</CODE></PRE><BR /> <H3 id="toc-hId--1143694262">Step 3: Open Your Project in VS Code</H3><BR /> Launch VS Code and open the folder containing your project. If you're starting from scratch, you can create a new folder and open it in VS Code.<BR /> <H3 id="toc-hId--1340207767">Step 4: Initialize a New TypeScript Project</H3><BR /> In the terminal integrated in VS Code (you can open it with <CODE>Ctrl+</CODE>), type the following command to create a <CODE>tsconfig.json</CODE> file, which is the configuration file for TypeScript:<BR /> <PRE class="language-perl"><CODE>tsc --init</CODE></PRE><BR /> This command creates a <CODE>tsconfig.json</CODE> file with default settings that you can customize as needed.<BR /> <H3 id="toc-hId--1536721272">Step 5: Write TypeScript Code</H3><BR /> Create a new file with the <CODE>.ts</CODE> extension. VS Code automatically recognizes it as a TypeScript file. You can now start typing your TypeScript code. For instance:<BR /> <PRE class="language-javascript"><CODE>function greet(person: string): string {<BR /> return `Hello, ${person}!`;<BR /> }<BR /> <BR /> const user = "Developer";<BR /> console.log(greet(user));</CODE></PRE><BR /> <H3 id="toc-hId--1733234777">Step 6: Compiling TypeScript to JavaScript</H3><BR /> To compile your TypeScript file to JavaScript, you can run the TypeScript compiler in the terminal:<BR /> <PRE class="language-perl"><CODE>tsc</CODE></PRE><BR /> If you have a <CODE>tsconfig.json</CODE> file in your project, running <CODE>tsc</CODE> without any input files will compile all files specified in that configuration.<BR /> <H3 id="toc-hId--1929748282">Step 7: Enable Automatic Compilation</H3><BR /> To make your development process smoother, you can enable automatic compilation of TypeScript files on save. To do this, press <CODE>Ctrl+Shift+P</CODE> to open the command palette and type 'Tasks: Configure Default Build Task'. Choose <CODE>tsc: watch - tsconfig.json</CODE>. This will compile your TypeScript files every time you save them.<BR /> <H3 id="toc-hId--2126261787">Step 8: Explore TypeScript Features in VS Code</H3><BR /> VS Code offers powerful features for TypeScript development, such as IntelliSense, code navigation, and refactoring tools. Take advantage of these features to boost your productivity.<BR /> <H3 id="toc-hId-1972192004">Step 9: Install TypeScript Declaration Files</H3><BR /> For using certain JavaScript libraries with TypeScript, you might need to install TypeScript declaration files to get type definitions. You can install them using npm. For example, for the popular library lodash, you would run:<BR /> <PRE class="language-perl"><CODE>npm install --save @types/lodash</CODE></PRE><BR /> <H3 id="toc-hId-1775678499">Conclusion</H3><BR /> Visual Studio Code and TypeScript together create a robust environment for developing scalable and maintainable JavaScript applications. By following these steps, you'll be well on your way to leveraging the full power of TypeScript in your development workflow. 2023-12-13T22:37:28+01:00 https://community.sap.com/t5/technology-blogs-by-sap/introducing-ui5-tooling-s-build-output-style-feature/ba-p/13579338 Introducing UI5 Tooling's Build Output Style Feature 2023-12-15T15:31:36+01:00 Yavor_I https://community.sap.com/t5/user/viewprofilepage/user-id/178175 <SPAN data-contrast="auto">UI5 Tooling continues to evolve, and with the latest enhancement to the UI5 Builder, developers can now take even more control over how their projects are built. The feature we are excited to introduce is the </SPAN><I><SPAN data-contrast="auto">Output Style</SPAN></I><SPAN data-contrast="auto">, a powerful capability that allows you to get a more efficient and organized build output.&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><SPAN data-contrast="auto">&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <H2 id="toc-hId-964103971">Understanding <I>Output Style</I></H2><BR /> <SPAN data-contrast="auto">In the UI5 world, projects (for example, libraries) come with namespaces that define a structured hierarchy. For instance, OpenUI5 features namespaces like "sap.m," "sap.f," and "sap.ui.core." Typically, when the bundler creates a build, it replicates this structure in the "resources" folder, leading to paths like "/resources/sap/m/RangeSlider.js."&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <BR /> <SPAN data-contrast="auto">With the </SPAN><I><SPAN data-contrast="auto">Output Style</SPAN></I><SPAN data-contrast="auto"> feature, you have the flexibility to omit both this project namespace and the "resources" directory. Imagine "/resources/sap/m/RangeSlider.js" transforming into "./RangeSlider.js." This results in a flat build output.&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <H2 id="toc-hId-767590466">Key Constraints and Considerations</H2><BR /> <SPAN data-contrast="auto">Keep in mind that the Output Style only affects the root project’s build structure. In other words, if dependencies are included into the build bundle, they will be built with their respective </SPAN><SPAN data-contrast="auto">Default</SPAN><SPAN data-contrast="auto"> Output Style. Check the table below for the different possible combinations:</SPAN><SPAN data-ccp-props="{&quot;134233117&quot;:false,&quot;134233118&quot;:false,&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559685&quot;:0,&quot;335559737&quot;:0,&quot;335559738&quot;:0,&quot;335559739&quot;:0,&quot;335559740&quot;:259}">&nbsp;</SPAN><BR /> <TABLE data-tablestyle="MsoNormalTable" data-tablelook="1184"><BR /> <TBODY><BR /> <TR><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Requested Output Style / Project Type</SPAN></B><SPAN data-ccp-props="{&quot;335551550&quot;:2,&quot;335551620&quot;:2,&quot;335559739&quot;:240}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Resulting Style</SPAN></B><SPAN data-ccp-props="{&quot;335551550&quot;:2,&quot;335551620&quot;:2,&quot;335559739&quot;:240}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Default</SPAN></B><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">application</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-contrast="none">Root project is written </SPAN><SPAN data-contrast="none">Flat</SPAN><SPAN data-contrast="none">-style, dependencies in their respective </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-contrast="none">-style</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">library</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-contrast="none">Root project is written </SPAN><SPAN data-contrast="none">Namespace</SPAN><SPAN data-contrast="none">-style, dependencies in their respective </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-contrast="none">-style</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">theme-library</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-contrast="none">Root project is written in the style of the sources (multiple namespaces), dependencies in their respective </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-contrast="none">-style</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">module</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><BR /> <BR /> <SPAN data-contrast="none">Root project is written with the </SPAN><A href="https://sap.github.io/ui5-tooling/stable/pages/Configuration/#available-path-mappings" target="_blank" rel="nofollow noopener noreferrer"><SPAN data-contrast="none">configured paths</SPAN></A><SPAN data-contrast="none">,&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <BR /> <SPAN data-contrast="none">dependencies in their respective </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-contrast="none">-style</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Flat</SPAN></B><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">application</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-contrast="none">Same as </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">library</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><BR /> <BR /> <SPAN data-contrast="none">Root project is written </SPAN><SPAN data-contrast="none">Flat</SPAN><SPAN data-contrast="none">-style (without its namespace, logging warnings for resources outside of it),&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <BR /> <SPAN data-contrast="none">dependencies in their respective </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-contrast="none">-style</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">theme-library</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Unsupported,</SPAN></B><SPAN data-contrast="none"> since a theme-library in most cases has more than one namespace</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">module</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Unsupported,</SPAN></B><SPAN data-contrast="none"> since modules have explicit path mappings configured and no namespace concept</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Namespace</SPAN></B><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">application</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><BR /> <BR /> <SPAN data-contrast="none">Root project is written </SPAN><SPAN data-contrast="none">Namespace</SPAN><SPAN data-contrast="none">-style (resources are prefixed with the project's namespace),&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <BR /> <SPAN data-contrast="none">dependencies are written in their respective </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-contrast="none">-style</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">library</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><SPAN data-contrast="none">Same as </SPAN><SPAN data-contrast="none">Default</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">theme-library</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Unsupported,</SPAN></B><SPAN data-contrast="none"> since a theme-library in most cases has more than one namespace</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> <TR><BR /> <TD data-celllook="0"><SPAN data-contrast="none">module</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> <TD data-celllook="0"><B><SPAN data-contrast="none">Unsupported,</SPAN></B><SPAN data-contrast="none"> since modules have explicit path mappings configured and no namespace concept</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></TD><BR /> </TR><BR /> </TBODY><BR /> </TABLE><BR /> <H2 id="toc-hId-571076961">Using Output Style with UI5 CLI</H2><BR /> <SPAN data-contrast="auto">To harness the power of the Output Style feature, UI5 CLI (with version <STRONG>3.8.0</STRONG> and above) introduces a new option: </SPAN><SPAN data-ccp-props="{&quot;134233117&quot;:false,&quot;134233118&quot;:false,&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559685&quot;:0,&quot;335559737&quot;:0,&quot;335559738&quot;:0,&quot;335559739&quot;:0,&quot;335559740&quot;:259}">&nbsp;</SPAN><BR /> <PRE class="language-css"><CODE>ui5 build --output-style=default | flat | namespace</CODE></PRE><BR /> <SPAN data-contrast="auto">Here are the possible values for</SPAN> <B><I><SPAN data-contrast="auto">--output-style</SPAN></I></B><SPAN data-contrast="auto">:</SPAN><SPAN data-contrast="auto">&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <UL><BR /> <LI data-leveltext="·" data-font="Symbol" data-listid="6" data-list-defn-props="{&quot;335552541&quot;:1,&quot;335559684&quot;:-2,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;·&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}" data-aria-posinset="1" data-aria-level="1"><B><SPAN data-contrast="auto">default</SPAN></B><SPAN data-contrast="auto">: The default directory structure for every project type. For applications this is identical to "flat" and for libraries to "namespace". Other types have a more distinct default output style.</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></LI><BR /> <LI data-leveltext="·" data-font="Symbol" data-listid="6" data-list-defn-props="{&quot;335552541&quot;:1,&quot;335559684&quot;:-2,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;·&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}" data-aria-posinset="2" data-aria-level="1"><B><SPAN data-contrast="auto">flat</SPAN></B><SPAN data-contrast="auto">: Omits the project namespace and the "resources" directory, resulting in a flat build.&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN></LI><BR /> <LI data-leveltext="·" data-font="Symbol" data-listid="6" data-list-defn-props="{&quot;335552541&quot;:1,&quot;335559684&quot;:-2,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769226&quot;:&quot;Symbol&quot;,&quot;469769242&quot;:[8226],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;·&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}" data-aria-posinset="3" data-aria-level="1"><B><SPAN data-contrast="auto">namespace</SPAN></B><SPAN data-contrast="auto">: Respect the project namespace and the "resources" directory, maintaining the original structure.&nbsp;</SPAN> &nbsp;<SPAN style="font-size: 1rem" data-ccp-props="{}">&nbsp;</SPAN></LI><BR /> </UL><BR /> <SPAN data-contrast="auto">For more detailed information and usage guidelines, refer to the official documentation: </SPAN><A href="https://sap.github.io/ui5-tooling/v3/pages/CLI/#ui5-build" target="_blank" rel="nofollow noopener noreferrer"><SPAN data-contrast="none">UI5 CLI - ui5 build</SPAN></A><SPAN data-contrast="auto">&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <H2 id="toc-hId-374563456">Migrating from ui5-task-flatten-library</H2><BR /> <SPAN data-contrast="auto">Previously, achieving a flat build involved using the </SPAN><SPAN data-contrast="auto">ui5-task-flatten-library</SPAN><SPAN data-contrast="auto"> custom task. However, with the integration of this functionality into UI5 Tooling, you are encouraged to migrate your code to the built-in solution. Here's how you can do it:&nbsp;</SPAN><BR /> <OL><BR /> <LI data-leveltext="%1." data-font="Calibri" data-listid="3" data-list-defn-props="{&quot;335552541&quot;:0,&quot;335559684&quot;:-1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}" data-aria-posinset="1" data-aria-level="1"><B><SPAN data-contrast="auto">Remove Custom Task</SPAN></B><SPAN data-contrast="auto">: In your </SPAN><B><SPAN data-contrast="auto">ui5.yam</SPAN></B><SPAN data-contrast="auto">l file, remove the custom task for </SPAN><B><SPAN data-contrast="auto">ui5-task-flatten-library</SPAN></B><SPAN data-contrast="auto">:&nbsp;</SPAN><SPAN data-ccp-props="{}"><BR /> </SPAN><BR /> <PRE class="language-css"><CODE>builder: <BR /> customTasks: <BR /> - name: ui5-task-flatten-library <BR /> afterTask: generateResourcesJson </CODE></PRE><BR /> </LI><BR /> <LI data-leveltext="%1." data-font="Calibri" data-listid="3" data-list-defn-props="{&quot;335552541&quot;:0,&quot;335559684&quot;:-1,&quot;335559685&quot;:720,&quot;335559991&quot;:360,&quot;469769242&quot;:[65533,0],&quot;469777803&quot;:&quot;left&quot;,&quot;469777804&quot;:&quot;%1.&quot;,&quot;469777815&quot;:&quot;hybridMultilevel&quot;}" data-aria-posinset="1" data-aria-level="1"><B><SPAN data-contrast="auto">Remove</SPAN></B><B><SPAN data-contrast="auto"> Dependency</SPAN></B><SPAN data-contrast="auto">: Remove </SPAN><B><SPAN data-contrast="auto">ui5-task-flatten-library</SPAN></B><SPAN data-contrast="auto"> from </SPAN><B><SPAN data-contrast="auto">package.json</SPAN></B><SPAN data-contrast="auto">.</SPAN></LI><BR /> <LI><B><SPAN data-contrast="auto">Update Build Command</SPAN></B><SPAN data-contrast="auto">: When executing a "</SPAN><B><I><SPAN data-contrast="auto">ui5 build</SPAN></I></B><SPAN data-contrast="auto">" command, include the new option </SPAN><B><I><SPAN data-contrast="auto">"--output-style=flat"</SPAN></I></B></LI><BR /> </OL><BR /> <SPAN data-contrast="auto">&nbsp;</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN><BR /> <BR /> <SPAN data-contrast="auto">As the UI5 Tooling team continues to enhance and streamline the development experience, </SPAN><SPAN data-contrast="auto">be sure to stay up to date with the latest features and best practices.</SPAN><SPAN data-ccp-props="{}">&nbsp;</SPAN> 2023-12-15T15:31:36+01:00 https://community.sap.com/t5/open-source-blogs/introducing-openui5-2-x/ba-p/13580633 Introducing OpenUI5 2.x 2023-12-21T13:06:14+01:00 OliverGraeff https://community.sap.com/t5/user/viewprofilepage/user-id/4124 <P><IMG src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/OpenUI5_blue_hotizontal.png" border="0" width="248" height="87" /></P><P><STRONG>Update 13.3.2024</STRONG>: On tool support for adapting an app, see a new blog post '<A href="https://community.sap.com/t5/technology-blogs-by-sap/introducing-ui5-linter/ba-p/13633898" target="_blank">Introducing UI5 linter</A>'</P><H2 id="toc-hId-964762251">Where is OpenUI5 today?</H2><P>OpenUI5 is SAP’s open-source Web UI framework, supporting the <A href="https://www.sap.com/products/technology-platform/fiori.html" target="_blank" rel="noopener noreferrer">SAP Fiori design system</A> for products. It celebrates its <A href="https://openui5.org/anniversary/" target="_blank" rel="noopener nofollow noreferrer">10th anniversary</A>, with many highlights that have shaped an incredible developer community. OpenUI5 will continue to innovate, ensuring a consistent and modern user experience for web applications.<BR /><BR />The size of OpenUI5 grew with its capabilities, with its APIs and with its number of controls, especially as old features were not removed for compatibility reasons, to stay upgrade compatible. This compatibility is a challenge in Web development: Web development standards evolve, new security gaps continuously appear and need to be closed, Web browsers evolve and have new features to take advantage of (e.g. for better performance). Therefore, it is not an option for modern UI technologies to rest on a status quo. As many developers rely on a stable and secure UI technology, OpenUI5 needs to innovate in time to stay competitive and future proof in this dynamic environment. On the other hand, the evolution of Web technologies also brings great value and opportunities to innovate, both for developers and well as with an improved and faster user experience for business users.</P><H2 id="toc-hId-768248746">The next step: OpenUI5 2.x</H2><P>To be able to continue with this successful journey, we need to make sure that:</P><UL><UL><LI>UIs are staying interactive with less code to be loaded and by avoiding synchronous JavaScript.</LI></UL></UL><UL><UL><LI>Security can be ensured with the strict Content Security Policy (CSP).</LI></UL></UL><UL><UL><LI>Development of OpenUI5 framework as well as application developers remain efficient.</LI></UL></UL><UL><UL><LI>Applications are compliant with the evolution of browsers (and restrictions they introduce).</LI></UL></UL><P><BR />For this the UI5 team needs to remove non-compliant, deprecated libraries and functionality from the OpenUI5 framework. In doing so, we need to find the right balance between upgrade stability and agility for innovations.</P><BLOCKQUOTE><BR /><P><STRONG>OpenUI5 2.x is renewing OpenUI5</STRONG><BR /><STRONG>by removing everything not required to comply with OpenUI5 best practices.</STRONG></P></BLOCKQUOTE><P>&nbsp;</P><H2 id="toc-hId-571735241">The value of OpenUI5 2.x</H2><P>OpenUI5 2.x allows to evolve in these areas:</P><UL><UL><LI><STRONG>Security</STRONG>: OpenUI5 2.x enforces an improved security with strict Content Security Policy (CSP).</LI></UL></UL><UL><UL><LI><STRONG>Smooth UI performance</STRONG>: OpenUI5 2.x assists with an improved UI performance by using fine granular tasks and asynchronous JavaScript only.</LI></UL></UL><UL><UL><LI><STRONG>General performance</STRONG>: With the modular core, new bootstrap and additional opportunities to improve performance we expect to see a better experience when launching and using OpenUI5 apps.</LI></UL></UL><UL><UL><LI><STRONG>Micro frontends</STRONG>: The removal of Globals simplifies the orchestration of micro frontends.</LI></UL></UL><UL><UL><LI><STRONG>App development</STRONG>: With streamlined APIs application developers learn and develop with OpenUI5 more easily.</LI></UL></UL><UL><UL><LI><STRONG>Best practices</STRONG>: OpenUI5 2.x means less effort for application developers to follow OpenUI5 best practices.</LI></UL></UL><UL><UL><LI><STRONG>Browser</STRONG>: OpenUI5 2.x makes it easier to stay up to date with the browser evolution.</LI></UL></UL><UL><UL><LI><STRONG>Web standards</STRONG>: OpenUI5 2.x enables a simple adoption of latest Web standards, e.g. CSS custom properties.</LI></UL></UL><UL><UL><LI><STRONG>Pace of innovation</STRONG>: It becomes easier for the UI5 team as well as for application developers to make progress. At the same time, less resources are needed for maintenance, shifting the focus towards innovative, yet stable capabilities.</LI></UL></UL><P>&nbsp;</P><H2 id="toc-hId-375221736">Recommendation for moving from OpenUI5 1.x to 2.x</H2><BLOCKQUOTE><BR /><P><STRONG>Follow the latest <A href="https://sdk.openui5.org/topic/28fcd55b04654977b63dacbee0552712" target="_blank" rel="noopener nofollow noreferrer">Best Practices for App Developers</A> of OpenUI5 1.x</STRONG><BR /><STRONG>Applications following these best practices will run smoothly with OpenUI5 2.x, no migration effort needed.</STRONG></P></BLOCKQUOTE><P><IMG src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/299599_Checklist_R.png" border="0" width="125" height="125" /></P><H2 id="toc-hId-178708231">Call to Action</H2><P>The UI5 team now offers a first version of OpenUI5 2.x <A href="https://sdk.openui5.org/nightly/2/index.html" target="_blank" rel="noopener nofollow noreferrer">https://sdk.openui5.org/nightly/2/index.html</A>&nbsp;for testing purposes (Disclaimer: This is a version with daily updates, possible API changes and temporary bugs.)</P><OL><LI>Make yourself familiar with the <A href="https://sdk.openui5.org/topic/28fcd55b04654977b63dacbee0552712" target="_blank" rel="noopener nofollow noreferrer">Best Practices</A>. (Updated instructions will follow.)</LI><LI>Review your application code, e.g. by removing of any usage of deprecated APIs. Test your custom OpenUI5 apps for OpenUI5 2.x compatibility by simply pointing the bootstrap to <A href="https://sdk.openui5.org/nightly/2/resources/sap-ui-core.js" target="_blank" rel="noopener nofollow noreferrer">https://sdk.openui5.org/nightly/2/resources/sap-ui-core.js</A></LI></OL><P>The team continues working on OpenUI5 2.x and the best practices for application development. To support the process of moving an app from 1.x to 2.x. the UI5 team is currently looking into which documentation and tool support can help with the manual migration of custom code.<BR /><BR /><STRONG>Your feedback is highly appreciated:</STRONG> Please add feedback to GitHub by reporting an issue at <A href="https://github.com/SAP/openui5/issues/new" target="_blank" rel="noopener nofollow noreferrer">https://github.com/SAP/openui5/issues/new</A> and prefix the title with "[UI5 2.x]".</P> 2023-12-21T13:06:14+01:00 https://community.sap.com/t5/technology-blogs-by-members/unlocking-possibilities-extending-fiori-elements-apps-with-the-flexible/ba-p/13580440 Unlocking Possibilities: Extending Fiori Elements Apps with the Flexible Programming Model 2023-12-21T20:11:51+01:00 mertuytun https://community.sap.com/t5/user/viewprofilepage/user-id/173555 <H1 id="toc-hId-835677638"><STRONG>Introduction</STRONG></H1><BR /> Welcome to the world of extending SAP Fiori elements OData V4 apps using the Flexible Programming Model. This model offers a hassle-free approach to expanding app functionalities. Whether you prefer coding in SAPUI5 or utilizing our new building blocks, you'll find ample ways to customize your apps with ease. In this blog, I'll provide an overview of the flexible programming model and guide you to the relevant documentation.<BR /> <H3 id="toc-hId-897329571"><STRONG>Summary</STRONG></H3><BR /> In this blog, you'll learn how the Flexible Programming Model simplifies extending SAP Fiori Elements apps. We'll explore customization options, from basic SAPUI5 coding to leveraging new building blocks. By the end, you'll have a clear understanding of how to adapt apps effortlessly while discovering resources for further exploration.<BR /> <H3 id="toc-hId-700816066"></H3><BR /> <H3 id="toc-hId-504302561">Pre-requisites:</H3><BR /> <UL><BR /> <LI>S/4 HANA System</LI><BR /> <LI>Sap Fiori Elements App (OData V4)</LI><BR /> <LI>VSCode with SAP Fiori Tools - Extension Pack</LI><BR /> </UL><BR /> &nbsp;<BR /> <UL><BR /> <LI><STRONG>Extension Points</STRONG><BR /> <UL><BR /> <LI>Custom Form Element</LI><BR /> <LI>Custom Filter</LI><BR /> </UL><BR /> </LI><BR /> <LI><STRONG>Building Blocks</STRONG><BR /> <UL><BR /> <LI>Table</LI><BR /> </UL><BR /> </LI><BR /> <LI><STRONG>Controller Extensions</STRONG><BR /> <UL><BR /> <LI>Edit Flow</LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> &nbsp;<BR /> <H3 id="toc-hId-307789056"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Extension Points</SPAN></STRONG><!-- notionvc: b34be143-b334-44e3-aacb-513be0a984e0 --></H3><BR /> Extension points in the Flexible Programming Model act like customizable slots in SAP Fiori Elements apps. They let you easily add your own custom elements—sections, actions, form elements, filters, header facets, table columns, and even entirely new pages—just like in Fiori elements for OData V2. But now, you can even seamlessly integrate whole new pages into your app's navigation flow, giving you the freedom to personalize your app experience in unique ways.<BR /> <BR /> Here are some examples:<BR /> <H3 id="toc-hId-111275551"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Custom Form Element</SPAN></STRONG><!-- notionvc: c73b1dbe-222f-4f9c-a642-47b203ad5015 --></H3><BR /> Custom form element let you easily add your own unique elements to forms wherever you need in SAP Fiori Elements apps like below.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/custom_form_elements.gif" /></P><BR /> First, we need change manifest.json<BR /> <PRE class="language-javascript"><CODE>//..<BR /> "ActivitySetObjectPage": {<BR /> "type": "Component",<BR /> "id": "ActivitySetObjectPage",<BR /> "name": "sap.fe.templates.ObjectPage",<BR /> "options": {<BR /> "settings": {<BR /> "editableHeaderContent": false,<BR /> "controlConfiguration": {<BR /> "@com.sap.vocabularies.UI.v1.FieldGroup#General": {<BR /> "fields": {<BR /> "customFormElementIdentification1": {<BR /> "template": "activity.list.report.ext.fragment.CustomField",<BR /> "label": "Custom Form Element",<BR /> "position": {<BR /> "placement": "After",<BR /> "anchor": "DataField::be_invoiced"<BR /> }<BR /> }<BR /> }<BR /> }<BR /> }<BR /> }<BR /> }<BR /> }<BR /> //..</CODE></PRE><BR /> In this code, our custom field will come from webapp/ext/fragment/CustomField.fragment.xml which has the label ‘Custom Form Element’ and is placed after the be_invoiced field. We should put our code block just below of ‘editableHeaderContent: false’. ‘activity.list.report’ is a namespace. FieldGroup#General is facet which we located our field.<BR /> <BR /> &nbsp;<BR /> <BR /> Lastly, we will create CustomField.fragment.xml in webapp/ext/fragment/.<BR /> <PRE class="language-markup"><CODE>&lt;core:FragmentDefinition<BR /> xmlns:core="sap.ui.core"<BR /> xmlns="sap.m"<BR /> xmlns:macros="sap.fe.macros"<BR /> &gt;<BR /> &lt;Switch<BR /> id="checkBoxOption2"<BR /> enabled="{ui&gt;/isEditable}"<BR /> state="{be_invoiced}"<BR /> type="AcceptReject"<BR /> /&gt;<BR /> &lt;/core:FragmentDefinition&gt;</CODE></PRE><BR /> There are two important points. First, the binding field that we want for our custom component and managing the edit status of this component with binding necessary UI properties.<BR /> <BR /> &nbsp;<BR /> <H3 id="toc-hId--85237954"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Custom Filter</SPAN></STRONG><!-- notionvc: 26d388c5-0b4e-439d-903c-5c50aa19d6ea --></H3><BR /> Custom filters in the Flexible Programming Model allow you to add personalized filters to refine data exactly how you need in SAP Fiori Elements apps.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/custom-filter.gif" /></P><BR /> <P style="overflow: hidden;margin-bottom: 0px">We need to change the manifest.json.</P><BR /> <BR /> <PRE class="language-javascript"><CODE>//...<BR /> "ActivitySetList": {<BR /> "type": "Component",<BR /> "id": "ActivitySetList",<BR /> "name": "sap.fe.templates.ListReport",<BR /> "options": {<BR /> "settings": {<BR /> "contextPath": "/ActivitySet",<BR /> "variantManagement": "Page",<BR /> "navigation": {<BR /> "ActivitySet": {<BR /> "detail": {<BR /> "route": "ActivitySetObjectPage"<BR /> }<BR /> }<BR /> },<BR /> "controlConfiguration": {<BR /> "@com.sap.vocabularies.UI.v1.SelectionFields": {<BR /> "showClearButton": true,<BR /> "filterFields": {<BR /> "activity_time": {<BR /> "label": "Activity Time",<BR /> "template": "activity.list.report.ext.fragment.CustomFilter",<BR /> "availability": "Default",<BR /> "position": {<BR /> "placement": "After",<BR /> "anchor": "activity_date"<BR /> }<BR /> }<BR /> }<BR /> }<BR /> }<BR /> }<BR /> }<BR /> },<BR /> //...</CODE></PRE><BR /> Our custom filter will come from webapp/ext/fragment/CustomFilter.fragment.xml which has label ‘Activity Time’ and placed After activity_date field with ‘Default’ availability. This custom field for ‘activity_time’ field. We should put our code block inside of settings of List Report Page. ‘activity.list.report’ is namespace.<BR /> <BR /> Now, we will create CustomFilter.fragment.xml in webapp/ext/fragment/.<BR /> <PRE class="language-markup"><CODE>&lt;core:FragmentDefinition<BR /> xmlns:core="sap.ui.core"<BR /> xmlns="sap.m"<BR /> &gt;<BR /> &lt;HBox<BR /> id="_IDGenHBox1234"<BR /> alignItems="Center"<BR /> width="100%"<BR /> core:require="{handler: 'activity/list/report/ext/controller/CustomFilter'}"<BR /> &gt;<BR /> &lt;StepInput<BR /> id="_IDGenStepInput1"<BR /> value="{path: 'filterValues&gt;', type: 'sap.fe.macros.filter.type.Value', formatOptions: { operator: 'LE' }}"<BR /> min="0"<BR /> max="8"<BR /> step="2"<BR /> width="100px"<BR /> /&gt;<BR /> &lt;core:Icon<BR /> id="_IDGenIcon113212"<BR /> src="sap-icon://reset"<BR /> press="handler.onReset"<BR /> class="sapUiSmallMarginBegin"<BR /> /&gt;<BR /> &lt;/HBox&gt;<BR /> &lt;/core:FragmentDefinition&gt;</CODE></PRE><BR /> We have js file to handle press event of reset button. core:require tag in HBox connects our js file to our view. Please note that, we must change dots to slashs in our namespace like that ‘activity.list.report’ to ‘activity/list/report’. Here is CustomFilter.js that is located in webapp/ext/controller.<BR /> <PRE class="language-javascript"><CODE>sap.ui.define(<BR /> ["sap/ui/model/Filter"],<BR /> function (Filter) {<BR /> "use strict";<BR /> <BR /> return {<BR /> onReset: function (oEvent) {<BR /> this.setFilterValues("activity_time");<BR /> }<BR /> };<BR /> }<BR /> );</CODE></PRE><BR /> &nbsp;<BR /> <H3 id="toc-hId--281751459"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Building Blocks</SPAN></STRONG><!-- notionvc: 6be6fe86-6810-4bcf-8f4b-d85c57e7e771 --></H3><BR /> Building blocks, distinct from extension points, serve as reusable elements essential in constructing SAP Fiori elements apps. These components, integral to the app's template, ensure a standardized approach while simplifying development. They're orchestrated consistently by the framework, ensuring automatic compliance with SAP Fiori standards, including functionalities like draft handling and side effects. Leveraging these building blocks streamlines app extension, promising enduring and easily maintainable software.<BR /> <BR /> &nbsp;<BR /> <H3 id="toc-hId--478264964"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Table</SPAN></STRONG></H3><BR /> I added the table building block to my custom section, boosting my app's features but you can also use this building block in your custom fiori app. You can use it directly in your own app without much hassle. The cool part? Table building block is also customizable as you can see I added new custom column. It's a handy feature that makes app development easier and more consistent, giving your app an instant functionality boost.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/building-blocks-table.png" /></P><BR /> <BR /> <PRE class="language-markup"><CODE>&lt;core:FragmentDefinition<BR /> xmlns:core="sap.ui.core"<BR /> xmlns="sap.m"<BR /> xmlns:macros="sap.fe.macros"<BR /> xmlns:mc="sap.suite.ui.microchart"<BR /> &gt;<BR /> &lt;macros:Table<BR /> contextPath="/ActivitySet"<BR /> metaPath="to_today/@com.sap.vocabularies.UI.v1.LineItem"<BR /> id="LineItemTable"<BR /> headerVisible="false"<BR /> readOnly="true"<BR /> enableAutoColumnWidth="true"<BR /> &gt;<BR /> &lt;macros:columns&gt;<BR /> &lt;macrosTable:Column<BR /> id="_IDGenColumn1"<BR /> header="Activity Time/Day"<BR /> importance="High"<BR /> width="10em"<BR /> key="FirstColumnKey"<BR /> &gt;<BR /> &lt;mc:HarveyBallMicroChart<BR /> id="_IDGenHarveyBallMicroChart1"<BR /> size="XS"<BR /> total="8"<BR /> totalScale="hr"<BR /> &gt;<BR /> &lt;mc:items&gt;<BR /> &lt;mc:HarveyBallMicroChartItem<BR /> id="_IDGenHarveyBallMicroChartItem1"<BR /> fraction="{activity_time}"<BR /> color="Good"<BR /> fractionScale="hr"<BR /> /&gt;<BR /> &lt;/mc:items&gt;<BR /> &lt;/mc:HarveyBallMicroChart&gt;<BR /> &lt;/macrosTable:Column&gt;<BR /> &lt;/macros:columns&gt;<BR /> &lt;/macros:Table&gt;<BR /> &lt;/core:FragmentDefinition&gt;<BR /> </CODE></PRE><BR /> ContextPath is your entity name if you want to use entity directly just make metaPath="@com.sap.vocabularies.UI.v1.LineItem" and you can add custom column and bind easily as below.<BR /> <BR /> &nbsp;<BR /> <H3 id="toc-hId--674778469"><STRONG><SPAN class="notion-enable-hover" data-token-index="0">Controller Extensions</SPAN></STRONG><!-- notionvc: 36e2aad5-2795-4fe8-9d89-7e6aedff60b9 --></H3><BR /> Controller Extensions in SAP Fiori elements offer a set of internally used extensions, exposing overrideable methods that provide developers with added flexibility in customizing app behaviors.<BR /> <H3 id="toc-hId--946523343"><STRONG>Edit Flow</STRONG></H3><BR /> The edit flow functionality empowers you to efficiently manage crucial object page events, such as before save, before discard, after edit, and after save, within your Fiori Elements app.<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/edit-flow.gif" /></P><BR /> &nbsp;<BR /> <BR /> First, we need to add these code block to our manifest.json under sap.ui5 to extend controller for ObjectPage.<BR /> <PRE class="language-javascript"><CODE>//..<BR /> "sap.ui5": {<BR /> "extends": {<BR /> "extensions": {<BR /> "sap.ui.controllerExtensions": {<BR /> "sap.fe.templates.ObjectPage.ObjectPageController": {<BR /> "controllerName": "activity.list.report.ext.OPExtend"<BR /> }<BR /> }<BR /> }<BR /> },<BR /> //..</CODE></PRE><BR /> &nbsp;<BR /> <BR /> Then we need to create OPExtend.controller.js under webapp/ext. We will handle every event in edit flow in that js.<BR /> <PRE class="language-javascript"><CODE>sap.ui.define(<BR /> [<BR /> "sap/ui/core/mvc/ControllerExtension",<BR /> "sap/m/Dialog",<BR /> "sap/m/DialogType",<BR /> "sap/m/Text",<BR /> "sap/m/Button",<BR /> "sap/m/ButtonType",<BR /> "sap/m/MessageToast",<BR /> "sap/m/MessageBox"<BR /> ],<BR /> function (ControllerExtension, Dialog, DialogType, Text, Button, ButtonType, MessageToast, MessageBox) {<BR /> "use strict";<BR /> <BR /> return ControllerExtension.extend("activity.list.report.ext.OPExtend", {<BR /> // this section allows to extend lifecycle hooks or override public methods of the base controller<BR /> override: {<BR /> onInit: function () { },<BR /> editFlow: {<BR /> onBeforeSave: function (mParameters) {<BR /> var sTodayDate = new Date().toISOString().split('T')[0];<BR /> return this._createDialog("Activity Date will be changed with today's date.").then(() =&gt; {<BR /> mParameters.context.setProperty('activity_date', sTodayDate)<BR /> });<BR /> }<BR /> },<BR /> },<BR /> _createDialog: async function (sText, mParameters) {<BR /> return new Promise(function (fnResolve, fnReject) {<BR /> var oApproveDialog = new Dialog({<BR /> type: DialogType.Message,<BR /> title: "Confirm",<BR /> content: new Text({ text: sText }),<BR /> beginButton: new Button({<BR /> type: ButtonType.Emphasized,<BR /> text: "Continue",<BR /> press: function () {<BR /> oApproveDialog.close();<BR /> fnResolve();<BR /> }<BR /> }),<BR /> endButton: new Button({<BR /> text: "Cancel",<BR /> press: function () {<BR /> oApproveDialog.close();<BR /> fnReject();<BR /> }<BR /> }),<BR /> escapeHandler: (pCloseDialog) =&gt; {<BR /> pCloseDialog.resolve();<BR /> fnReject();<BR /> }<BR /> });<BR /> oApproveDialog.open();<BR /> });<BR /> }<BR /> });<BR /> }<BR /> );</CODE></PRE><BR /> This code allows you to change activity date with today’s date before save event.<BR /> <BR /> &nbsp;<BR /> <BLOCKQUOTE>Remember, Guided Development is another valuable tool that streamlines your app extension at various stages. It's a powerful aid in making your development process smoother and more efficient.</BLOCKQUOTE><BR /> <H2 id="toc-hId--849633841"></H2><BR /> <H2 id="toc-hId--1046147346">Conclusion</H2><BR /> In simple terms, the Flexible Programming Model helps customize apps easily and control how they work. These examples are just the beginning to explore how it all works. Let's dive in together and discover more about how to create and shape apps using this logic. Share your thoughts too — let's explore and learn together!<BR /> <BR /> &nbsp;<BR /> <H2 id="toc-hId--1242660851"><STRONG>References</STRONG></H2><BR /> <A href="https://sapui5.hana.ondemand.com/test-resources/sap/fe/core/fpmExplorer/index.html" target="_blank" rel="nofollow noopener noreferrer">SAP Fiori Elements: Flexible Programming Model Explore</A><BR /> <BR /> <!-- notionvc: d2756756-3bb9-4d49-bcde-fbef5bc3f4f6 --><BR /> <BR /> <!-- notionvc: 6ebc968c-c6d6-42f7-a683-416005295b9f --><BR /> <BR /> <!-- notionvc: a4d45970-09ad-4d28-82e5-59f243ee7b57 --><BR /> <BR /> <!-- notionvc: 9abb60b0-7a90-4d67-b568-b830c9987261 --><BR /> <BR /> <!-- notionvc: e78875e7-abc3-43b8-b667-3ccf631674c8 --><BR /> <DIV><BR /> <DIV></DIV><BR /> </DIV> 2023-12-21T20:11:51+01:00 https://community.sap.com/t5/technology-blogs-by-members/a-scripting-guide-for-master-data-crud-operations-in-sap-analytics-cloud/ba-p/13571806 A Scripting Guide For Master Data CRUD Operations in SAP Analytics Cloud Stories 2023-12-24T16:28:18+01:00 rohitchouhan https://community.sap.com/t5/user/viewprofilepage/user-id/782213 <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/New-Project.jpg" /></P><BR /> SAP Analytics Cloud is an extremely useful business tool for data analysis. It consists of various components, each with individual features to facilitate analysis. In the realm of data analysis, we employ stories and analytical applications. Today, we will focus on understanding how to create, read, update, and delete (CRUD) with model master data using Planning Model scripting<BR /> <BR /> There is four main functions for Planning Model Scripting Object<BR /> <TABLE style="border-collapse: collapse;width: 100%" border="1"><BR /> <TBODY><BR /> <TR style="height: 13px"><BR /> <TD style="width: 50%;height: 13px"><SPAN style="color: #003300"><STRONG>Function</STRONG></SPAN></TD><BR /> <TD style="width: 50%;height: 13px"><SPAN style="color: #003300"><STRONG>Description</STRONG></SPAN></TD><BR /> </TR><BR /> <TR style="height: 13px"><BR /> <TD style="width: 50%;height: 13px"><A class="codelink" href="https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html#PlanningModel_McreateMembers" target="_blank" rel="noopener noreferrer"><SPAN class="idAnchorText">createMembers</SPAN></A></TD><BR /> <TD style="width: 50%;height: 13px">Create new master data in model</TD><BR /> </TR><BR /> <TR style="height: 13px"><BR /> <TD style="width: 50%;height: 13px"><SPAN class="idAnchorText"><A class="codelink" href="https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html#PlanningModel_MgetMember" target="_blank" rel="noopener noreferrer">getMember</A> and <A class="codelink" href="https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html#PlanningModel_MgetMembers" target="_blank" rel="noopener noreferrer">getMembers</A></SPAN></TD><BR /> <TD style="width: 50%;height: 13px">Read single or multiple master data</TD><BR /> </TR><BR /> <TR style="height: 13px"><BR /> <TD style="width: 50%;height: 13px"><SPAN class="idAnchorText"><A class="codelink" href="https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html#PlanningModel_MupdateMembers" target="_blank" rel="noopener noreferrer">updateMembers</A></SPAN></TD><BR /> <TD style="width: 50%;height: 13px">Update exist master data in model</TD><BR /> </TR><BR /> <TR style="height: 13px"><BR /> <TD style="width: 50%;height: 13px"><A class="codelink" href="https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html#PlanningModel_MdeleteMembers" target="_blank" rel="noopener noreferrer"><SPAN class="idAnchorText">deleteMembers</SPAN></A></TD><BR /> <TD style="width: 50%;height: 13px">Delete exist master data in model</TD><BR /> </TR><BR /> </TBODY><BR /> </TABLE><BR /> <EM><STRONG>Note:</STRONG> Planning Model Script Object only supports Generic dimensions, its not support date, version, account and organization dimensions.</EM><BR /> <BR /> Before staring scripting, you need to create Planning Model Script Object, <A href="https://blogs.sap.com/2022/06/14/update-master-data-of-dimension-using-javascript-in-analytical-application/" target="_blank" rel="noopener noreferrer">Follow this blog</A> to know how to create Planning Model step by step.<BR /> <BR /> <STRONG>Video is also available with full guidance step by step</STRONG><BR /> <BR /> <IFRAME width="560" height="315" src="https://www.youtube.com/embed/D3GKd_MJiCQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></IFRAME><BR /> <H2 id="toc-hId-963870353">1. Create Member</H2><BR /> For creating new master data in model, we have to use createMembers function.<BR /> <DIV><BR /> <UL><BR /> <LI><STRONG>Parameters:</STRONG><BR /> <OL><BR /> <LI><CODE>dimensionId</CODE> (Type: string)<BR /> <UL><BR /> <LI>Represents the ID of the dimension for which the members will be updated.</LI><BR /> </UL><BR /> </LI><BR /> <LI><CODE>members</CODE> (Type: <CODE>PlanningModelMember JSON | PlanningModelMember[] JSON</CODE>)<BR /> <UL><BR /> <LI>Represents the member or an array of members in JSON format that contain the updated information.</LI><BR /> </UL><BR /> </LI><BR /> </OL><BR /> </LI><BR /> <LI><STRONG>Return Type:</STRONG><BR /> <UL><BR /> <LI><CODE>boolean</CODE><BR /> <UL><BR /> <LI>The function is expected to return <CODE>true</CODE> if the update operation was successful, and <CODE>false</CODE> otherwise.</LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> </DIV><BR /> <OL><BR /> <LI><STRONG>Variable Declaration:</STRONG><BR /> <DIV><BR /> <PRE class="language-markup"><CODE>var dimensionId = "CARS";</CODE></PRE><BR /> </DIV><BR /> This line declares a variable <CODE>dimensionId</CODE> and assigns it the value "CARS." This variable represents the ID of the dimension where the new member will be created.</LI><BR /> <LI><STRONG>Member Object Definition:</STRONG><BR /> <DIV><BR /> <PRE class="language-javascript"><CODE>members = {<BR /> id:"Audi",<BR /> description:"Audi Car",<BR /> properties:{<BR /> car_model:"A4"<BR /> }<BR /> };</CODE></PRE><BR /> </DIV><BR /> &nbsp;</LI><BR /> <LI>Create variable with name '<STRONG>members</STRONG>' as <STRONG>Script Variable&nbsp;</STRONG>with data type of <STRONG><CODE>PlanningModelMember</CODE></STRONG></LI><BR /> <LI>The <CODE>members</CODE> object is defined with properties for the new member. It has an ID ("Audi"), a description ("Audi Car"), and additional properties, such as "car_model" with a value of "A4."</LI><BR /> <LI><STRONG>Function Call to Create Members:</STRONG><BR /> <DIV><BR /> <PRE class="language-javascript"><CODE>PlanningModel_1.createMembers(dimensionId, members);</CODE></PRE><BR /> </DIV><BR /> This line calls the <CODE>createMembers</CODE> function of the <CODE>PlanningModel_1</CODE> object, passing the <CODE>dimensionId</CODE> and <CODE>members</CODE> as parameters. This function is responsible for creating planning model members. If the operation is successful, it typically returns <CODE>true</CODE>; otherwise, it returns <CODE>false</CODE>.</LI><BR /> </OL><BR /> Result:-<BR /> <H2 id="toc-hId-767356848"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/created.png" /></H2><BR /> <H2 id="toc-hId-570843343">2. Get Member</H2><BR /> <UL><BR /> <LI><STRONG>Parameters:</STRONG><BR /> <OL><BR /> <LI><CODE>dimensionId</CODE> (Type: string)<BR /> <UL><BR /> <LI>Represents the ID of the dimension from which the member will be retrieved.</LI><BR /> </UL><BR /> </LI><BR /> <LI><CODE>memberId</CODE> (Type: string)<BR /> <UL><BR /> <LI>Represents the ID of the specific member to be retrieved.</LI><BR /> </UL><BR /> </LI><BR /> </OL><BR /> </LI><BR /> <LI><STRONG>Return Type:</STRONG><BR /> <UL><BR /> <LI><CODE>PlanningModelMember</CODE><BR /> <UL><BR /> <LI>The function is expected to return an object of type <CODE>PlanningModelMember</CODE>, which likely contains information about the specified planning model member.</LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> Here's how you might use this function in a practical example:<BR /> <DIV><BR /> <PRE class="language-javascript"><CODE>/* Get single master data */<BR /> <BR /> var dimensionId = "CARS";<BR /> var memberId = "Audi";<BR /> <BR /> var member = PlanningModel_1.getMember(dimensionId, memberId);<BR /> <BR /> // Now 'member' contains information about the specified planning model member.<BR /> // You can access properties of the member object as needed.<BR /> <BR /> /* Get all master data */<BR /> <BR /> var all_members = PlanningModel_1.getMembers(dimensionId);<BR /> <BR /> //Now 'all_members' contains information about the all planning model member.</CODE></PRE><BR /> </DIV><BR /> In this example, the function is called with a specific <CODE>dimensionId</CODE> ("CARS") and <CODE>memberId</CODE> ("Audi"). The returned <CODE>member</CODE> object likely contains details such as the member's ID, description, and other relevant properties specific to the planning model in SAP Analytics Cloud.<BR /> <PRE class="language-javascript"><CODE>//Output of member variable<BR /> {<BR /> "id": "Audi",<BR /> "description": "Audi Car",<BR /> "properties": {<BR /> "car_model": "A4"<BR /> },<BR /> "hierarchies": {}<BR /> }<BR /> <BR /> //Output of all_members varibale<BR /> [<BR /> {<BR /> "id": "#",<BR /> "description": "Unassigned",<BR /> "properties": {<BR /> "car_model": ""<BR /> },<BR /> "hierarchies": {}<BR /> },<BR /> {<BR /> "id": "Audi",<BR /> "description": "Audi Car",<BR /> "properties": {<BR /> "car_model": "A4"<BR /> },<BR /> "hierarchies": {}<BR /> },<BR /> {<BR /> "id": "Bugatti",<BR /> "description": "Bugatti Chiron",<BR /> "properties": {<BR /> "car_model": "Sport 110 Ans"<BR /> },<BR /> "hierarchies": {}<BR /> }<BR /> ]</CODE></PRE><BR /> <H2 id="toc-hId-374329838">3. Update Member</H2><BR /> <UL><BR /> <LI><STRONG>Parameters:</STRONG><BR /> <OL><BR /> <LI><CODE>dimensionId</CODE> (Type: string)<BR /> <UL><BR /> <LI>Represents the ID of the dimension for which the members will be updated.</LI><BR /> </UL><BR /> </LI><BR /> <LI><CODE>members</CODE> (Type: <CODE>PlanningModelMember JSON | PlanningModelMember[] JSON</CODE>)<BR /> <UL><BR /> <LI>Represents the member or an array of members in JSON format that contain the updated information.</LI><BR /> </UL><BR /> </LI><BR /> </OL><BR /> </LI><BR /> <LI><STRONG>Return Type:</STRONG><BR /> <UL><BR /> <LI><CODE>boolean</CODE><BR /> <UL><BR /> <LI>The function is expected to return <CODE>true</CODE> if the update operation was successful, and <CODE>false</CODE> otherwise.</LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> </LI><BR /> <LI><STRONG>Note:</STRONG><BR /> <UL><BR /> <LI>After performing the update, it is recommended to call certain refresh methods (<CODE>DataSource.refreshData()</CODE> or <CODE>Application.refreshData()</CODE>) to ensure that charts or tables reflect the updated members in subsequent operations.</LI><BR /> </UL><BR /> </LI><BR /> <LI>Create variable with name '<STRONG>members</STRONG>' as <STRONG>Script Variable&nbsp;</STRONG>with data type of <STRONG><CODE>PlanningModelMember</CODE></STRONG></LI><BR /> </UL><BR /> Here's how you might use this function in a practical example:<BR /> <PRE class="language-javascript"><CODE>var dimensionId = "CARS";<BR /> members = {<BR /> id: "Audi",<BR /> description: "Audi New Car",<BR /> properties: {<BR /> car_model: "Q3"<BR /> }<BR /> };<BR /> <BR /> var isUpdateSuccessful = PlanningModel_1.updateMembers(dimensionId, members);<BR /> <BR /> // Now 'isUpdateSuccessful' indicates whether the update operation was successful.<BR /> // You can handle further logic based on this result.</CODE></PRE><BR /> In this example, the function is called with a specific <CODE>dimensionId</CODE> ("CARS") and an data of memebers. The returned <CODE>isUpdateSuccessful</CODE> variable indicates whether the update operation was successful.<BR /> <H2 id="toc-hId-177816333"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/updated.png" /></H2><BR /> <H2 id="toc-hId--18697172">4. Delete Member</H2><BR /> <UL><BR /> <LI><STRONG>Parameters:</STRONG><BR /> <OL><BR /> <LI><CODE>dimensionId</CODE> (Type: string)<BR /> <UL><BR /> <LI>Represents the ID of the dimension for which the members will be deleted.</LI><BR /> </UL><BR /> </LI><BR /> <LI><CODE>members</CODE> (Type: <CODE>string | string[]</CODE>)<BR /> <UL><BR /> <LI>Represents the ID of the member or an array of member IDs to be deleted.</LI><BR /> </UL><BR /> </LI><BR /> </OL><BR /> </LI><BR /> <LI><STRONG>Return Type:</STRONG><BR /> <UL><BR /> <LI><CODE>boolean</CODE><BR /> <UL><BR /> <LI>The function is expected to return <CODE>true</CODE> if the delete operation was successful, and <CODE>false</CODE> otherwise.</LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> </LI><BR /> <LI><STRONG>Note:</STRONG><BR /> <UL><BR /> <LI>After performing the delete, it is recommended to call certain refresh methods (<CODE>DataSource.refreshData()</CODE> or <CODE>Application.refreshData()</CODE>) to ensure that charts or tables reflect the deleted members in subsequent operations.</LI><BR /> </UL><BR /> </LI><BR /> </UL><BR /> Here's how you might use this function in a practical example:<BR /> <DIV><BR /> <PRE class="language-javascript"><CODE>var dimensionId = "CARS";<BR /> var membersToDelete = ["Audi"]; //or "Audi"<BR /> <BR /> //if you want to delete multiple data just pass as array ex ["Audi","BMW"....]<BR /> var isDeleteSuccessful = PlanningModel_1.deleteMembers(dimensionId, membersToDelete);<BR /> <BR /> // Now 'isDeleteSuccessful' indicates whether the delete operation was successful.<BR /> // You can handle further logic based on this result.<BR /> </CODE></PRE><BR /> </DIV><BR /> <H2 id="toc-hId--215210677"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/deleted.png" /></H2><BR /> <H2 id="toc-hId--411724182">Conclusion</H2><BR /> In this blog post, we explored the powerful capabilities of SAP Analytics Cloud for data analysis, focusing on the manipulation of planning model members through CRUD operations. We learned how to create, read, update, and delete members using scripting functions such as <CODE>createMembers</CODE>, <CODE>getMember</CODE>, <CODE>updateMembers</CODE>, and <CODE>deleteMembers</CODE>.<BR /> <BR /> These operations provide a flexible framework for managing data within specific dimensions, offering a seamless experience for users leveraging SAP Analytics Cloud. Whether you are adding new members, retrieving information, updating existing data, or removing members, the scripting functionalities enable efficient interaction with planning models. 2023-12-24T16:28:18+01:00 https://community.sap.com/t5/technology-blogs-by-members/significance-of-promises-in-asynchronous-operations/ba-p/13580596 Significance of Promises in asynchronous Operations. 2023-12-29T09:06:32+01:00 abdulrazak https://community.sap.com/t5/user/viewprofilepage/user-id/178688 <DIV></DIV><BR /> <H1 id="toc-hId-835678760"><STRONG>Intro:</STRONG></H1><BR /> Hello,<BR /> <BR /> I'm Writing my first blog on SAP community network, while developing the application and reviewing the other's code I came across this requirement. many new developers are feared to use the promises. and they develop a logic that is not convenient and increase the execution time.<BR /> <BR /> In this blog I'm trying my best to describe how to use Promises and manage asynchronous operations.<BR /> <BR /> This blog post delve into the world of promises in SAP UI5, exploring their importance and practical implementation.<BR /> <DIV></DIV><BR /> <H1 id="toc-hId-639165255"><STRONG>What is Asynchronous programming?</STRONG></H1><BR /> Asynchronous programming plays a crucial role in modern web development and SAP UI5 developer as can leverage promises to streamline the asynchronous operations.<BR /> <BR /> In asynchronous programming the operation is executed in the background by external process for this JavaScript engine uses the event loop and callback mechanism so the engine can perform the next operation/task in line. after completion of that task the asynchronous operation which is waiting in queue is completed.<BR /> <BR /> In some cases our task is depend on the result of that asynchronous operation results and we get the wrong outcome because our asynchronous function is not executed and our dependant task is executed already, for that many developers use some weird logic of count and setTimeout(). and if the time set in timeout function is passed before executing the asynchronous operation then it will show the wrong result, Promise can help in this area.<BR /> <BR /> &nbsp;<BR /> <H1 id="toc-hId-442651750"><STRONG>Basics of Promise:</STRONG></H1><BR /> A promise is created using the <STRONG>'Promise'</STRONG> constructor, which take a a single argument function known as "executor".<BR /> <BR /> The executor function takes two arguments <STRONG>resolve and reject</STRONG>. These are the functions provided by the promise implementation.<BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Screenshot_1.png.jpg" height="88" width="495" /></DIV><BR /> <DIV></DIV><BR /> <H2 id="toc-hId-375220964"><STRONG>State of promises:</STRONG></H2><BR /> <UL><BR /> <LI><BR /> <H3 id="toc-hId-307790178"><STRONG>Pending:</STRONG></H3><BR /> This is the initial state when promise is created</LI><BR /> <LI><BR /> <H3 id="toc-hId-111276673"><STRONG>Resolved:</STRONG></H3><BR /> when promise is fulfilled. The promise transition to this state when 'resolve' function is called.</LI><BR /> <LI><BR /> <H3 id="toc-hId--85236832"><STRONG>Rejected:</STRONG></H3><BR /> when there is an error or the async operation fails. Promise transition to this state when the 'reject' function is called.</LI><BR /> </UL><BR /> <DIV></DIV><BR /> <H2 id="toc-hId--410833056"><STRONG>Consuming promise:</STRONG></H2><BR /> Once a promise is created you can use the '.then' method to handle the resolved state and '.catch<BR /> <BR /> method to handle the rejected state.<BR /> <DIV></DIV><BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Screenshot_2.png.jpg" height="154" width="489" /></DIV><BR /> <DIV></DIV><BR /> <H1 id="toc-hId--736429280"><STRONG>Promise.all:</STRONG></H1><BR /> The 'Promise.all' method takes an iterable of promise and return a new promise that resolves when<BR /> <BR /> all of the input promise have resolved or rejects.<BR /> <BR /> &nbsp;<BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Screenshot_2.png-1.jpg" height="424" width="427" /></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <H3 id="toc-hId--674777347"><STRONG>Result:</STRONG></H3><BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Screenshot_2.png-2.jpg" /></DIV><BR /> <DIV></DIV><BR /> <H3 id="toc-hId--946522221"></H3><BR /> <H1 id="toc-hId--556229712"><STRONG>async / await:</STRONG></H1><BR /> <STRONG>'async / await'</STRONG> is a feature in JavaScript that simplifies the handling of asynchronous code, It's build on top of promises and provides a more synchronous - looking syntax, making asynchronous code easier to read and write.<BR /> <DIV></DIV><BR /> <H3 id="toc-hId--1339549231"><EM><STRONG>async function:</STRONG></EM></H3><BR /> <DIV>mark a function as 'async' by using 'async' keyword before the function declaration. This signifies that the function will always return a promise.</DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <H3 id="toc-hId--1536062736"><EM><STRONG>await operator:</STRONG></EM></H3><BR /> Inside async function you can use the 'await' keyword before a promise to pause the execution of the function until the promise is resolved.<BR /> <BR /> &nbsp;<BR /> <BR /> This make asynchronous operation looks like synchronous code.<BR /> <DIV></DIV><BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Fullscreen-capture-28-12-2023-080429.bmp_.jpg" height="126" width="442" /></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <BLOCKQUOTE><BR /> <DIV><EM><STRONG>await</STRONG> keyword only work with Parent level async function.</EM></DIV></BLOCKQUOTE><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <H3 id="toc-hId--1732576241"><STRONG>Wrong example:</STRONG></H3><BR /> <DIV></DIV><BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Fullscreen-capture-28-12-2023-080946.bmp_.jpg" height="189" width="422" /></DIV><BR /> <DIV></DIV><BR /> <BLOCKQUOTE><BR /> <DIV></DIV><BR /> <DIV><EM>above function will throw an error because the await keyword only work with parent level async function here forEach callback function is parent level function so I have to</EM></DIV><BR /> <DIV><EM>make the forEach function async.</EM></DIV></BLOCKQUOTE><BR /> <DIV></DIV><BR /> <H3 id="toc-hId--1929089746"><STRONG>Correct Example:</STRONG></H3><BR /> <DIV></DIV><BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/Fullscreen-capture-28-12-2023-081153.bmp_.jpg" height="163" width="422" /></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV><BR /> <H1 id="toc-hId--1538797237">Conclusion</H1><BR /> </DIV><BR /> The use of&nbsp; <STRONG>promises</STRONG> or <STRONG>async / await</STRONG> and the combination of both is depend on the requirement of the project. promises helps to manipulate the asynchronous operation as per developer need.<BR /> <BR /> You can write your thoughts and questions in the comment section. If you think this blog is helpful please do not forget to like and share&nbsp;<span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span>&nbsp;.<BR /> <BR /> &nbsp;<BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV><BR /> <DIV></DIV> 2023-12-29T09:06:32+01:00 https://community.sap.com/t5/technology-blogs-by-members/utilizing-sap-cloud-sdk-for-javascript-in-a-btp-proxy-app-for-non-web-apps/ba-p/13572792 Utilizing SAP Cloud SDK for JavaScript in a BTP Proxy App for non-Web-Apps to access onpremise SAP Systems 2023-12-31T03:44:52+01:00 WRoeckelein https://community.sap.com/t5/user/viewprofilepage/user-id/3438 <H1 id="toc-hId-834816739">Introduction</H1><BR /> Oftern there is a need for Apps running outside of the corporate network to access SAP onpremise systems. Using a Proxy App running in the BTP (Business Technology Platform) Cloud Foundry Environment utilizing the SAP Cloud Connector is a well known pattern for such requirements.<BR /> <BR /> However using the destination and connectivity service to get access to the tunnel to the onpremise systems is not trivial and is best done with the help of libraries.The approuter (cf. <A href="https://www.npmjs.com/package/@sap/approuter)" target="test_blank" rel="nofollow noopener noreferrer">https://www.npmjs.com/package/@sap/approuter)</A> is a well known and proven solution for this. However approuter is aimed at web applications. Using it for eg native applicatios is a bit troublesome.<BR /> <BR /> Since March 2019 there is also the SAP Cloud SDK for JavaScript (in the beginning known as the SAP S/4HANA Cloud SDK for JavaScript and also going by SAP Cloud SDK for Node.js) available. The http-client of this SDK completly handles the interaction with the destination and connectivity service, you just have to provide the destination name (please check out other features of this SDK, there is a lot of other useful stuff there!). This blog intends to give you some guidance for using this SDK for this porpose.<BR /> <H1 id="toc-hId-638303234">Basics</H1><BR /> The SDK is availabe in the public npm repository. Add the line<BR /> <PRE class="language-javascript"><CODE>"@sap-cloud-sdk/http-client": "^3.9.0",</CODE></PRE><BR /> <DIV><BR /> <DIV>to the dependencies section in your package.json. Use at least version 3.9.0 to avoid being hit by the xssec security vulnerability (cf. <A href="https://me.sap.com/notes/3411067" target="_blank" rel="noopener noreferrer">Security Note 3411067</A>).</DIV><BR /> <DIV></DIV><BR /> <DIV>In your JavaScript file to be run add</DIV><BR /> <DIV><BR /> <PRE class="language-javascript"><CODE>const httpclient = require('@sap-cloud-sdk/http-client');​</CODE></PRE><BR /> The object thus obtained follows the&nbsp;<A href="https://axios-http.com/" target="_blank" rel="noopener noreferrer nofollow">axios HTTP client</A> API with an added first object parameter (second parameter is compatible to RawAxiosRequestConfig), eg to make a POST call to an onpremise destination use this code<BR /> <BR /> </DIV><BR /> <DIV><BR /> <PRE class="language-javascript"><CODE>await httpclient.executeHttpRequest(<BR /> {<BR /> destinationName: 'MYDESTINATION'<BR /> },<BR /> { <BR /> method: 'POST',<BR /> url: "/sap/opu/odata/sap/ZMY_SERV_SRV/MyEntitySet",<BR /> headers: {<BR /> "Content-Type":"application/json; charset=utf-8",<BR /> "Accept":"application/json"<BR /> },<BR /> data: body<BR /> }<BR /> ).then(response =&gt; {<BR /> // use eg response.status and response.data <BR /> }).catch(err =&gt; {<BR /> // use eg err.code or message<BR /> });​</CODE></PRE><BR /> (Note: All code examples in this blog are just examples, do not use in your productive code, eg consider adding logging and a more robust coding)<BR /> <BR /> CSRF handling is done by default.<BR /> <BR /> </DIV><BR /> <H1 id="toc-hId-441789729">Proxy with Basic Authentication</H1><BR /> </DIV><BR /> <DIV>For a first simple proxy example we will use Basic Authentication from Outside and also Basic Authentication to the onpremise system. The latter is an easy brainer: Just use Basic Authentication as Authentication Type when defining the destination in the BTP cockpit:</DIV><BR /> <DIV></DIV><BR /> <DIV><BR /> <DIV><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/2023-12-30_22h51_36.png" /></DIV><BR /> <DIV>(Do yourself a favor and pay attention to the warning in the picture!)</DIV><BR /> <DIV>With such a destination you need merely give the destinationName in the example above.</DIV><BR /> </DIV><BR /> <DIV></DIV><BR /> <DIV>In the mta.yaml define the depencies to the destination and connectivity service:</DIV><BR /> <DIV><BR /> <PRE class="language-javascript"><CODE>...<BR /> modules:<BR /> - name: myproxy<BR /> type: nodejs<BR /> requires:<BR /> - name: myproxy-destination-service<BR /> parameters:<BR /> content-target: true<BR /> - name: myproxy-connectivity-service<BR /> parameters:<BR /> health-check-type: process<BR /> ...<BR /> resources:<BR /> - name: myproxy-destination-service<BR /> type: org.cloudfoundry.managed-service<BR /> parameters:<BR /> config:<BR /> version: 1.0.0<BR /> service: destination<BR /> service-name: myproxy-destination-service<BR /> service-plan: lite<BR /> - name: myproxy-connectivity-service<BR /> type: org.cloudfoundry.managed-service<BR /> parameters:<BR /> service: connectivity<BR /> service-plan: lite<BR /> </CODE></PRE><BR /> Health check is set to process because otherwise health check uses an unauthenticated HTTP access to / which won't work with our basic authentication requirement and thus would give constant app restarts.<BR /> <BR /> </DIV><BR /> <DIV><BR /> <DIV></DIV><BR /> <DIV>For the proxy function we use express and passport, ie in the package.json</DIV><BR /> <DIV><BR /> <PRE class="language-javascript"><CODE> "express": "^4.17.3",<BR /> "body-parser": "^1.20.2",<BR /> "passport": "^0.6.0",<BR /> "passport-http": "^0.3.0"</CODE></PRE><BR /> Preperation in the JavaScript file<BR /> <PRE class="language-javascript"><CODE>const express = require('express');<BR /> const bodyParser = require('body-parser')<BR /> const passport = require('passport');<BR /> const passportHTTP = require('passport-http');<BR /> <BR /> const auth_env = {login: process.env['AUTH_LOGIN'], password: process.env['AUTH_PASSWORD']};<BR /> const app = express();<BR /> passport.use(new passportHTTP.BasicStrategy(<BR /> function(username, password, done) {<BR /> if (username === auth_env.login &amp;&amp; password === auth_env.password) {<BR /> return done(null, username);<BR /> } else {<BR /> return done(null, false);<BR /> }<BR /> }<BR /> ));<BR /> app.use(passport.initialize());<BR /> app.use(passport.authenticate('basic', { session: false }));<BR /> app.use(bodyParser.text({ type: 'application/json' }));<BR /> ...<BR /> app.listen(process.env.PORT || 5000, function () {<BR /> console.log('Proxy app started');<BR /> });</CODE></PRE><BR /> and code for proxying a POST call<BR /> <PRE class="language-javascript"><CODE>app.post('/MyEntitySet', async (req, res) =&gt; {<BR /> if (!req.user) { res.sendStatus(403); return; }<BR /> await httpclient.executeHttpRequest(<BR /> {<BR /> destinationName: 'MY_DESTINATION'<BR /> },<BR /> { <BR /> method: 'POST',<BR /> url: "/sap/opu/odata/sap/ZMY_SERV_SRV/MyEntitySet",<BR /> headers: {<BR /> "Content-Type":"application/json; charset=utf-8",<BR /> "Accept":"application/json"<BR /> },<BR /> data: req.body<BR /> }<BR /> ).then(response =&gt; {<BR /> res.send(response.data);<BR /> }).catch(err =&gt; {<BR /> res.status(500).send('Backend Error');<BR /> }); <BR /> });</CODE></PRE><BR /> (in real life you might want to use wildcards and parse req.url)<BR /> <H1 id="toc-hId-245276224">Proxy with Principal Propagation</H1><BR /> Principal Propagation means the client needs to authenticate the user aka principal against the BTP and obtain tokens which need to be stored securely. When accessing the proxy the token needs to be presented. The token is then forwared to the cloud connector which generates authorization info using the principal for the onpremise system (configuring cloud connector and onpremise systems to achieve this is beyond the scope of this blog).<BR /> <BR /> In this example the native client needs to obtain a JWT token and send it along with request to the proxy. The JWT token will eg be obtained and renewed by utilizing SAML 2.0 and OAuth 2 protocols with the XSUAA service, cf. <A href="https://sap.github.io/cloud-sdk/docs/java/guides/cloud-foundry-xsuaa-service" target="test_blank" rel="nofollow noopener noreferrer">https://sap.github.io/cloud-sdk/docs/java/guides/cloud-foundry-xsuaa-service</A> . The native client should use libraries for this tasks (this is however beyond the scope of this blog). After deploying the proxy app to the BTP go to the deployed app in the BTP cockpit, select "Service Bindings" on the left and click on "Show sensitive data" to obtain the data for the libraries. You need<BR /> <UL><BR /> <LI>token_service_url : Add /oauth/authorize for the SAML 2.0 login endpoint, /oauth/token for the OAuth 2.0 endpoint and /logout.do for the SAML 2.0 logout endpoint</LI><BR /> <LI>clientid and clientsecret for the OAuth 2 protocol</LI><BR /> </UL><BR /> Also go to "Scopes" on the left and use the displayed scope name in the OAuth 2 protocol.<BR /> <BR /> The configuration for xsuaa in the xs-security.json file is needed (add scopes and role-templates as needed):<BR /> <PRE class="language-javascript"><CODE>{<BR /> "scopes": [<BR /> {<BR /> "name": "$XSAPPNAME.access",<BR /> "description": "Access"<BR /> }<BR /> ],<BR /> "role-templates": [<BR /> {<BR /> "name": "Access",<BR /> "default-role-name": "My Proxy Access Authorization",<BR /> "scope-references": [<BR /> "$XSAPPNAME.access"<BR /> ]<BR /> }<BR /> ],<BR /> "oauth2-configuration": {<BR /> "redirect-uris": [<BR /> "http://localhost:9999/success"<BR /> ]<BR /> }<BR /> }</CODE></PRE><BR /> Use Redirect-URIs as needed by the native libraries (can also use different schemes than http oder https as commonly used on mobile OS).<BR /> <BR /> </DIV><BR /> In the mta.yaml there is now a depedency to the xsuaa service (add role-collections as needed):<BR /> <DIV><BR /> <PRE class="language-javascript"><CODE> requires:<BR /> - name: my_proxy-uaa<BR /> ...<BR /> resources:<BR /> - name: my_proxy-uaa<BR /> type: org.cloudfoundry.managed-service<BR /> parameters:<BR /> path: ./xs-security.json<BR /> service-plan: application<BR /> service: xsuaa<BR /> config:<BR /> xsappname: my_proxy-${space}<BR /> tenant-mode: dedicated<BR /> role-collections:<BR /> - name: 'my_proxy-Access-${space}'<BR /> role-template-references:<BR /> - $XSAPPNAME.Access<BR /> </CODE></PRE><BR /> The xsappname and the role collections name thus contains the space name in order to be able to deploy the proxy app to multiple spaces in the same subacount (eg for three-tier development, test, production spaces). In this case the destination need to be defined at app-level in the respective destination service instance instead of subaccount level to have identical named destinations pointing to development, test, production onpremise systems respectivly (destination can't be defined at space level). Assign the role collection to users, either individually or via groups or via IdP configuration.<BR /> <BR /> The destination is defined with Principal Propagation:<BR /> <P style="overflow: hidden;margin-bottom: 0px"><IMG class="migrated-image" src="https://community.sap.com/legacyfs/online/storage/blog_attachments/2023/12/2023-12-31_03h05_31.png" /></P><BR /> In the package.json we need now additional dependencies to xsenv and xssec:<BR /> <PRE class="language-abap"><CODE> "@sap/xsenv": "^4.2.0",<BR /> "@sap/xssec": "^3.6.0",</CODE></PRE><BR /> (Use at least version 3.6.0 of xssec because of mentioned security issue)<BR /> <BR /> In the JavaScript file the preperation now includes verification of the JWT token with the xsuaa service:<BR /> <PRE class="language-javascript"><CODE>const httpclient = require('@sap-cloud-sdk/http-client');<BR /> const express = require('express');<BR /> const bodyParser = require('body-parser')<BR /> const xsenv = require('@sap/xsenv');<BR /> const passport = require('passport');<BR /> const xssec = require('@sap/xssec');<BR /> <BR /> xsenv.loadEnv();<BR /> const app = express();<BR /> const services = xsenv.getServices({ uaa: 'my_proxy-uaa' });<BR /> passport.use(new xssec.JWTStrategy(services.uaa));<BR /> app.use(passport.initialize());<BR /> app.use(passport.authenticate('JWT', { session: false }));<BR /> app.use(bodyParser.text({ type: 'application/json' }));</CODE></PRE><BR /> The code for proxying a call now contains code for checking the scope and for fowarding the JWT token for principal propagation:<BR /> <PRE class="language-javascript"><CODE>app.post('/MyEntitySet', async (req, res) =&gt; {<BR /> if (!req.authInfo.checkLocalScope('access')) {<BR /> return res.status(403).send('Forbidden');<BR /> }<BR /> await httpclient.executeHttpRequest( <BR /> {<BR /> destinationName: 'MY_DESTINATION'<BR /> jwt: req.authInfo.getAppToken()<BR /> },<BR /> { <BR /> method: 'POST',<BR /> url: "/sap/opu/odata/sap/ZMY_SERV_SRV/MyEntitySet",<BR /> headers: {<BR /> "Content-Type":"application/json; charset=utf-8",<BR /> "Accept":"application/json"<BR /> },<BR /> data: req.body<BR /> }<BR /> )</CODE></PRE><BR /> <H1 id="toc-hId-48762719">Conclusion</H1><BR /> The SAP Cloud SDK for JavaScript makes it easy to use onpremise destinations with principal propagation in a BTP proxy app and hides the complexity in dealing with the destination, connectivity and xsuaa service. Happy Coding and have a succesful and peaceful new year!<BR /> <BR /> </DIV><BR /> </DIV> 2023-12-31T03:44:52+01:00 https://community.sap.com/t5/technology-blogs-by-sap/re-gt-cap-2024-call-for-speakers-and-sponsors/ba-p/13585521 re>≡CAP 2024 | Call for Speakers and Sponsors 2024-01-31T10:36:21.388000+01:00 TheSebastian https://community.sap.com/t5/user/viewprofilepage/user-id/142742 <H1 id="toc-hId-835827493">reWhat?</H1><P><STRONG>re&gt;≡CAP: </STRONG>The yearly developer (un-) conference all around the <A href="https://cap.cloud.sap/" target="_blank" rel="noopener nofollow noreferrer">SAP Cloud Application Programming Model (CAP)</A>. It's the<SPAN>&nbsp;time of the year where our vivid community, customers, and partners meet the CAP team and exchange best practices, technical concepts, current projects, ideas for the future, and way more...<BR /></SPAN><SPAN>You can get a good impression about the format, content, event impressions&nbsp;<A href="https://community.sap.com/t5/technology-blogs-by-sap/re-gt-cap-2023-it-s-been-a-blast/ba-p/13562977" target="_blank">here</A>&nbsp;(2023 summary).</SPAN></P><P><SPAN>So better get excited if you see the blue cap... <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></SPAN></P><P><A href="https://recap-conf.dev/" target="_blank" rel="noopener nofollow noreferrer"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="recap_logo.svg" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/55166iCB478F1A2A9679AE/image-size/small?v=v2&amp;px=200" role="button" title="recap_logo.svg" alt="recap_logo.svg" /></span></SPAN></A></P><P>&nbsp;</P><H1 id="toc-hId-639313988">Where and when?</H1><P><STRONG>4th of June, 2024</STRONG> - <STRONG><A href="https://maps.app.goo.gl/KsB42AK1DwNznLMJA" target="_blank" rel="noopener nofollow noreferrer">same location</A> </STRONG>in St.-Leon Rot, Germany.</P><P><EM>(There will be streaming for the presentation tracks. The hands-on workshops, meet-the-experts corners and of course the networking and evening events are onsite only).</EM></P><H1 id="toc-hId-442800483"><A href="https://recap-conf.dev/" target="_blank" rel="noopener nofollow noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="reCAP_2024_dark.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/55257iAF4084231BAE6D93/image-size/large?v=v2&amp;px=999" role="button" title="reCAP_2024_dark.png" alt="reCAP_2024_dark.png" /></span></A></H1><P>&nbsp;</P><H1 id="toc-hId-246286978">&nbsp;</H1><H1 id="toc-hId-49773473"><SPAN>Become Speaker</SPAN></H1><P>The<STRONG> call for speakers</STRONG> is open and we want <STRONG>YOU</STRONG> to be part of it. So don't hesitate and <A href="https://recap.cfapps.eu12.hana.ondemand.com/" target="_blank" rel="noopener nofollow noreferrer"><STRONG>propose your session</STRONG></A> until <STRONG>end of February</STRONG> latest.</P><P><A href="https://recap.cfapps.eu12.hana.ondemand.com/" target="_blank" rel="noopener nofollow noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="twitter.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/55222iEAB1DCF345A3D859/image-size/large?v=v2&amp;px=999" role="button" title="twitter.png" alt="twitter.png" /></span></A></P><H1 id="toc-hId--146740032">&nbsp;</H1><H1 id="toc-hId--343253537">Become Sponsor</H1><P>Use the chance to support us and present your brand at our conference - check-out the customizable packages at our <A href="https://recap-conf.dev/" target="_blank" rel="noopener nofollow noreferrer">website</A>!</P><P><A href="https://recap-conf.dev/" target="_blank" rel="noopener nofollow noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="2 1.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/55224iB95D51A557EC3B45/image-size/large?v=v2&amp;px=999" role="button" title="2 1.png" alt="2 1.png" /></span></A></P><H1 id="toc-hId--539767042">&nbsp;</H1><H1 id="toc-hId--736280547">Early Bird Tickets</H1><P><SPAN>You have a long journey and need to plan your trip to re&gt;≡CAP 2024 early (before the official registration starts)? Please send us an&nbsp;</SPAN><A title="contact us via mail" href="mailto:recap.conf@gmail.com?subject=[reCAP%202024]%20Early%20Bird%20Tickets" target="_blank" rel="noopener noreferrer nofollow">email</A><SPAN>&nbsp;with your name, company, and mailing address.</SPAN></P><P><A href="https://recap-conf.dev/" target="_blank" rel="noopener nofollow noreferrer"><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="early_bird_2_X.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/55221iEE48780EE12AD429/image-size/large?v=v2&amp;px=999" role="button" title="early_bird_2_X.png" alt="early_bird_2_X.png" /></span></SPAN></A></P><H1 id="toc-hId--932794052">&nbsp;</H1><H1 id="toc-hId--359567474">There is more! UI5con 2024 and ABAPconf 2024</H1><P>It gets even better - we are in close sync with our friends from <STRONG>UI5</STRONG> and <STRONG>ABAP</STRONG>:&nbsp;Their conferences - <STRONG><A href="https://openui5.org/ui5con/germany2024/" target="_self" rel="nofollow noopener noreferrer">UI5con 2024</A></STRONG> and <STRONG><A href="https://abapconf.org/" target="_self" rel="nofollow noopener noreferrer">ABAPConf 2024</A></STRONG> - will take place in the same week and at the same location. So it becomes even more attractive for you to come to Germany!</P><P><A href="https://openui5.org/ui5con/germany2024/" target="_blank" rel="noopener nofollow noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="ui5con logo (2).svg" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/55188i1685823552EF13FE/image-size/small?v=v2&amp;px=200" role="button" title="ui5con logo (2).svg" alt="ui5con logo (2).svg" /></span></A>&nbsp; &nbsp;<A href="https://abapconf.org/" target="_blank" rel="noopener nofollow noreferrer"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abapconf2024_logo.png" style="width: 200px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/55189iD04C4E84DBCB52AD/image-size/small?v=v2&amp;px=200" role="button" title="abapconf2024_logo.png" alt="abapconf2024_logo.png" /></span></A></P><P>We will do a dedicated post highlighting the full conference week soon...</P> 2024-01-31T10:36:21.388000+01:00 https://community.sap.com/t5/technology-blogs-by-members/dynamically-show-hide-enable-disable-fields-in-fiori-elements/ba-p/13609053 Dynamically show/hide enable/disable fields in Fiori Elements 2024-02-19T08:05:02.709000+01:00 Mikhail_Minakov https://community.sap.com/t5/user/viewprofilepage/user-id/163112 <P>Sometimes you need to hide or disable the field in UI dynamically based on condition, for example based on entered data in edit mode.</P><P>You can make it in backend using&nbsp;hide annotation with value based on control field. Changing the value of control field via value help additional bindings or determination with side effects.</P><P>Use Case: In our app for order creation user is entering order code first. Based on the value of order code the device location field or the device number field should be enabled dynamically in edit mode. I one is enabled for entering the other must be read only.&nbsp;</P><H2 id="toc-hId-986265649">1. Hide annotation based on another field</H2><P>First of all we create two additional read only fields in Interface and projection views</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>devloc as deviceLocation, devloc as deviceLocationRO, ... _Equipment.SerialNumber as deviceNumber, _Equipment.SerialNumber as deviceNumberRO,</code></pre><P>&nbsp;</P><P>&nbsp;</P><DIV><P>Then we make read only fields read only in behaviour definition</P></DIV><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>field ( readonly ) deviceNumberRO, deviceLocationRO;</code></pre><P>&nbsp;</P><P>The idea is to hide the editable field showing the readonly field what makes user thinking the field is disabled/enabled dynamically.</P><P>So we add another four control fields, which will contain true or false based on condition, and will be used as value for hide annotation</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>_OrderCode.hideDevLoc as hideDevLocation, _OrderCode.hideSerNum as hideDevNumber, _OrderCode.hideDevLocRO as hideDevLocationRO, _OrderCode.hideSerNumRO as hideDevNumberRO,</code></pre><P>&nbsp;</P><P>We need four of them to be able to hide all of fields until user enters order code.</P><P>Now we bind all this together, making aur fields be hideable depending of the value of control fields. I am using annotation.xml in UI, but the same can be done in backend metadata extension.</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>&lt;Record Type="UI.DataField"&gt; &lt;PropertyValue Property="Value" Path="deviceNumber"/&gt; &lt;Annotation Term="UI.Hidden" Path="hideDeviceNumber" /&gt; &lt;/Record&gt; &lt;Record Type="UI.DataField"&gt; &lt;PropertyValue Property="Value" Path="deviceNumberRO"/&gt; &lt;Annotation Term="UI.Hidden" Path="hideDevNumberRO" /&gt; &lt;/Record&gt; ... &lt;Record Type="UI.DataField"&gt; &lt;PropertyValue Property="Value" Path="deviceLocationRO"/&gt; &lt;Annotation Term="UI.Hidden" Path="hideDevLocationRO" /&gt; &lt;/Record&gt; &lt;Record Type="UI.DataField"&gt; &lt;PropertyValue Property="Value" Path="deviceLocation"/&gt; &lt;Annotation Term="UI.Hidden" Path="hideDevLocation"/&gt; &lt;/Record&gt; ...</code></pre><P>&nbsp;</P><P>In the same way for deviceLocation and deviceLocationRO.</P><P>The next step here is to set the values of control fields as additional binding of order code value help view ZI_DM_CSORDER_OCODE_VH:</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>define view ZI_DM_CSORDER_OCODE_VH as select from ... { ... @UI.hidden: true cast(case when bzgsobjtyp='01' then 'X' else '' end as boolean) as hideSerNum, @UI.hidden: true cast(case when bzgsobjtyp='03' then 'X' else '' end as boolean) as hideDevLoc, @UI.hidden: true cast(case when bzgsobjtyp='01' then '' else 'X' end as boolean) as hideSerNumRO, @UI.hidden: true cast(case when bzgsobjtyp='03' then '' else 'X' end as boolean) as hideDevLocRO, ... }</code></pre><P>&nbsp;</P><P>Value Help for order code is defined as following:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@Consumption.valueHelpDefinition: [ { entity : {name: 'ZI_DM_CSORDER_OCODE_VH', element: 'orderCode'}, { localElement: 'hideDevLocation', element: 'hideDevLoc', usage: #RESULT }, { localElement: 'hideDevNumber', element: 'hideSerNum', usage: #RESULT }, { localElement: 'hideDevLocationRO', element: 'hideDevLocRO', usage: #RESULT }, { localElement: 'hideDevNumberRO', element: 'hideSerNumRO', usage: #RESULT } ] orderCode,</code></pre><P>&nbsp;</P><P>As of now, when user changed order code, the control fields hideDevice* will be updated with the values generated in value help view causing dynamically hiding of fields in edit mode. The same behaviour may be achieved with determination code and side effects.</P><P>How it looks in UI.&nbsp;</P><P>Case 1 - user enters order code for device location. Device location is showing, device number is hided.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Mikhail_Minakov_0-1708259092917.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67162iB44D6AC639A0979A/image-size/medium?v=v2&amp;px=400" role="button" title="Mikhail_Minakov_0-1708259092917.png" alt="Mikhail_Minakov_0-1708259092917.png" /></span></P><P>Case 2 - user enters order code for device number. Device number is showing for edit, device location is showing read only, because it will be determined later based on device number data.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Mikhail_Minakov_1-1708259236578.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67163iD36302E9592500D5/image-size/medium?v=v2&amp;px=400" role="button" title="Mikhail_Minakov_1-1708259236578.png" alt="Mikhail_Minakov_1-1708259236578.png" /></span></P> 2024-02-19T08:05:02.709000+01:00 https://community.sap.com/t5/technology-blogs-by-members/deleting-mutiple-data-in-m-table-amp-deleting-multiple-data-using-batch/ba-p/13607399 Deleting mutiple data in m-table & Deleting multiple data using Batch-call(Consuming OData Service) 2024-02-19T08:06:54.441000+01:00 kavya07 https://community.sap.com/t5/user/viewprofilepage/user-id/178389 <P>Hello Everyone,</P><P>I am writing this blog to explain how to delete multiple data in m table in SAP UI5 application and also deleting multiple data in m table using Batch call consuming ODATA service.</P><P>The m table is also called as responsive table which contains a set of line items and is fully responsive. The control for m-table is sap.m.Table.</P><P>Step1: Create a new Project in VS Code. View---Command Palette ---Open Application Generator---Template Type (SAP Fiori)—Select Basic ---Click on Next</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_0-1708061332921.png" style="width: 706px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/66324iA9B914E3716CCE38/image-dimensions/706x420?v=v2" width="706" height="420" role="button" title="kavya07_0-1708061332921.png" alt="kavya07_0-1708061332921.png" /></span></P><P>Step 2: Data Source ---Select None ---Click on Next</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_0-1708061487737.png" style="width: 705px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/66325i263723F5FD06BE6E/image-dimensions/705x396?v=v2" width="705" height="396" role="button" title="kavya07_0-1708061487737.png" alt="kavya07_0-1708061487737.png" /></span></P><P>Step 3: Give View name and Project name Click on Finish.</P><P>Step 4: Click on model --- Create employee.json file.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_0-1708061755626.png" style="width: 512px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/66330i4BB88D59CDF44E7C/image-dimensions/512x630?v=v2" width="512" height="630" role="button" title="kavya07_0-1708061755626.png" alt="kavya07_0-1708061755626.png" /></span></P><P>Step 5: Configure in manifest.json</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_0-1708061852550.png" style="width: 708px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/66331i4FF5C6DBE93A6BDF/image-dimensions/708x523?v=v2" width="708" height="523" role="button" title="kavya07_0-1708061852550.png" alt="kavya07_0-1708061852550.png" /></span></P><P>Step 6: In View, Create m table.</P><P>&nbsp;</P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="multipledatadelete.controller.View1" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m"&gt; &lt;Page id="page" title="{i18n&gt;title}"&gt; &lt;content&gt; &lt;Table items="{emp&gt;/data}" mode="MultiSelect" selectionChange="oSelectedItems" id="mytable" &gt; &lt;columns&gt; &lt;Column &gt; &lt;Text text="Name"&gt;&lt;/Text&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="Id"&gt;&lt;/Text&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="City"&gt;&lt;/Text&gt; &lt;/Column&gt; &lt;/columns&gt; &lt;ColumnListItem &gt; &lt;cells&gt; &lt;Text text="{emp&gt;name}"&gt;&lt;/Text&gt; &lt;Text text="{emp&gt;id}"&gt;&lt;/Text&gt; &lt;Text text="{emp&gt;city}"&gt;&lt;/Text&gt; &lt;/cells&gt; &lt;/ColumnListItem&gt; &lt;headerToolbar&gt; &lt;OverflowToolbar &gt; &lt;Bar &gt; &lt;contentRight&gt; &lt;Button text="Delete" type="Negative" press="oDelete" &gt;&lt;/Button&gt; &lt;/contentRight&gt; &lt;/Bar&gt; &lt;/OverflowToolbar&gt; &lt;/headerToolbar&gt; &lt;/Table&gt; &lt;/content&gt; &lt;/Page&gt; &lt;/mvc:View&gt;</code></pre><P>&nbsp;</P><P>Step 7: In Controller, Write Logic.</P><P>Based on selected keys, we can delete multiple data.</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>sap.ui.define([ "sap/ui/core/mvc/Controller" ], /** * {typeof sap.ui.core.mvc.Controller} Controller */ function (Controller) { "use strict"; var oSelectedPath =[] return Controller.extend("multipledatadelete.controller.View1", { onInit: function () { }, oSelectedItems:function(oEvent){ oSelectedPath = oEvent.getSource().getSelectedContextPaths() }, oDelete:function(){ var oModel = this.getView().getModel("emp") var aOldData = oModel.oData.data var aTemp = [] for(let j=0;j&lt;oSelectedPath.length;j++) { var index = oSelectedPath[j].split("/")[2] for(let i=0; i&lt;aOldData.length; i++) { if(aOldData[i].id == aOldData[index].id ) { aTemp.push(aOldData[i]) } } } for(let k=0;k&lt;aTemp.length;k++) { for(let n=0; n&lt;aOldData.length; n++) { if(aOldData[n].id == aTemp[k].id ) { aOldData.splice(n,1) } } } oModel.refresh(true) this.getView().byId("mytable").removeSelections(true) } }); });</code></pre><P>&nbsp;</P><P>Run the program --- Open Integrated Terminal ---npm run start.</P><P>Output look like this.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_0-1708062249832.png" style="width: 685px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/66334i3EDF5397F16BF449/image-dimensions/685x214?v=v2" width="685" height="214" role="button" title="kavya07_0-1708062249832.png" alt="kavya07_0-1708062249832.png" /></span></P><P>After deleting multiple data, Output look like this</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_1-1708062249837.png" style="width: 683px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/66333iE7C79F2F44AC5B82/image-dimensions/683x166?v=v2" width="683" height="166" role="button" title="kavya07_1-1708062249837.png" alt="kavya07_1-1708062249837.png" /></span></P><P><STRONG>---------------------------------------------------------------------------------------------------------------------------------------------</STRONG></P><P><STRONG><U>Multiple data delete using Batch call.</U></STRONG></P><P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; In some business scenarios multiple entity instances need to be handled together as a single logical unit of work. This is where $batch processing come into picture. $batch request means multiple operations bundled as a single request. Batch processing simple batches several independent OData service call to a single OData call. Batch call Reduce number of Api calls. In a single http request we can do multiple crud operation.</P><P>Step 1: Create a Database table in SE11.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_1-1708318934951.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67282i065AC171AC30A911/image-dimensions/707x359?v=v2" width="707" height="359" role="button" title="kavya07_1-1708318934951.png" alt="kavya07_1-1708318934951.png" /></span></P><P>Step 2: I have inserted some records. Output look like this.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_1-1708316313131.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67262i4C822C6EFF16FC5A/image-dimensions/707x364?v=v2" width="707" height="364" role="button" title="kavya07_1-1708316313131.png" alt="kavya07_1-1708316313131.png" /></span></P><P>Step3: After creating Database table. Go to T-Code SEGW. Create OData Project.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_2-1708316384640.png" style="width: 708px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67264i0AEA56B5438951CC/image-dimensions/708x391?v=v2" width="708" height="391" role="button" title="kavya07_2-1708316384640.png" alt="kavya07_2-1708316384640.png" /></span></P><P>Step 4: Import DDIC Structure and Generate Runtime Objects and Also Register Service to open SAP Gateway Client.</P><P>Step 5: Click on DPC_EXT.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_3-1708316384649.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67265iD420E5BCF04A21F7/image-dimensions/707x186?v=v2" width="707" height="186" role="button" title="kavya07_3-1708316384649.png" alt="kavya07_3-1708316384649.png" /></span></P><P>Step 6: Redefine GET_ENTITYSET and write logic.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_4-1708316384650.png" style="width: 709px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67263iC22AC130B5A86E6F/image-dimensions/709x96?v=v2" width="709" height="96" role="button" title="kavya07_4-1708316384650.png" alt="kavya07_4-1708316384650.png" /></span></P><P>Step 7: Redefine GET_ENTITY and write logic.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_5-1708316384652.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67266i9C007F8BEED10D16/image-dimensions/707x99?v=v2" width="707" height="99" role="button" title="kavya07_5-1708316384652.png" alt="kavya07_5-1708316384652.png" /></span></P><P>Step 8: Redefine CREATE_ENTITY and write logic.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_6-1708316384654.png" style="width: 709px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67268iB7244A8599966801/image-dimensions/709x234?v=v2" width="709" height="234" role="button" title="kavya07_6-1708316384654.png" alt="kavya07_6-1708316384654.png" /></span></P><P>Step 9: Redefine UPDATE_ENTITY and write logic.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_7-1708316384656.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67267i164A1FCFF7EDF64F/image-dimensions/707x211?v=v2" width="707" height="211" role="button" title="kavya07_7-1708316384656.png" alt="kavya07_7-1708316384656.png" /></span></P><P>Step 10: Redefine DELETE_ENTITY and write logic.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_8-1708316384658.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67269i4A9C9194CD8E01D1/image-dimensions/707x135?v=v2" width="707" height="135" role="button" title="kavya07_8-1708316384658.png" alt="kavya07_8-1708316384658.png" /></span></P><P>Step 11: Redefine CHANGESET_BEGIN AND CHANGESET_END without writing any logic.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_9-1708316384663.png" style="width: 706px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67270i659E3006263BFA7D/image-dimensions/706x219?v=v2" width="706" height="219" role="button" title="kavya07_9-1708316384663.png" alt="kavya07_9-1708316384663.png" /></span></P><P>Step 12: Go to SAP Gateway Client and Check all crud operations and Batch Call service working or not through URI.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_10-1708316384672.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67271i80E8B563268AE336/image-dimensions/707x318?v=v2" width="707" height="318" role="button" title="kavya07_10-1708316384672.png" alt="kavya07_10-1708316384672.png" /></span></P><P><STRONG>Front end:</STRONG></P><P>Step 1: Create one SAP UI5 application based on Service created in Back-end. Metadata.xml file will be created.</P><P>Step 2: Home Page</P><P>&nbsp;</P><pre class="lia-code-sample language-markup"><code>&lt;mvc:View controllerName="odatabatch.controller.View1" xmlns:mvc="sap.ui.core.mvc" displayBlock="true" xmlns="sap.m"&gt; &lt;Page id="page" title="{i18n&gt;title}"&gt; &lt;content&gt; &lt;Table items="{employee&gt;/}" mode="MultiSelect" selectionChange="onRead" id="Tableid" &gt; &lt;columns&gt; &lt;Column &gt; &lt;Text text="Employee ID"&gt;&lt;/Text&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="Employee Name"&gt;&lt;/Text&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="Employee Age"&gt;&lt;/Text&gt; &lt;/Column&gt; &lt;Column &gt; &lt;Text text="Employee City"&gt;&lt;/Text&gt; &lt;/Column&gt; &lt;/columns&gt; &lt;items&gt; &lt;ColumnListItem &gt; &lt;cells&gt; &lt;Text text="{employee&gt;Employeeid}"&gt;&lt;/Text&gt; &lt;Text text="{employee&gt;Empname}"&gt;&lt;/Text&gt; &lt;Text text="{employee&gt;Empage}"&gt;&lt;/Text&gt; &lt;Text text="{employee&gt;Empcity}"&gt;&lt;/Text&gt; &lt;/cells&gt; &lt;/ColumnListItem&gt; &lt;/items&gt; &lt;/Table&gt; &lt;Bar &gt; &lt;contentRight&gt; &lt;Button text="Delete" type="Negative" id="btnid" press="onMultipleDelete"&gt;&lt;/Button&gt; &lt;/contentRight&gt; &lt;/Bar&gt; &lt;/content&gt; &lt;/Page&gt; &lt;/mvc:View&gt;</code></pre><P>&nbsp;</P><P>Step 3: Controller logic</P><P>onInit logic:</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code> onInit: function () { var oModel = new sap.ui.model.json.JSONModel(); this.getOwnerComponent().getModel().read("/zemployeeSet", { success: function (oData, response) { debugger // Assuming that the data you want is in the 'results' property oModel.setData(oData.results); }, error: function (error) { console.error("Error loading data: " + error); } }); this.getView().setModel(oModel, "employee"); }</code></pre><P>&nbsp;</P><P>onRead logic:</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>onRead:function(oEvent){ this.getOwnerComponent().getModel().read("/zemployeeSet", { success: function (oData, response) { }, error: function (error) { console.error("Error loading data: " + error); } }); },</code></pre><P>&nbsp;</P><P>onMultipleDelete logic:</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code> onMultipleDelete:function(oEvent){ var oTable = this.getView().byId('Tableid'); var selectItem = oTable.getSelectedItems(); var object = []; for (var i = 0; i &lt; selectItem.length; i++) { var selectedrow = { "Employeeid": selectItem[i].getBindingContext('employee').getObject().Employeeid, "Empname": selectItem[i].getBindingContext('employee').getObject().Empname, "Empage": selectItem[i].getBindingContext('employee').getObject().Empage, "Empcity": selectItem[i].getBindingContext('employee').getObject().Empcity, }; object.push(selectedrow); } var oModel = this.getView().getModel(); oModel.setUseBatch(true); var jModel = this.getOwnerComponent().getModel(); for(var i = 0; i&lt;object.length; i++){ var oEntry = object[i].Employeeid; oModel.remove("/zemployeeSet('" + oEntry + "')", { method:"DELETE", success:function(response){ sap.m.MessageToast.show("Employee Data is deleted"); } } ); } oModel.submitChanges({ success: function(data, response){ }, error: function(e){ } }) location.reload(); }</code></pre><P>&nbsp;</P><P>Step 4: Output before</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_0-1708316992941.png" style="width: 708px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67272iAB684C318DBA3F5C/image-dimensions/708x184?v=v2" width="708" height="184" role="button" title="kavya07_0-1708316992941.png" alt="kavya07_0-1708316992941.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_1-1708316992945.png" style="width: 707px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67274iB827699EE2E49743/image-dimensions/707x364?v=v2" width="707" height="364" role="button" title="kavya07_1-1708316992945.png" alt="kavya07_1-1708316992945.png" /></span></P><P>Step 5: After deleting multiple data output look like this.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_2-1708316992947.png" style="width: 706px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67273iBDA0905B2A4C0BD7/image-dimensions/706x134?v=v2" width="706" height="134" role="button" title="kavya07_2-1708316992947.png" alt="kavya07_2-1708316992947.png" /></span></P><P>In Database table, Multiple data get deleted.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="kavya07_3-1708316992949.png" style="width: 706px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67275i669F00DE884FE5A2/image-dimensions/706x196?v=v2" width="706" height="196" role="button" title="kavya07_3-1708316992949.png" alt="kavya07_3-1708316992949.png" /></span></P><P>Conclusion:</P><P>Instead of deleting single records we can delete multiple data in a single shot.</P><P>Thanks &amp; Regards,</P><P>Kavya H B</P> 2024-02-19T08:06:54.441000+01:00 https://community.sap.com/t5/technology-blogs-by-members/extracting-excel-data-into-json-object-and-display-into-sap-ui-table-using/ba-p/13613695 Extracting Excel Data into JSON object and display into sap.ui.table using Javascript and sheetJS 2024-02-21T12:58:44.402000+01:00 abdulrazak https://community.sap.com/t5/user/viewprofilepage/user-id/178688 <P><FONT size="6"><STRONG>Intro:</STRONG></FONT></P><P>Hello,</P><P>We have some cases where we want to upload the excel file data into database and show this data to UI using table and for that Backend build query and convert excel file into readable format then upload it to table using query and then give GET service to UI and the UI fetch this service and then display this data.</P><P>I cam across with this case and for that I use xlsx library featured by <A href="https://sheetjs.com/" target="_self" rel="nofollow noopener noreferrer">sheetJS</A>. It helps to solve this problem and we are able to extract the data from excel sheet to Json object and then we can show this data to sap ui table and also, able to post this data to backend using post service if needed also we are able to apply the validations.</P><P><FONT size="6"><STRONG>Prerequisites:</STRONG></FONT></P><UL><LI>JavaScript FileReader object.</LI><LI>Sap UI5 and UI elements.</LI></UL><P><FONT size="5"><STRONG>let's get start.</STRONG></FONT></P><P>1.<STRONG> create a sample application.</STRONG></P><P>2. <STRONG>we need a xlsx library</STRONG></P><P>3. <STRONG>add library path to manifest.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_0-1708454245954.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68470i26254E0BA4CE8C57/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_0-1708454245954.png" alt="abdulrazak_0-1708454245954.png" /></span></P><P>4. <STRONG>We create a simple UI where we are able to upload a file and a table where we can display data.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_1-1708454463083.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68471iCE948E9E20746BF1/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_1-1708454463083.png" alt="abdulrazak_1-1708454463083.png" /></span></P><P>5.<STRONG> We have a sample data file for testing.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_2-1708454521653.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68472i7BE4F2FF4FE09E6A/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_2-1708454521653.png" alt="abdulrazak_2-1708454521653.png" /></span></P><P>6<STRONG>.First select the file and then click on upload.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_3-1708454622334.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68473i3C52C52F1EEB877B/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_3-1708454622334.png" alt="abdulrazak_3-1708454622334.png" /></span></P><P>7.<STRONG>from here our controller comes into picture from here we use the file reader object of javascript and read the file.</STRONG></P><P><STRONG>What is File Reader?</STRONG></P><P><SPAN>The&nbsp;FileReader</SPAN><SPAN>&nbsp;object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using&nbsp;File</SPAN><SPAN>&nbsp;or&nbsp;blob</SPAN><SPAN>&nbsp;objects to specify the file or data to read.</SPAN></P><P><SPAN>onLoad event&nbsp;</SPAN>&nbsp;is fired when a file has been read successfully. This event is not cancelable and does not bubble.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_8-1708455165313.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68478i8FDA093925FF9E8F/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_8-1708455165313.png" alt="abdulrazak_8-1708455165313.png" /></span></P><P>9. <STRONG>In this onload event we are building a code to extract the data in Json Object.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_6-1708455053665.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68476iB9CB231EC570C378/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_6-1708455053665.png" alt="abdulrazak_6-1708455053665.png" /></span></P><P>10. <STRONG>Now we want to confirm that the file we upload have the same columns as we mentioned so we will not lose any data</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_7-1708455118496.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68477i34D970D7A7D2A57A/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_7-1708455118496.png" alt="abdulrazak_7-1708455118496.png" /></span></P><P>11.<STRONG> If our data is correct, we are now in a place to set this data into model and then send if required post it.</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_9-1708455264776.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68479i71B2AB62C5206B96/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_9-1708455264776.png" alt="abdulrazak_9-1708455264776.png" /></span></P><P>12.<STRONG>Result</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_0-1708520408781.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68719i7BA110D3C5E7847B/image-size/large?v=v2&amp;px=999" role="button" title="abdulrazak_0-1708520408781.png" alt="abdulrazak_0-1708520408781.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>13. <STRONG>Show proper messages so that user can be able to identify where the thing is getting wrong.</STRONG></P><UL><LI>e.g. if user press upload button without selecting file.</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_11-1708455576243.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68483i89305032FAE277A5/image-size/medium?v=v2&amp;px=400" role="button" title="abdulrazak_11-1708455576243.png" alt="abdulrazak_11-1708455576243.png" /></span></P><UL><LI>if the column name is wrong.</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_12-1708455640653.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68486i84701A99DACC35B8/image-size/medium?v=v2&amp;px=400" role="button" title="abdulrazak_12-1708455640653.png" alt="abdulrazak_12-1708455640653.png" /></span></P><UL><LI>If the Mandatory data is missing</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="abdulrazak_13-1708455925360.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/68490i65FAE012EE4BD376/image-size/medium?v=v2&amp;px=400" role="button" title="abdulrazak_13-1708455925360.png" alt="abdulrazak_13-1708455925360.png" /></span></P><P>&nbsp;</P><P><STRONG><FONT size="6">Limitations:</FONT></STRONG></P><UL><LI>Developer should pre known the column names means column names should be static</LI><LI>column names and object count should be same if the columns in excel are more then we are not able to capture that data and if the column is missing then we will set the undefined values.</LI><LI>validations to check the column names and mandatory field are manually from developer.</LI></UL><P>&nbsp;</P><P><STRONG><FONT size="6">Conclusion:</FONT></STRONG></P><P>As sometime user wanted to upload the excel data into system that time user is not required to depend on backend side as he has the application that build to post the data from excel to Database.</P><P>many case scenarios can be performed using this library.</P><P>It helps me and hope so it will help you to.</P><P>If you find this content informative, please share and give feedback.</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> 2024-02-21T12:58:44.402000+01:00 https://community.sap.com/t5/technology-blogs-by-members/amount-to-words-using-sap-adobe-forms-scripting-javascript/ba-p/13657738 Amount to Words using SAP Adobe Forms Scripting - JavaScript 2024-04-05T16:31:33.312000+02:00 hazem2020 https://community.sap.com/t5/user/viewprofilepage/user-id/861936 <P>&nbsp;</P><P><FONT size="5"><STRONG><SPAN>Problem Overview</SPAN></STRONG><SPAN>&nbsp;</SPAN></FONT></P><P><SPAN>&nbsp; &nbsp; &nbsp; &nbsp;<FONT size="4">&nbsp;It is a common task to add the Amount in words to Adobe Forms in both SAP on-premise and Cloud Systems.</FONT></SPAN><FONT size="4"><SPAN>&nbsp;</SPAN></FONT></P><P><FONT size="4"><SPAN>Sometimes we face an issue that there is no enhancement point to write the functionality in ABAP(Backend). To Solve that I had to write the whole functionality in the Form Script Editor.</SPAN><SPAN>&nbsp;</SPAN></FONT></P><P><FONT size="4"><SPAN>In this short Blog post, I am going to share with you a repository with the Logic&nbsp;that you can use if you face the same problem.&nbsp;</SPAN><SPAN>&nbsp;</SPAN></FONT></P><P><FONT size="4"><SPAN>To achieve that I found a great repository that achieve the needed but it is not compatible with the Form Scripting JS version, So I had to modify it to the simplest form to work properly.</SPAN></FONT><SPAN>&nbsp;</SPAN></P><P>&nbsp;</P><P><FONT size="5"><STRONG><SPAN>Code :</SPAN></STRONG><SPAN>&nbsp;</SPAN></FONT></P><P><FONT size="4"><SPAN>Here is the repository with the modified version, It is only in&nbsp;</SPAN><SPAN>Arabic and&nbsp;</SPAN><SPAN>Saudi Riyal.</SPAN><SPAN>&nbsp;</SPAN></FONT></P><P><FONT size="4"><SPAN>But you will find other currencies in the original Repository that you can use.</SPAN><SPAN>&nbsp;</SPAN></FONT></P><P><FONT size="4"><SPAN>The Modified Version (For SAP Forms):&nbsp;</SPAN><A href="https://github.com/hazem-jr/AmountInWords_InArabic.git" target="_blank" rel="noopener nofollow noreferrer"><SPAN>https://github.com/hazem-jr/AmountInWords_InArabic.git</SPAN></A><SPAN>&nbsp;</SPAN></FONT></P><P><FONT size="4"><SPAN>The Original Version :&nbsp;</SPAN><A href="https://github.com/mmahgoub/tafgeetjs.git" target="_blank" rel="noopener nofollow noreferrer"><SPAN>https://github.com/mmahgoub/tafgeetjs.git</SPAN></A><SPAN>&nbsp;</SPAN></FONT></P><P>&nbsp;</P><P><FONT size="5"><STRONG><SPAN class=""><SPAN class="">Tests:</SPAN></SPAN><SPAN class="">&nbsp;</SPAN></STRONG></FONT></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="hazem2020_0-1712158272923.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/90638i4674EF97BB56AA4A/image-size/large?v=v2&amp;px=999" role="button" title="hazem2020_0-1712158272923.png" alt="hazem2020_0-1712158272923.png" /></span></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="hazem2020_1-1712157636895.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/90626i282FA6152B79A936/image-size/large?v=v2&amp;px=999" role="button" title="hazem2020_1-1712157636895.png" alt="hazem2020_1-1712157636895.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><SPAN>&nbsp;</SPAN></P> 2024-04-05T16:31:33.312000+02:00 https://community.sap.com/t5/technology-blogs-by-members/embracing-typescript-in-sapui5-development/ba-p/13675744 Embracing TypeScript in SAPUI5 Development 2024-04-22T11:05:53.164000+02:00 SimonMDM https://community.sap.com/t5/user/viewprofilepage/user-id/1441755 <P style=" text-align: center; "><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SimonMDM_0-1713517469569.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98682iF004C9A377DF935C/image-size/medium?v=v2&amp;px=400" role="button" title="SimonMDM_0-1713517469569.png" alt="SimonMDM_0-1713517469569.png" /></span></P><P>&nbsp;</P><P><SPAN>Throughout 11 years of SAP development, I've seen the evolution of SAP's vision for its ERP, particularly with the introduction of FIORI, marking a significant shift towards user-experience-centered innovation. Developers have also been considered with tools like UI5, BAS, and more recently SAP BUILD Code. Now, I will focus on the introduction of TypeScript (TS) into SAPUI5, sharing my experience and observations while developing Fiori applications in TS for a specific project.</SPAN></P><H2 id="toc-hId-992617829"><SPAN>Understanding TypeScript: A Brief Introduction</SPAN></H2><P><SPAN>TypeScript (TS), developed by Microsoft, extends JavaScript (JS) by incorporating features like static typing, interfaces, and classes: commonly found in object-oriented programming. The compilation of TypeScript into JavaScript before execution enables the detection of programming errors during development, fostering code quality and robustness.</SPAN></P><P><I><SPAN>If you'd like to delve deeper:<BR /></SPAN></I><A href="https://www.typescriptlang.org/" target="_blank" rel="noopener nofollow noreferrer"><SPAN>TypeScript: JavaScript With Syntax For Types. (typescriptlang.org)</SPAN></A></P><H2 id="toc-hId-796104324"><SPAN>SAP &amp; TypeScript: A Not-So-New Collaboration</SPAN></H2><P><SPAN>SAP's journey with TypeScript began in 2021 as part of a "beta experimental" phase, paving the way for developers to explore this new avenue in application development. In 2023, during UI5con, SAP officially announced TypeScript support for UI5 starting from version 1.116.0.</SPAN></P><P><I><SPAN>For further details:<BR /></SPAN></I><A href="https://community.sap.com/t5/technology-blogs-by-sap/typescript-for-ui5-yay-it-s-official-and-a-round-up-of-recent-changes/ba-p/13570051" target="_blank"><SPAN>TypeScript for UI5: “Yay, it’s official!” – and a ... - SAP Community</SPAN></A></P><H2 id="toc-hId-599590819"><SPAN>Feedback and Experience Sharing</SPAN></H2><P><SPAN>Today, rather than a tutorial on TypeScript development (perhaps in another blog <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span>), I aim to share my personal experience and insights, motivating those who are curious yet hesitant to take the plunge.</SPAN></P><H2 id="toc-hId-403077314"><SPAN>Advantages of TypeScript Integration</SPAN></H2><H3 id="toc-hId-335646528"><STRONG>Static Typing:</STRONG></H3><P><SPAN>The first error you'll encounter when starting with TypeScript is compilation errors due to missing typings! However, this very aspect underscores one of TypeScript's greatest strengths…</SPAN></P><P>&nbsp;</P><P style=" text-align: center; "><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SimonMDM_1-1713517469331.png" style="width: 292px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98680i10E7B974211A3D9C/image-dimensions/292x127?v=v2" width="292" height="127" role="button" title="SimonMDM_1-1713517469331.png" alt="SimonMDM_1-1713517469331.png" /></span><BR /><I><SPAN>example of static typing for a structure</SPAN></I></P><P>&nbsp;</P><P><SPAN>By explicitly specifying data types, TypeScript detects typing errors during compilation, fostering code reliability and reducing the risk of runtime errors. At the beginning of your project, you might see a lot of errors flagged in red, but it will quickly become a natural part of the development process.</SPAN></P><P>&nbsp;</P><P style=" text-align: center; "><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="SimonMDM_2-1713517469327.png" style="width: 543px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98681iAC52D08884032AC0/image-dimensions/543x118?v=v2" width="543" height="118" role="button" title="SimonMDM_2-1713517469327.png" alt="SimonMDM_2-1713517469327.png" /></span><I><SPAN>example of TS error for missing types</SPAN></I></P><P>&nbsp;</P><P><SPAN>It prevents errors that might otherwise require tedious debugging later on. And a big plus, it makes understanding the code easier, especially when dealing with a developer who isn't generous with comments <span class="lia-unicode-emoji" title=":grinning_face_with_sweat:">😅</span>. This investment proves to be profitable in the long term for application maintenance, as it helps reduce errors introduced during future modifications.</SPAN></P><P>&nbsp;</P><P><SPAN>To find the correct type when using a method, I refer to the </SPAN><A href="https://ui5.sap.com/#/api/" target="_blank" rel="noopener noreferrer"><SPAN>API Reference - Demo Kit - SAPUI5 SDK</SPAN></A><SPAN> website. Alternatively, we can use </SPAN><STRONG>BAS</STRONG><SPAN>; if we hover the mouse over the method, a pop-up appears and provides the types that can be used.</SPAN></P><P style=" text-align: center; "><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="SimonMDM_3-1713517469422.png" style="width: 550px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98684i21472E0461349866/image-dimensions/550x120?v=v2" width="550" height="120" role="button" title="SimonMDM_3-1713517469422.png" alt="SimonMDM_3-1713517469422.png" /></span><I><SPAN>example of documentation included in BAS</SPAN></I></P><H3 id="toc-hId-139133023"><STRONG>Interfaces and Inheritance:</STRONG></H3><P><SPAN>Drawing from object-oriented principles, TypeScript empowers developers with the creation of classes, interfaces, and inheritances. Interfaces offer great convenience as they allow us to precisely define the structure of objects, specifying the necessary properties along with their associated data types. They are truly a "must-have" as they ensure code consistency, making it easier to maintain and evolve. Moreover, the ability to reuse these interfaces throughout the application contributes to a modular and efficient design.</SPAN></P><P>&nbsp;</P><P style=" text-align: center; "><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="SimonMDM_4-1713517469397.png" style="width: 485px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98683i1602C4400F73954A/image-dimensions/485x50?v=v2" width="485" height="50" role="button" title="SimonMDM_4-1713517469397.png" alt="SimonMDM_4-1713517469397.png" /></span><I><SPAN>example of interface definition</SPAN></I></P><H3 id="toc-hId--57380482"><STRONG>ES6 Features:</STRONG></H3><P><SPAN>Another aspect I particularly appreciated was the ES6 features. Although not specific to TypeScript, it was my experience with this language that led me to these features. They allow for writing more elegant and clear code, while ensuring application logic with fewer lines, greatly simplifying maintenance.</SPAN></P><H2 id="toc-hId--382976706"><SPAN>Challenges Faced and Overcome</SPAN></H2><H3 id="toc-hId--450407492"><STRONG>Library Integration:</STRONG></H3><P><SPAN>Despite its advantages, integrating TypeScript with SAP libraries sometimes presented challenges. For example, I needed to use the "sap/ushell/Container" libraries into a project extension: my project refused to compile because TypeScript didn't know "sap/ushell/Container".</SPAN></P><P><I><SPAN>Issue raised on GitHub </SPAN></I><SPAN>:</SPAN><SPAN><BR /></SPAN><A href="https://github.com/SAP/ui5-typescript/issues/347" target="_blank" rel="noopener nofollow noreferrer"><SPAN>`sap.ushell.Container` removed in 1.100? · Issue #347 · SAP/ui5-typescript · GitHub</SPAN></A></P><P><SPAN>So developers may encounter issues such as missing typings or compatibility issues, requiring workarounds until resolved by SAP through updates.</SPAN></P><H3 id="toc-hId--646920997"><STRONG>Learning Curve:</STRONG></H3><P><SPAN>Transitioning from JavaScript to TypeScript may involve a learning curve for developers unfamiliar with static typing and TypeScript-specific syntax. While the initial investment in learning TypeScript pays off in the long run, it may temporarily impact development velocity.</SPAN></P><H2 id="toc-hId--625262864"><SPAN>Conclusion</SPAN></H2><P><SPAN>While at first I complained about having to give up my good old JavaScript for TypeScript (like a good Frenchman <span class="lia-unicode-emoji" title=":grinning_face_with_sweat:">😅</span>), I finally got the taste for TypeScript and wouldn't go back for anything in the world. From enhanced development comfort to improved code maintenance and readability, TypeScript has become an indispensable tool in my SAPUI5 arsenal.</SPAN><SPAN><BR /></SPAN><SPAN>With trust in SAP's commitment to resolving library-related issues, my journey with TypeScript continues to evolve, promising a future of enriched development experiences!</SPAN></P><P>&nbsp;</P><P style=" text-align: center; "><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="SimonMDM_5-1713517469554.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98685i209D257F6E86C232/image-size/medium?v=v2&amp;px=400" role="button" title="SimonMDM_5-1713517469554.png" alt="SimonMDM_5-1713517469554.png" /></span><EM>credit:&nbsp;<A href="https://twitter.com/ChickyThoughts" target="_blank" rel="nofollow noopener noreferrer">Chicken Thoughts (@ChickyThoughts) / X (twitter.com)</A></EM></P><P>&nbsp;</P><P>&nbsp;</P> 2024-04-22T11:05:53.164000+02:00 https://community.sap.com/t5/technology-blogs-by-members/sap-cap-controller-service-repository-architecture/ba-p/13674697 SAP CAP: Controller - Service - Repository architecture 2024-04-22T13:52:18.556000+02:00 Dragolea https://community.sap.com/t5/user/viewprofilepage/user-id/170147 <P>Hi all,</P><H3 id="toc-hId-1121669954">Introduction</H3><P>Many of us are leveraging SAP CAP (Node.js with TypeScript) for our projects.<BR /><BR />While CAP provides an abstract mechanism to derive<SPAN>&nbsp;<STRONG>RESTful oData<SPAN>&nbsp;services given a CDS data model there's always room for more efficiency and improvement in the implementing communication between<SPAN>&nbsp;<STRONG><EM>business logic, service, and persistence layer.</EM></STRONG></SPAN></SPAN></STRONG></SPAN></P><P><STRONG><EM>-</EM></STRONG></P><H3 id="toc-hId-925156449"><STRONG>Blog series</STRONG></H3><P><STRONG>1.&nbsp;<A title="CDS-TS-Dispatcher: Simplifying SAP CAP TypeScript Development" href="https://community.sap.com/t5/technology-blogs-by-members/cds-ts-dispatcher-simplifying-sap-cap-typescript-development/ba-p/13572824" target="_blank">CDS-TS-Dispatcher: Simplifying SAP CAP TypeScript Development&nbsp;</A></STRONG></P><P><STRONG>2.&nbsp;<A title="CDS-TS-Repository: Simplify SAP CAP Entity persistance with BaseRepository" href="https://community.sap.com/t5/technology-blogs-by-members/cds-ts-repository-simplify-sap-cap-entity-persistance-with-baserepository/ba-p/13572903" target="_self">CDS-TS-Repository: Simplify SAP CAP Entity persistance with BaseRepository</A></STRONG></P><P><EM>Crafted by developers for developers b</EM><EM>y&nbsp;</EM><STRONG><EM><A href="https://www.abs-gmbh.de/" target="_blank" rel="noopener nofollow noreferrer">ABS &amp; DxFrontier team</A></EM></STRONG></P><P><STRONG>-</STRONG></P><H3 id="toc-hId-728642944"><STRONG>Prerequisites</STRONG></H3><P>Before we dive in, you should have a basic understanding of&nbsp;<STRONG><A title="CDS-TS-Dispatcher: Simplifying SAP CAP TypeScript Development" href="https://community.sap.com/t5/technology-blogs-by-members/cds-ts-dispatcher-simplifying-sap-cap-typescript-development/ba-p/13572824" target="_blank">CDS-TS-Dispatcher: Simplifying SAP CAP TypeScript Development&nbsp;<BR /></A></STRONG></P><P>&nbsp;</P><H3 id="toc-hId-532129439"><STRONG>Section 1: Introduction to Controller - Service - Repository - design pattern</STRONG></H3><P><SPAN>This pattern provides a structured approach to organizing the various components of our <STRONG>SAP CAP applications</STRONG>, promoting <STRONG>modularity</STRONG>, <STRONG>scalability</STRONG>, and <STRONG>maintainability</STRONG>.</SPAN></P><P><SPAN>Each layer should be structured based on domain of responsabilities like :&nbsp;</SPAN></P><UL><LI><SPAN><STRONG>Controller -</STRONG>&nbsp;Responsible for managing the <STRONG>REST</STRONG> interface to the business logic implemented in Service</SPAN></LI><LI><STRONG>Service -&nbsp;</STRONG>Contains business logic implementations, the middleware between&nbsp;<STRONG>Controller</STRONG> and <STRONG>Repository</STRONG>.</LI><LI><STRONG>Repository&nbsp;-&nbsp;</STRONG>This component is dedicated to handling entity manipulation operations by leveraging the power of&nbsp;<STRONG><A href="https://cap.cloud.sap/docs/node.js/cds-ql" target="_blank" rel="nofollow noopener noreferrer">CDS-QL.</A></STRONG></LI></UL><P><STRONG><SPAN><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Controller Service Repository design pattern" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/99778iABEADCB0E7ED27D9/image-size/large?v=v2&amp;px=999" role="button" title="2024-04-18_15-49-07.png" alt="Controller Service Repository design pattern" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Controller Service Repository design pattern</span></span></SPAN></STRONG></P><H3 id="toc-hId-335615934">&nbsp;</H3><H3 id="toc-hId-139102429"><STRONG>Section 2: Introduction to Controller <FONT color="#FFCC00">(@EntityHandler)&nbsp;</FONT><FONT color="#000000">layer</FONT></STRONG></H3><P>Now we will create the <STRONG>Controller layer </STRONG>and for that we created a new class&nbsp;<STRONG>BookHandler </STRONG>which is annotated with the&nbsp;<STRONG><STRONG><EM>@EntityHandler(Book) </EM></STRONG></STRONG><EM>decorator.</EM></P><P><EM><STRONG>@EntityHandler </STRONG>decorator takes as an argument the&nbsp;<STRONG>Book entity,&nbsp;</STRONG>this will have effect on all event decorators like<STRONG>&nbsp;@BeforeRead(), @AfterRead() ..., </STRONG>all events will be executed exclusively for this <STRONG>Book entity.</STRONG></EM></P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>@EntityHandler(Book) class BookHandler { @Inject(SRV) private readonly srv: Service; @Inject(BookService) private readonly bookService: BookService; @AfterCreate() private async afterCreate(@Result() result: Book, @Req() req: Request): Promise&lt;void&gt; { this.bookService.validateData(result, req); } @BeforeRead() @Use(MiddlewareMethodBeforeRead) private async beforeRead(@Req() req: TypedRequest&lt;Book&gt;): Promise&lt;void&gt; { this.bookService.showConsoleLog(); } @AfterRead() private async afterRead( @Req() req: Request, @Results() results: Book[], @SingleInstanceSwitch() singleInstance: boolean, @IsColumnSupplied&lt;Book&gt;('price') hasPrice: boolean, @IsPresent('SELECT', 'columns') hasColumns: boolean, @IsRole('Developer', 'AnotherRole') role: boolean, @GetRequest('locale') locale: Request['locale'], Promise&lt;void&gt; { await this.bookService.manageAfterReadMethods({ req, results, singleInstance }); } @AfterUpdate() private async afterUpdate(@Result() result: Book, @Req() req: TypedRequest&lt;Book&gt;): Promise&lt;void&gt; { await this.bookService.addDefaultTitleText(result, req); } @AfterDelete() private async afterDelete(@Result() deleted: boolean, @Req() req: Request): Promise&lt;void&gt; { this.bookService.notifyItemDeleted(req, deleted); } } export default BookHandler;</code></pre><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>@EntityHandler(Book)&nbsp;</STRONG>decorator will registers&nbsp;<STRONG><EM><STRONG>5 events :</STRONG></EM></STRONG></P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>@AfterCreate() @BeforeRead() @AfterRead() @AfterUpdate() @AfterDelete()</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>Every decorator will create the corresponding event in the <STRONG>SAP CAP CDS event registration</STRONG>, this means that the callback of every decorator will be triggered when a <STRONG>REST (CRUD) Request</STRONG> is performed.</P><P><STRONG>@BeforeRead() </STRONG>and<STRONG> @AfterRead() </STRONG>will be triggered when a new<STRONG> GET request </STRONG>is performed<STRONG>&nbsp;:</STRONG></P><P><STRONG>Example:</STRONG>&nbsp;&nbsp;<STRONG>GET <A href="http://localhost:4004/odata/v4/catalog/Books" target="_blank" rel="noopener nofollow noreferrer">http://localhost:4004/odata/v4/catalog/Book</A></STRONG></P><P><STRONG>-</STRONG></P><P><STRONG>@Inject()&nbsp;</STRONG>dependencies</P><P>We inject the <EM><STRONG>BookService class </STRONG>by using<STRONG> Dependendy injection&nbsp;</STRONG>and gaining visibility over all public methods created in the BookService class:</EM></P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>@Inject(BookService) private readonly bookService: BookService</code></pre><P>&nbsp;</P><P>&nbsp;</P><H3 id="toc-hId--57411076"><STRONG>Section 3: Introduction to Service&nbsp;<FONT color="#3366FF">(@Servicelogic) <FONT color="#000000">layer</FONT></FONT></STRONG></H3><P>In&nbsp;<STRONG>@ServiceLogic() </STRONG>will reside all of the customer business logic. This<STRONG> @ServiceLogic() </STRONG>will make a<STRONG> connection </STRONG>between<STRONG> @EntityHandler(Book) </STRONG>and<STRONG> @Repository()&nbsp;</STRONG></P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>@ServiceLogic() class BookService { @Inject(SRV) private readonly srv: Service; @Inject(BookRepository) private readonly bookRepository: BookRepository; // PRIVATE routines private async emitOrderedBookData(req: Request) { await this.srv.emit('OrderedBook', { book: 'dada', quantity: 3, buyer: req.user.id }); } private notifySingleInstance(req: Request, singleInstance: boolean) { if (singleInstance) { req.notify('Single instance'); } else { req.notify('Entity set'); } } private enrichTitle(results: Book[]) { results.map((book) =&gt; (book.title += ` -- 10 % discount!`)); } // PUBLIC routines public async manageAfterReadMethods(args: { req: Request; results: Book[]; singleInstance: boolean }) { await this.emitOrderedBookData(args.req); this.notifySingleInstance(args.req, args.singleInstance); this.enrichTitle(args.results); } public notifyItemDeleted(req: Request, deleted: boolean) { req.notify(`Item deleted : ${deleted}`); } public showConsoleLog() { console.log('****************** Before read event'); } public validateData(result: Book, req: Request) { if (result.currency_code === '') { return req.reject(400, 'Currency code is mandatory!'); } } public async addDefaultTitleText(result: Book, req: TypedRequest&lt;Book&gt;) { await this.bookRepository.update({ ID: req.data.ID }, { title: 'Dracula' }); } public async verifyStock(req: ActionRequest&lt;typeof submitOrder&gt;) { const { book, quantity } = req.data; const bookFound = await this.bookRepository.findOne({ ID: book! }); if (quantity != null) { if (quantity &lt; 1) { return req.reject(400, `quantity has to be 1 or more`); } // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!bookFound) { return req.error(404, `Book #${book} doesn't exist`); } if (bookFound.stock !== null &amp;&amp; quantity &gt; bookFound.stock!) { return req.reject(409, `${quantity} exceeds stock for book #${book}`); } await this.bookRepository.update(bookFound, { stock: (bookFound.stock! -= quantity), }); } await this.srv.emit('OrderedBook', { book, quantity, buyer: req.user.id }); return { stock: bookFound!.stock }; } } export default BookService;</code></pre><P>&nbsp;</P><P>&nbsp;</P><H3 id="toc-hId--253924581"><STRONG>Section 4: Introduction to Repository&nbsp;<FONT color="#339966">(@Repository) <FONT color="#000000">layer</FONT></FONT></STRONG></H3><P><FONT color="#000000"><STRONG>This BaseRepository will provide out-of-the-box functionalities like:&nbsp;</STRONG></FONT></P><UL><LI>.create(): Create new records in the table.</LI><LI>.update(): Updates a new record in the table.</LI><LI>.createMany(): Creates many records in the table.</LI><LI>.findAll(): Retrieve all records from the table.</LI><LI>.find(): Query the database to find specific data.</LI><LI>.delete(): Remove records from the database.</LI><LI>.exists(): Check the existence of data in the table.</LI><LI>.<EM><STRONG>.. and many more actions</STRONG></EM></LI></UL><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>@Repository() class BookRepository extends BaseRepository&lt;Book&gt; { constructor() { super(Book); } // ... define custom CDS-QL actions if BaseRepository ones are not satisfying your needs ! } export default BookRepository;</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>In the&nbsp;<STRONG>@Repository() </STRONG>we've utilizied the<STRONG> BaseRepository </STRONG>by extending the<STRONG> BookRepository class&nbsp;</STRONG>with&nbsp;<STRONG><A title="CDS-TS-Repository" href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="nofollow noopener noreferrer">CDS-TS-Repository</A></STRONG>&nbsp;will give us access to the most common CDS-QL actions.</P><P>You can find additional documentation for<SPAN>&nbsp;<STRONG>BaseRepository<SPAN>&nbsp;at<SPAN>&nbsp;<STRONG><A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository GitHub</A></STRONG></SPAN></SPAN></STRONG></SPAN></P><H3 id="toc-hId--450438086">&nbsp;</H3><H3 id="toc-hId--646951591"><STRONG>Conclusion</STRONG></H3><P>In conclusion,&nbsp;<STRONG><A href="https://github.com/dxfrontier/cds-ts-dispatcher" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Dispatcher</A></STRONG>&nbsp;combined with&nbsp;<STRONG><A href="https://github.com/dxfrontier/cds-ts-repository" target="_blank" rel="noopener nofollow noreferrer">CDS-TS-Repository</A></STRONG>&nbsp;is a powerful tool that can speed up your SAP CAP TypeScript projects by eliminating repetitive code and being a better fit for common team architecture setups.</P><H3 id="toc-hId--918696465">&nbsp;</H3><H3 id="toc-hId--1115209970"><STRONG>Additional Resources</STRONG></H3><P>Find an example of usage of the&nbsp;<A href="https://github.com/dxfrontier/cds-ts-samples" target="_blank" rel="nofollow noopener noreferrer">CDS-TS-Samples GitHub</A></P> 2024-04-22T13:52:18.556000+02:00 https://community.sap.com/t5/technology-blogs-by-members/introducing-fileuploadxls-the-ultimate-excel-custom-widget-for-sap/ba-p/13704913 Introducing fileUploadXLS: The Ultimate Excel Custom Widget for SAP Analytics Cloud 2024-05-17T14:49:54.822000+02:00 rohitchouhan https://community.sap.com/t5/user/viewprofilepage/user-id/782213 <P><STRONG>fileUploadXLS</STRONG>&nbsp;custom widget is designed to streamline the process of handling Excel or XLS files within SAP Analytics Cloud (SAC), enabling users to easily retrieve file data in various formats such as JSON, raw text, and more.</P><H3 id="toc-hId-1143837089">Key Features</H3><UL><LI><STRONG>File Format Flexibility</STRONG>: Supports Excel and XLS files, ensuring compatibility with common data sources.</LI><LI><STRONG>Data Retrieval</STRONG>: Easily retrieve file data in JSON, raw text, or other formats.</LI><LI><STRONG>Filtering Capabilities</STRONG>: Apply filters to records based on value or character presence.</LI><LI><STRONG>Data Hierarchy</STRONG>: Set hierarchical structures for better data organization.</LI></UL><H3 id="toc-hId-947323584">Installation Guide</H3><P>Watch Full Guidance on YouTube As well</P><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%2Fi58hL4gZh0g%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Di58hL4gZh0g&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fi58hL4gZh0g%2Fhqdefault.jpg&amp;key=b0d40caa4f094c68be7c29880b16f56e&amp;type=text%2Fhtml&amp;schema=youtube" width="200" height="112" scrolling="no" title="Excel File Upload using fileUploadXLS Custom Widget | SAP Analytics Cloud | by Rohit Chouhan" frameborder="0" allow="autoplay; fullscreen; encrypted-media; picture-in-picture;" allowfullscreen="true"></iframe></div></P><P>To integrate the <STRONG>fileUploadXLS</STRONG> widget into your SAP application, follow these simple steps:</P><OL><LI><P><STRONG>Download the Widget</STRONG>:</P><P>Click the link above to download the <A href="https://sap-custom-widget.github.io/?dl=fileUploadXLS" target="_blank" rel="noopener nofollow noreferrer"><FONT color="#3366FF">fileUploadXLS.json</FONT></A> file.</P>Or Download it from Github :&nbsp;<A href="https://github.com/SAP-Custom-Widget/fileUploadXLS" target="_blank" rel="noopener nofollow noreferrer">https://github.com/SAP-Custom-Widget/fileUploadXLS</A></LI><LI><P><STRONG>Add to SAC Portal</STRONG>:</P><UL><LI>Navigate to your SAC Portal.</LI><LI>Select "Analytic Application" from the left sidebar.</LI><LI>Go to the "Custom Widget" tab.</LI><LI>Click the "+" icon on the right side and upload the fileUploadXLS.json file you downloaded.</LI></UL></LI><LI><P><STRONG>Start Using the Widget</STRONG>:</P><UL><LI>Once uploaded, the widget is ready to use in your applications.</LI></UL></LI></OL><H3 id="toc-hId-750810079">Methods Overview</H3><P>The widget offers a range of methods to interact with your data:</P><H4 id="toc-hId-683379293">Set Methods</H4><TABLE><TBODY><TR><TD width="269.648px" height="30px"><FONT color="#333333"><STRONG>Method</STRONG></FONT></TD><TD width="147.336px" height="30px"><FONT color="#333333"><STRONG>Parameter Type</STRONG></FONT></TD><TD width="334.016px" height="30px"><FONT color="#333333"><STRONG>Description</STRONG></FONT></TD></TR><TR><TD width="269.648px" height="57px">setHierarchyStructure(hierarchy)</TD><TD width="147.336px" height="57px">Selection/Object array</TD><TD width="334.016px" height="57px">Sets the hierarchical structure for the data.</TD></TR><TR><TD width="269.648px" height="57px">setFilterByValue(filterByValue)</TD><TD width="147.336px" height="57px">Selection/Object array</TD><TD width="334.016px" height="57px">Applies a filter to records based on a constant value.</TD></TR><TR><TD width="269.648px" height="57px">setFilterByContains(filterByContains)</TD><TD width="147.336px" height="57px">Selection/Object array</TD><TD width="334.016px" height="57px">Applies a filter to records based on a value that contains a specific character.</TD></TR></TBODY></TABLE><H4 id="toc-hId-486865788">Get Methods</H4><TABLE><TBODY><TR><TD><STRONG>Method</STRONG></TD><TD><STRONG>Return Type</STRONG></TD><TD><STRONG>Description</STRONG></TD></TR><TR><TD>getDataAsJson()</TD><TD>Selection/Object array</TD><TD>Retrieves uploaded data in JSON/Selection/Object format.</TD></TR><TR><TD>getDataAsRaw()</TD><TD>string</TD><TD>Retrieves uploaded data in raw string format.</TD></TR><TR><TD>getTotalRecordsCount()</TD><TD>integer</TD><TD>Gets the total count of records.</TD></TR><TR><TD>getHeaders()</TD><TD>string[] array</TD><TD>Gets the headers of uploaded data as an array.</TD></TR></TBODY></TABLE><P>I hope you find the <STRONG>SAC-P Custom Widget: fileUploadXLS</STRONG> useful for your projects. Your feedback and suggestions are always welcome, as they help in making this tool even better. Thank you for your support, and happy data handling!</P><H3 id="toc-hId-161269564">Conclusion</H3><P>The <STRONG>SAC-P Custom Widget: fileUploadXLS</STRONG> is a powerful addition to SAP Analytics Cloud, designed to simplify the process of working with Excel and XLS files. With its robust data retrieval capabilities and flexible filtering options, it enhances your ability to manage and manipulate data efficiently. By following the easy installation steps, you can quickly integrate this widget into your applications and start leveraging its features immediately.</P> 2024-05-17T14:49:54.822000+02:00 https://community.sap.com/t5/technology-blogs-by-members/create-custom-tables-in-sap-analytics-cloud-with-dynamotable/ba-p/13705854 Create Custom Tables in SAP Analytics Cloud with dynamoTable 2024-05-18T17:15:33.400000+02:00 rohitchouhan https://community.sap.com/t5/user/viewprofilepage/user-id/782213 <P><STRONG>dynamoTable&nbsp;</STRONG>custom widget is designed to help you create dynamic, customizable tables within SAP Analytics Cloud (SAC), enhancing your data presentation and analysis capabilities.</P><H3 id="toc-hId-1143866044">Key Features</H3><UL><LI><STRONG>Custom Table Creation</STRONG>: Easily create and manage tables to suit your specific data needs.</LI><LI><STRONG>Flexible Data Handling</STRONG>: Input data as an array of objects for seamless integration.</LI><LI><STRONG>Simple Integration</STRONG>: Quickly add the widget to your SAC applications with straightforward installation steps.</LI></UL><H3 id="toc-hId-947352539">Installation Guide</H3><P>YouTube Video Tutorial</P><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%2FJZwDJQmuQMA%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DJZwDJQmuQMA&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FJZwDJQmuQMA%2Fhqdefault.jpg&amp;key=b0d40caa4f094c68be7c29880b16f56e&amp;type=text%2Fhtml&amp;schema=youtube" width="400" height="225" scrolling="no" title="Create Custom Table in SAC with Custom Records | SAP Analytics Cloud | by Rohit Chouhan" frameborder="0" allow="autoplay; fullscreen; encrypted-media; picture-in-picture;" allowfullscreen="true"></iframe></div></P><P>To integrate the <STRONG>dynamoTable</STRONG> widget into your SAP application, follow these simple steps:</P><OL><LI><P><STRONG>Download the Widget</STRONG>:</P><P>Click the link above to download the <FONT color="#3366FF"><A href="https://sap-custom-widget.github.io/?dl=dynamoTable" target="_blank" rel="noopener nofollow noreferrer">dynamoTable.json</A></FONT> file.</P>Or find on GitHub:&nbsp;<A href="https://github.com/SAP-Custom-Widget/dynamoTable" target="_blank" rel="noopener nofollow noreferrer">https://github.com/SAP-Custom-Widget/dynamoTable</A>&nbsp;</LI><LI><P><STRONG>Add to SAC Portal</STRONG>:</P><UL><LI>Navigate to your SAC Portal.</LI><LI>Select "Analytic Application" from the left sidebar.</LI><LI>Go to the "Custom Widget" tab.</LI><LI>Click the "+" icon on the right side and upload the dynamoTable.json file you downloaded.</LI></UL></LI><LI><P><STRONG>Start Using the Widget</STRONG>:</P><UL><LI>Once uploaded, the widget is ready to use in your applications.</LI></UL></LI></OL><H3 id="toc-hId-750839034">Methods Overview</H3><P>The widget offers a simple method to manage your table data:</P><TABLE><TBODY><TR><TD><STRONG>Method</STRONG></TD><TD><STRONG>Return Type</STRONG></TD><TD><STRONG>Description</STRONG></TD></TR><TR><TD>setTableData(arrayOfObject)</TD><TD>Selection/Object array</TD><TD>Sets the table data using an array of objects.</TD></TR></TBODY></TABLE><H3 id="toc-hId-554325529">Example Usage</H3><P>Here’s an example of how to use the setTableData method to populate your table:</P><P>&nbsp;</P><pre class="lia-code-sample language-javascript"><code>var data = [ { productId: "1", productName: "Wireless Mouse", category: "Electronics", price: "25.99", quantitySold: "120", dateOfSale: "2024-05-01" }, { productId: "2", productName: "Bluetooth Headphones", category: "Electronics", price: "79.99", quantitySold: "75", dateOfSale: "2024-05-02" } ]; dynamoTable_1.setTableData(data);</code></pre><P>&nbsp;</P><H3 id="toc-hId-357812024">Output</H3><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="rohitchouhan_2-1716044525328.png" style="width: 587px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/112624iDCE50E24FBE6463E/image-dimensions/587x243?v=v2" width="587" height="243" role="button" title="rohitchouhan_2-1716044525328.png" alt="rohitchouhan_2-1716044525328.png" /></span></P><H3 id="toc-hId-161298519">Conclusion</H3><P>The <STRONG>SAC-P Custom Widget: dynamoTable</STRONG> is a powerful tool for creating and customizing tables in SAP Analytics Cloud. By following the simple installation steps, you can enhance your data presentations and streamline your analytics processes. Explore the features of dynamoTable and take your data handling to the next level.</P> 2024-05-18T17:15:33.400000+02:00