Quantcast
Channel: SAPUI5 Developer Center
Viewing all 789 articles
Browse latest View live

Best practices, guidelines, good architecture & lots of stupid questions, etc.

$
0
0

What am I trying to achieve here?

Well, a long time ago we were doing our first Web Dynpro Java project on what was then pretty bleeding edge technology from SAP.  My colleague Richard Tucker and I spent a lot of time determining the "best" way to componentise our efforts to ensure good re-use and maintainability.  So much so, Rich went on to give a session at that year's SAPTechEd and also worked on a document/whitepaper for SAPInsider (if my memory serves me correctly.)  In short, we got to a really good end game in terms of template for componentising our WD Java applications that we've pretty much stuck to ever since.  I'd quite like to get some sort of similar, maybe less formal but equally useful template of apps, components, etc. within the SAPUI5 world, especially as the work I'll likely be doing with it will be delivered by teams of people, rather than single developers who can control all objects.

 

Let's get meddlin'.

Before I really get into this1, firstly let me apologise for the increase in quantity and decrease in quality of questions, comments and thoughts I'm likely to add to SAPUI5/HTML5 Space over the coming months.  For better or worse, I've got some time to actually get my hands a little dirty with some SAPUI5 development.  I'm more used to Java Web Dynpro development and indeed, even with that it's been some time since I've really got my hands dirty with "proper code"

 

As mentioned, I'm now doing some SAPUI5 meddling in anger and as is my customary approach, I seem to be spending a lot of time reading documents, blogs and examples, and wondering about best practices and guidelines for building something, rather than actually coding the damn thing.  As I alluded to in one of my recent discussions, it seems I'm falling foul of one of SAPUI5's great strengths - it's flexibility.  There are just too many ways to achieve the same thing.

 

To try and get my head around some of the key concepts, I've set myself the task of building what is a very simple app:2

  • Simple one page with no page-to-page navigation
  • Designed and built primarily for desktop (but could use sap.m controls if needed)
  • Is a simple combination of some images above a data table with a few columns

 

All sounds pretty straight forward right?  Well, to make it more worthwhile, I also want to achieve/make use of the following:

  • Use a mock.json file to populate the table via model binding
  • Dynamically generate images (simple coloured DIV's actually) based on the number of rows in the data table, so 3 rows of data mean 3 images
  • Control how the DIV is built based on values from each row, so one of the columns contains a value that dictates the size of the DIV for instance
  • Explore componentisation with the Component.js object, to create a single reusable UI component to represent the resizable DIV image and also a faceless component that acts as a data model between my table UI and the mock.json model, so I essentially end up with a reusable data access component too
  • Make use of i18n properties files to remove hard-coded texts
  • Ensure the project has a file & folder structure that makes sense and supports further componentisation, reuse and maintenance

 

To many of the afficiandos on these here forums, I'm still not probably setting the world on fire but for me, this is a first principals type of effort; a way of understanding how an application should be structured and built in a "best practice" manner; finally a way of creating a template app that I can quickly use as a quick-start for other app's.

 

Here's a rough mockup of what I'm aiming for:-

AppDemo.jpg

It should be obvious a slick UI isn't what I'm aiming for here, and form certainly does follow function!

 

To start the ball rolling, I created a new SAPUI5 Application project (called TemplateCompApp) and started by building up a folder structure as a skeleton of where I wanted to end up in terms of content.  I allowed the wizard to create the initial view for me (calling it App) but I'm not 100% sure if this is the right or wrong thing to do.  On the one hand, I like when a wizard can do some of the heavy lifting for me but on the other, I worry what content is it creating for me that will be incompatible with any further entities I create - I guess we'll have to wait and see.

 

I ended up with this:

SAPUI5 FolderStructure.jpg

This is quite an extensive folder structure, considering I haven't actually built anything yet!  As I'm sat here building it up, I'm not sure if I've put the components folder in the right place.  With hindsight, I think it should actually sit underneath the "templatecompapp" folder.  I'm mostly happy with how I've got my individual components, with the UI and faceless ones having the relevant sub-folders as needed.  (Of course, I don't really need to create empty folders just for the sake of it but this is a template/PoC after all.)

 

It's also fair to say my logic in this regard is greatly influenced with how I approach Web Dynpro Java componentisation and re-use (see the opening paragraph for the background in case you've already forgotten.)  As I think about this more, it becomes apparent that yes, my components folder really should be within my "templatecompapp" folder, so a bit of shuffling leads me to this:

SAPUI5 FolderStructure2.jpg

At this point I've also decided to make my actual data table a separate UI component as well, and for the sake of completeness from a template perspective, I've renamed the components folders with a numeric suffix.  Ultimately I should end up with a main application and view, that re-uses one of the data table components and 0..n of the dynamic DIV components.  The data for the table will come from the faceless component.  See - it makes perfect sense when you say it quick like that...

 

From a slightly more abstract perspective, this is the kind of generic model I'm looking for from my SAPUI5 apps and components:

SAPUI5 ComponentStructure.jpg

 

Summary

At this stage, I've lost an hour or so creating and deleting various folders and files but still haven't got near to creating content.  It's important to me to get the foundations right with this sort of thing, hence me over-thinking all of this.  So now, over to the rest of you - what am I doing wrong, right, could do better, etc?  What are people's opinions on componentisation, re-use and my approach to structuring the files & folders in my projects?  Many may say this kind of structure just doesn't fit with the ethos of SAPUI5 and they may be right - all feedback is welcome   Hopefully at some point I'll get round to building something that actually does something too...

 

1 As with many of my blog postings, this started off as a long winded discussion but I thought it made more sense in this form - there are lots of implied questions throughout (where it's obvious I don't know what I'm doing!) so I hope this generates some decent conversation in the comments section below.

2 Luckily, one of my customers has a number of current projects with an element of 7.31 EP and SAPUI5 so I've had some inspiration from there - often I find building demo and PoC app's for self-development is hampered by not knowing what to build.


TDD and JavaScript - JavaScript Unit Tests for UI5 Applications

$
0
0

Introduction

Unit-tests are one of the crucial tools in Test-Driven Development (TDD) and should be an important pillar of your test strategy. If you're coming from a different programming language or infrastructure setup and want to develop your JavaScript application test-driven, two main questions need to be solved:

  1. How can you setup your infrastructure to efficiently execute your unit tests?
  2. How can you write testable code in JavaScript?


This article tackles the first topic. It is part of a series of articles about Test-Driven Development and JavaScript in which also the second topic will be covered in depth. Press 'like' onthe overview blog post to stay tuned and get updates about more topics around software engineering with SAPUI5 and JavaScript!

 

Scope of this Article

This article assumes that you're building your project with maven. If your backend is implemented in Java, maven is one of the most popular ways to manage your dependencies and automate your build. Some parts and takeaways are still useful when you employ a different build technology but want to use jasmine for unit testing UI5 applications or if you use maven but want a different unit testing framework, such as QUnit.

The specific goals to achieve with the descriptions in this article are

  • to execute tests during development and have fast feedback cycles
  • to execute tests during local maven build
  • to execute tests on an Continuous Integration (CI) server such as jenkins

Furthermore, the tests during a maven build should require no additional setup on a developer's workplace or on your CI server.

 

Challenges with JavaScript Unit Testing

If you have used a JavaScript unit testing framework such as jasmine or QUnit before, you know how they work: You write put your test code into a test.js file. Then they ask you to embed your test.js and productiveCode.js within a Test.html file containing some additional boilerplate, such as an area to display the test results, the test framework code itself, and any additional libraries you might need. When you open the Test.html with your browser, the tests are executed and the results are shown on the page. For small examples, this is easy, however, in the real world we encounter a number of challenges.

 

[High maintenance effort] You'll end up with manytest.js files, usually one for each unit of code you want to test. Every new file has to be embedded in a Test.html to actually execute the tests. This will lead to a large number of copy-and-pasted Test.html files which have to be maintained individually. This problem is exacerbated by the use of custom JavaScript libraries such as UI5 and by the size of projects when they grow bigger. Custom libraries may bring their own modularization concepts (such as UI5's declare/require or require.js's define/require) which have to be integrated into your Test.html as well.
[Simple feedback access during development] With a growing number of tests and Test.html files it gets harder for developers to simply check the test results when they are making changes to the code. The test infrastructure should provide whether all tests passed of a test failed at a glance.
[Modularization and Dependencies] UI5 and require.js load dependencies during runtime, while your browser's Same-origin Policy denies loading files from your local filesystem. The two possible solutions are either deactivating this security setting just to execute your local tests or to manually set up a web server which contains all necessary test and productive JavaScript files, neither of which is desirable.
[Automatic test execution] The CI server should be able to execute the tests automatically during a build.
[Self-contained setup] The project's build and test should be completely self-contained and require no additional setup. In many cases, you don't have file-system access to your CI server itself and cannot rely on certain programs or libraries to be present on the path. Furthermore, everything that adds complexity to the setup of a developer's workplace should be avoided.

 

Proposed Solution: jasmine-maven-plugin & PhantomJS

The jasmine-maven-plugin provides a solution for some of the previously stated challenges:

  • It collects all of your test.js files and embeds them into a Test.html file based on a template you provide. There are some pre-defined Test.html.template files available, e.g. for frameworks such as require.js which can be adapted for UI5 with little effort. This solves the challenges of high maintenance effort and simple feedback access: No manual embedding of test.js files and dependencies is necessary, and the framework bootstrapping can be maintained in a single file, your Test.html.template. Results are presented on a single page, providing a fast overview.
  • During development, executing mvn jasmine:bdd starts a web server at http://localhost:8234. This helps you to overcome the challenge of either deactivating web security settings or maintaining your own server for testing.
  • During a build, automatic execution can be achieved with PhantomJS, a headless webkit implementation in Qt. Let's assume for the first version of this setup that it is installed on your CI server and developer machines. This assumption will be removed in one of the next setup steps.

This clearly leaves the challenge of a self-contained setup. Using another maven dependency, this can be solved as well. Pre-packaged binaries of PhantomJS are available as maven dependencies which can be downloaded and extracted during the build. Note that these binaries are OS-specific, which complicates the setup.

 

Technical details: How to configure your setup

The setup is described in three parts: Using the jasmine-maven-plugin, automatically installing PhantomJS during the build, and integrating SAPUI5 as a test dependency.

 

Using the jasmine-maven-plugin

Add the following example configuration for the jasmine-maven-plugin to your pom.xml:

<plugins><plugin>  <groupId>com.github.searls</groupId>  <artifactId>jasmine-maven-plugin</artifactId>  <version>1.3.1.4</version>  <executions>  <execution>    <goals>    <goal>test</goal>    </goals>  </execution>  </executions>  <configuration>  <!-- Where is your productive JavaScript code -->  <jsSrcDir>${basedir}/src/main/js</jsSrcDir>  <!-- Where are your test.js files -->  <jsTestSrcDir>${basedir}/src/test/js</jsTestSrcDir>  <!-- Use PhantomJS to access the Test.html, assuming it is available on your PATH -->  <webDriverClassName>org.openqa.selenium.phantomjs.PhantomJSDriver</webDriverClassName>  <!-- Create a report in surefire format to be read by jenkins -->  <junitXmlReportFileName>../surefire/TEST-${project.name}.jasmine.xml</junitXmlReportFileName>  <!-- Execute tests automatically every 60 seconds -->  <autoRefreshInterval>60</autoRefreshInterval>  </configuration></plugin></plugins>

 

 

 

Installing PhantomJS during the build

PhantomJS can be installed as a binary from the maven central repository by using the maven-dependency-plugin. Add this execution to your pom.xml:


 

<plugin>  <artifactId>maven-dependency-plugin</artifactId>  <version>2.6</version>  <executions>    <execution>      <id>extract-js-unit-tests-runtime</id>      <phase>process-test-resources</phase>      <goals>        <goal>unpack</goal>      </goals>      <configuration>        <markersDirectory>${project.build.directory}/js-tests</markersDirectory>        <overWriteReleases>false</overWriteReleases>        <overWriteSnapshots>false</overWriteSnapshots>        <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>        <artifactItems>          <artifactItem>            <groupId>org.jboss.arquillian.extension</groupId>            <artifactId>arquillian-phantom-binary</artifactId>            <version>1.9.2</version>            <classifier>${phantomjs.os.classifier}</classifier>          </artifactItem>        </artifactItems>        <outputDirectory>${project.build.directory}/js-tests/phantomjs</outputDirectory>        <stripVersion>true</stripVersion>      </configuration>    </execution>  </executions></plugin>

 

 

 

This code uses the variable phantomjs.os.classifier to distinguish between the different binaries for your operating system. This variable can be filled using profiles which are activated based on the used operating system:

 

<!-- Set OS-type variable used for installing phantomjs binary --><profile><id>linux</id><activation>  <os>  <family>unix</family>  </os>  <activeByDefault>false</activeByDefault></activation><properties>  <phantomjs.os.classifier>linux-64</phantomjs.os.classifier>  <phantomjs.path>${project.build.directory}/js-tests/phantomjs/bin/phantomjs</phantomjs.path></properties><!-- Set the executable bit for the phantomjs.sh --><build>  <plugins>  <plugin>    <artifactId>maven-antrun-plugin</artifactId>    <version>1.7</version>    <executions>    <execution>      <id>make-phantomjs-executable</id>      <phase>process-test-classes</phase>      <goals>      <goal>run</goal>      </goals>      <configuration>      <target>        <chmod file="${phantomjs.path}" perm="ugo+rx" />      </target>      </configuration>    </execution>    </executions>  </plugin>  </plugins></build></profile><profile><id>mac</id><activation>  <os>  <family>mac</family>  </os>  <activeByDefault>false</activeByDefault></activation><properties>  <phantomjs.os.classifier>macosx</phantomjs.os.classifier>  <phantomjs.path>${project.build.directory}/js-tests/phantomjs/bin/phantomjs</phantomjs.path></properties></profile><profile><id>windows</id><activation>  <os>  <family>windows</family>  </os>  <activeByDefault>false</activeByDefault></activation><properties>  <phantomjs.os.classifier>windows</phantomjs.os.classifier>  <phantomjs.path>${project.build.directory}/js-tests/phantomjs/phantomjs.exe</phantomjs.path></properties></profile>

Afterwards, the variable phantomjs.path contains the path to the executable. Add a property pointing to this path in the configuration of the jasmine-maven-plugin:

 

<webDriverCapabilities>    <capability>          <name>phantomjs.binary.path</name>          <value>${phantomjs.path}</value>    </capability></webDriverCapabilities>

 

Adding UI5 as a custom library to the setup

So far, you can test JavaScript code, but cannot use UI5 controls or rely on the declare/require statements for modularizing your code. To solve this, we must include a UI5 distribution in the test web server provided by the jasmine-maven-plugin and provide a custom Test.html.template containing bootstrap code. Note, that this code uses the UI5 version available over the content delivery network (CDN) at https://sapui5.hana.ondemand.com/. If UI5 was available at as a maven dependency, you could simply add an execution to the maven-dependency-plugin and unpack a specific version yourself.

You now have to create a custom Test.html.template to load and bootstrap UI5. Create this file, e.g. in src/test/resources and fill it with this content:

 

<!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=$sourceEncoding$">  $if(autoRefresh)$  <meta http-equiv="refresh" content="$autoRefreshInterval$">  $endif$  <title>Jasmine Spec Runner for UI5</title>  $cssDependencies$  $javascriptDependencies$  <script type="text/javascript" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"  id="sap-ui-bootstrap"  data-sap-ui-libs="sap.ui.commons,sap.ui.ux3,sap.ui.table,sap.ui.layout"  data-sap-ui-theme="sap_bluecrystal">  </script>  <!-- Register your library paths with UI5 -->  <script type="text/javascript">    jQuery.sap.registerModulePath("my.library.name", "/src/my/library/name");  </script>  $allScriptTags$</head><body>  <script type="text/javascript">    var specs = $specsList$;    var executeJasmineSpecs = function(){      window.reporter = new jasmine.$reporter$(); jasmine.getEnv().addReporter(reporter);      if ('$reporter$' == 'HtmlReporter') {        jasmine.getEnv().specFilter = function(spec) {          return window.reporter.specFilter(spec);        };      }  //Don't do live updates when running through HTMLUnit      if ('$reporter$' == 'JsApiReporter'){  jasmine.getEnv().updateInterval = Number.MAX_VALUE;      }      jasmine.getEnv().execute();    };    if (window.addEventListener) {      addEventListener('DOMContentLoaded', executeJasmineSpecs, false);    } else {      attachEvent('onload', executeJasmineSpecs);    }  </script></body></html>

Please note that you have to Register your library paths with UI5 in this template. While the resolution of declare/require statements works automatically in your application, the location of the productive JavaScript code will be different on the test server: an additional '/src' will be added in front of all paths. For example, if you have declared your own library/namespace for the file SomeFile with jQuery.sap.declare("my.own.library.SomeFile"); and have your JavaScript sources in /src/main/js, you need to configure this like in the above template. Be sure to adapt this to your project specific needs.


Afterwards, configure this template to be used in your tests. Add these lines to the configuration of the jasmine-maven-plugin:

 

<!-- Use a custom template to generate the Test.html. This includes UI5 bootstrapping --><customRunnerTemplate>${basedir}/src/test/resources/Test.html.template</customRunnerTemplate>

Get Your Hands Dirty: Download an Example Project


You can download a pre-configured example project as an extension to Jens Glander's excellent PersonsList Project, which is available on GitHub. Just follow his detailed instructions and be sure to get Extension-003 by synching branch origin/Extension-003 of the project.

 

A test suite is located in personslist/personslist-web/src/test/js/. The tests are running in each maven build of the complete project. Additionally, you can execute mvn jasmine:bdd within the personslist-web folder and point your browser to http://localhost:8234to execute your tests. The tests are re-executed every 60 seconds or when you press F5 in your browser.

 

When you start adding new tests, simply create a new file within the above folder or extend the already existing suite. To see changes in test code or productive code take effect, a refresh in the browser is enough!

 

Conclusion

In this article, I described how you can use a combination of the jasmine, jasmine-maven-plugin, and PhantomJS

  • to execute tests during development and have fast feedback cycles
  • to execute tests during local maven build
  • to execute tests on an Continuous Integration (CI) server such as jenkins

with no additional setup on a developer's workplace or on your CI server.

The tests enable you to write your JavaScript code TDD-style and have fast-feedback cycles during development.

 

Now it's your turn: Write some clean and tested code!

 

This blog post is part of a series, press 'like' on the overview blog postto stay tuned and get updates about more topics around software engineering with SAPUI5 and JavaScript.

UI5 Boilerplate explained (Part 1)

$
0
0

In this blog I will describe the structure and concepts behind the UI5 SplitApp Boilerplate. It is a template/skeleton for Mobile UI5 Apps and follows the "Mobile First" approach, but also works on desktop (Thanks to the great sap.m controls).


The UI5 Boilerplate can be understood as starting point for your own UI5 Development, both for OpenUI5 and SAPUI5 Apps.

 

The Boilerplate provides a basic application and file structure, implements some core features (navigation, i18n) and contains example views/controllers for different purposes. The UI5 SplitApp Boilerplate code is hosted on Github: 6of5/UI5SplitApp-Boilerplate · GitHub

 

The UI5 Boilerplate also supports SAP and also Non-SAP Development approach, this is reflected in two different git branches:

 

non-SAP centric Development ScenarioSAP-centric Development Scenario
Development ToolAny IDE with Git Support (Sublime Text, Eclipse, IntelliJ,…)Eclipse 4.3 with SAP UI Development Toolkit for HTML5 (SAP UI5 Toolkit)
Git BranchmastereclipseKepler
Git Clone Commandgit clone https://github.com/6of5/UI5SplitApp-Boilerplate.gitgit clone -b eclipseKepler https://github.com/6of5/UI5SplitApp-Boilerplate.git
More Informationhttp://scn.sap.com/community/developer-center/front-end/blog/2014/01/13/ui5-mobile-splitapp-boilerplate/UI5 Boilerplate with Eclipse + SAPUI5 Toolkit
local Dev Runtime Optionsnode.js, Apache HTTP, Apache TomcatWeb App Preview, Apache Tomcat, HANA Cloud Local Runtime

 

Application Structure and File Structure

UI5 Boilerplate is structured via the following folders:

Foldercontains
appUI5 Application Component, which is responsible for general tasks, like initialize core models like i18n
cssCSS files go here
i18nresource bundles for internationalization (i18n) are stored in this folder
imgassets like images, logos,….
jsgeneral javascript files, e.g. app.js
modelUI5 model information (like json, xml files)
viewUI5 view files (XMLViews, JSViews) and the corresponding controllers

 

Let's have a look at the index.html file first:

UI5bp-expl1_01m.png

It is a normal HTML5 document file. After the meta declaration for IE the UI5 bootstrap script is declared. The boilerplate offers here in comments some alternative  sources for the UI5 bootstrap (OpenUI5 external, SAPUI5 external, local SAPUI5 from SAP UI5 Toolkit or providing it via local web server). With local options you can define yourself which dedicated UI5 version you want to use. The index.html links to the JavaScript file app.js:

 

app.js (declare UI5 resources / 'start' Application Component)

UI5bp-expl1_02.png

in the app.js we define where UI5 resources are located. These are in the folders app, model and view. So UI5 will be able to use resources within these folders.And we specify that we require app.Component. This Component represents our Application. We create an ComponentContainer with name "app" and attach it to the DOM element with the id "root", which in our case is the body tag <body class="sapUiBody" id="root">. This willcreate and run our Component (Application):

 

Component.js (Application Component)

UI5bp-expl1_03.png

In the Component (Application) we create the App.view (which represents the UI of our Application) and create the i18n model and attach this to the App.View. Within the component further general tasks could be executed, like loading master data, which can then be used throughout the application. Now lets have a look at the App View:

 

App.view.js (Application UI)

UI5bp-expl1_04.png

In the App View we create a SplitApp which will enable the "iPad" like UI Style. We can then define the Master Page(s) which will be on the smaller left side of the screen (here the Menu View)  and also the Detail Pages (CoffeeList View and Info View). Both JSViews or XMLViews are possible as you can see. And of course you could also use the other view types here. The App View is now returning the SplitApp as member of the mobile Shell. The result can be seen in the browser (with UI5 Boilerplate Version 0.6.0):

UI5bp-expl1_05.png

Next

In the next blog article I want to explain, how you can easily extend the UI5 Boilerplate with further Pages.

Display Smartform (PDF) in SAPUI5

$
0
0

Introduction -


Many times I saw questions on requirement to display smartform in SAPUI5.

Below are few threads on same requirement,

How to call smart from in sapui5?

Print Forms in SAP UI5

Open PDF retrieved via odata service in SAPUI5

Adobe form and SAPUI5

Showing pdf in sapui5 using a string.


Hence I thought to write blog on this subject and put focus on step-by-step procedure to display smartform in SAPUI5. I assume that many of us will be interested to know how to get the smartform (pdf) content on SAPUI5 by exposing the backend data in the form of Gateway (OData) service.


Let me explain all these details by taking simple example of standard smartform SF_EXAMPLE_03 (Smart Forms Training Example 3). Our objective will be to display the output of smartform SF_EXAMPLE_03 in SAPUI5 application in the form of PDF. We will have customer number as the input field and will get the flight details invoice as an PDF output. We will use iframe as container to display the PDF.



Procedure -

 

Very first, we will develop function module Z_TEST_PDF_DISPLAY which will take customer  number as input and will export URL pointing to PDF. You can get the logic of function module at sapui5-display_smartform_pdf/Z_TEST_PDF_DISPLAY at master · CmIm/sapui5-display_smartform_pdf · GitHub

 

P.S. Inside FM, I used the logic from thread Mime logo not displaying to build temporary URL for PDF.

 

Now we will develop GW service which will help us to expose this data. Let's create simple GW service ZTESTPDF as displayed below.
ui5_sf1.jpg

We will have entity as pdf and entity set as pdfset. generate runtime artifacts and then go to generated class ZCL_ZTESTPDF_DPC_EXT. Now Redefine method PDFSET_GET_ENTITY and put below code. You can also get below code at sapui5-display_smartform_pdf/PDFSET_GET_ENTITY at master · CmIm/sapui5-display_smartform_pdf · GitHub


DATA:   lt_keys     TYPE /iwbep/t_mgw_tech_pairs,   ls_key      TYPE /iwbep/s_mgw_tech_pair,   lv_customer TYPE s_customer,   lv_url      TYPE string.   lt_keys = io_tech_request_context->get_keys( ).   READ TABLE lt_keys WITH KEY name = 'CUSTOMER' INTO ls_key.   lv_customer = ls_key-value.   CALL FUNCTION 'Z_TEST_PDF_DISPLAY'     EXPORTING       i_customer = lv_customer     IMPORTING       e_url      = lv_url.   er_entity-customer = lv_customer.   er_entity-url = lv_url.

Now register your GW service and That's it! We are now ready to test our GW service. Just query with customer number and you should be able to get the output as highlighted below.

ui5_sf2.jpg


At this point, we are done with backend logic and GW service development. Now we will develop SAPUI5 application which will consume this service.


Create SAPUI5 project and put code in index.html.(follow MVC pattern as best practice!). You will get source code of index.html at sapui5-display_smartform_pdf/index.html at master · CmIm/sapui5-display_smartform_pdf · GitHub


End Result -


Now deploy your application to SAP ABAP server and run the application. provide any customer number (check if data is available in table SCUSTOM) and click on Display PDF button. You will see below result on screen. Here, we are displaying invoice details of customer number 3 in the form of smartform pdf.


ui5_sf3.jpg


Closing Remarks -


With the simple technique of creating temporary URL for the PDF and then exposing that URL through GW service, we are building SAPUI5 application to consume GW service. We are then setting src property of an iframe with this URL and setting this content to HTML UI element.


I hope with this simple way, you can display any backend smartform in SAPUI5 application !


I will request you to put the comment on this blog. Please feel free to put your comments/suggestions and any other way to display smartform in sapui5!


Happy Learning and Coding

New cocktail: SAPUI5 & Web Page Composer

$
0
0

SAPUI5 has been based on standard & common technologies (JS/HTML5/CSS3) that lets us to deploy / embed wherever we want ( almost ): mobile online or offline (e.g. mixing PhoneGap), NW Portal, ABAP, HTTP Server, Java WAS Server, ...

 

This blog explains How to visualize a Web Page Composer Form with SAPUI5 in SAP NW Portal 7.3.

 

First of all, I've followed this guide to create a new form: http://scn.sap.com/docs/DOC-22253

 

On step 4.1.2 - Define the presentation of the Web form, I've extended XSL style definition in order to load SAPUI5 libs and read XML contents into SAPUI5 components (labels, textviews, etc).

 

 

Web Form Definition

 

This file defines Web Form metadata structure.

 

<documenttype id="wpc_pc_new" description="SAPUI5 New" showpreview="false" showelementlist="false">  <properties>  <property id="fileName" description="xml.xlbl.filename" type="inputfield" size="25" isrequired="true" isfilename="true" />  <property id="date" description="xml.xlbl.date" type="dateinput" isrequired="true"/>  </properties>  <elements>  <element id="title" description="xml.xlbl.title" type="inputfield" size="50" default="true" isrequired="true" nodelete="true" singleinstance="true"/>  <element id="author" description="xml.xlbl.author" type="inputfield" size="50" default="true" isrequired="false" nodelete="true" singleinstance="true" />  <element id="description" description="xml.xlbl.description" type="inputfield" size="150" default="true" isrequired="true" nodelete="true" singleinstance="true"/>  <element id="image" description="xml.xlbl.image" type="imageselect" default="true" isrequired="false" nodelete="true" singleinstance="true"/>  </elements></documenttype>

Web Form Presentation

 

This file transforms XML content into an HTML page with SAPUI5. Previously I've deployed a SAPUI5 app com.sap.scn.demo with SAPUI5 libs (/com.sap.scn.demo/resources/sap-ui-core.js). If your portal has installed SAPUI5 addOn it is possible access directly to SAPUI5 libs on your portal.

 

<?xml version="1.0"?><!DOCTYPE stylesheet[<!ENTITY apos  "&#39;" ><!-- replace &apos; with html escape character for ' -->
]><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wpc="com.sap.nw.wpc.km.service.editor.xslt.XsltHelperCore">  <xsl:template match="/">  <html>  <head>  <!-- No cache, Force IE9 -->  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />  <meta http-equiv="Pragma" content="no-cache" />  <meta http-equiv="Expires" content="0" />  <meta http-equiv="X-UA-Compatible" content="IE=IE9"/>  <meta charset="UTF-8"/>                                      <-- Load SAPUI5 Libs -->  <script src="/com.sap.scn.demo/resources/sap-ui-core.js"  id="sap-ui-bootstrap"  data-sap-ui-libs='sap.ui.commons'  data-sap-ui-theme="sap_goldreflection">                  </script>  <script>  jQuery.sap.includeStyleSheet("/com.sap.scn.demo/css/custom.css");  jQuery.sap.includeStyleSheet("https://fonts.googleapis.com/css?family=Playfair+Display");  var mainLayout = new sap.ui.commons.layout.MatrixLayout({  layoutFixed : true,  columns : 1,  width : '100%',  height : '100%',  widths : ['100%'] });  var oLayout = new sap.ui.commons.layout.MatrixLayout({  layoutFixed : true,  columns : 2,  width : '100%',  height : '100%',  widths : ['200px', ''] });  var leftLayout = new sap.ui.commons.layout.VerticalLayout({width: '100%'});  var rightLayout = new sap.ui.commons.layout.VerticalLayout({width: '100%'});  var oRow = new sap.ui.commons.layout.MatrixLayoutRow();  var oCell = new sap.ui.commons.layout.MatrixLayoutCell({padding: sap.ui.commons.layout.Padding.Both, colSpan : 1, vAlign: sap.ui.commons.layout.VAlign.Top});  oCell.addContent(leftLayout);  oRow.addCell(oCell);  oCell = new sap.ui.commons.layout.MatrixLayoutCell({padding: sap.ui.commons.layout.Padding.Both, separation: sap.ui.commons.layout.Separation.SmallWithLine, colSpan : 1, vAlign: sap.ui.commons.layout.VAlign.Top});  oCell.addContent(rightLayout);  oRow.addCell(oCell);  oLayout.addRow(oRow);  $( document ).ready(function() {  var label;  var field;  var layoutField;  <!-- Left -->  var oImage = new sap.ui.commons.Image({width:'200px'});  <xsl:for-each select="document/elements/element">  <xsl:if test="@type='image'">  oImage = new sap.ui.commons.Image({width:'200px'});  oImage.setSrc('/irj/go/km/docs/<xsl:value-of select="current()"/>');  </xsl:if>  </xsl:for-each>  leftLayout.addContent(oImage);  leftLayout.addContent(new sap.ui.commons.layout.AbsoluteLayout({width: "1px", height: "10px"}));  <xsl:for-each select="document/elements/element">  <xsl:if test="@type='author'">  layoutField = new sap.ui.commons.layout.MatrixLayout({  layoutFixed : true,  columns : 2,  width : '100%',  widths : ['75px', '125px'] });  label = new sap.ui.commons.TextView();  label.setText('Author:');  field = new sap.ui.commons.TextView({design: sap.ui.commons.TextViewDesign.Italic});  field.setText('<xsl:value-of select="current()"/>');  layoutField.createRow(label, field);  leftLayout.addContent(layoutField);  leftLayout.addContent(new sap.ui.commons.layout.AbsoluteLayout({width: "1px", height: "10px"}));  </xsl:if>  </xsl:for-each>  <xsl:for-each select="document/properties/property">  <xsl:if test="@type='date'">  layoutField = new sap.ui.commons.layout.MatrixLayout({  layoutFixed : true,  columns : 2,  width : '100%',  widths : ['75px', '125px'] });  label = new sap.ui.commons.TextView();  label.setText('Date:');  field = new sap.ui.commons.TextView({design: sap.ui.commons.TextViewDesign.Italic});  field.setText('<xsl:value-of select="current()"/>');  layoutField.createRow(label, field);  leftLayout.addContent(layoutField);  leftLayout.addContent(new sap.ui.commons.layout.AbsoluteLayout({width: "1px", height: "10px"}));  </xsl:if>  </xsl:for-each>  <!-- Right -->  <xsl:for-each select="document/elements/element">  <xsl:if test="@type='title'">  field = new sap.ui.commons.TextView({  text : '<xsl:value-of select="current()"/>',  wrapping : false,  design: sap.ui.commons.TextViewDesign.H2  });  rightLayout.addContent(field);  </xsl:if>  </xsl:for-each>  <xsl:for-each select="document/elements/element">  <xsl:if test="@type='shortDescription'">  field = new sap.ui.commons.TextView({  text : '<xsl:value-of select="current()"/>',  design: sap.ui.commons.TextViewDesign.Italic,  wrapping : false  });  rightLayout.addContent(field);  </xsl:if>  </xsl:for-each>  mainLayout.createRow(oLayout);  mainLayout.placeAt('content');  });  </script>                            </head>  <body class="sapUiBody" role="application">  <div id="debugInfo"></div>  <div id="content"></div>  </body>  </html>  </xsl:template></xsl:stylesheet>

Result

 

webformsapui5.PNG

SAPUIfy all you can

Enjoy!

i181n Confusion, investigation and findings

$
0
0

Hi All, this posting started off as a simple(ish) question I was going to post as a discussion but as I went through building up my findings I ended up answering (some) of my own questions, so thought I'd share my findings for all to read and comment on...  It began with some simple queries around the usage of i18n resource bundles.

 

I'm building up a Shell based desktop app template and using it to work through various different controls, partly as a learning exercise and partly in response to this brilliant blog.

 

I've hit a bit of a funny with i18n resource bundles and how they are parsed at runtime, that I just can't get my head around.  I'm pretty sure there is a simple answer but I'll be ****** if I know what it is

 

In my index.html I have

<title>UI5 Desktop App - Boilerplate</title>

within the <HEAD> tag.  I'd like to change this to be replaced by something like

<title>"{i18n>APP.TITLE}"</title>

so that a translated title can come from my i18n.properties file.  I understand that this can't work in the index.html file as at that point, the i18n model has not been loaded.  I've tried performing a

document.title = "{i18n>APP.TITLE}"

in various places through my application:-

  • app.js - this doesn't work and that makes sense, as at this point I still haven't loaded the i18n model
  • Component.js - this doesn't work even if I perform the title update after I have loaded the i18n model as per the below code.  I'm aware that just doing a "document.title..." at this point probably isn't the best thing to do but I'm just playing around at the moment:-
jQuery.sap.declare("app.Component");
jQuery.sap.require("sap.ui.model.resource.ResourceModel");
sap.ui.core.UIComponent.extend("app.Component", {  createContent : function() {  // App View  var oView = sap.ui.jsview("app", "view.App");  // set i18n model  var i18nModel = new sap.ui.model.resource.ResourceModel({  bundleUrl : "i18n/i18n.properties"  });  oView.setModel(i18nModel, "i18n");  // over-ride window title?  document.title = "{i18n>APP.TITLE}";  return oView;  }
});

This gives me:

i18n Title.jpg

  • App.controller.js - adding it into the onInit() function or onAfterRendering() function or indeed any other function, again this doesn't work and gives me the same result as the screenshot above.  (Can you tell I'm clutching at straws here?!)

 

Moving on, in a similar vein I'm trying to add a link to an external webpage from a Button and add it as a headerItem in my Shell.  So I have code like this:

// define any header items, next to the logout button - Button, MenuButton or TextView are allowed  headerItems : [  // TextView that could show user name  new sap.ui.commons.TextView({  text : "User Name", // logic required to authenticate user and determine their name  tooltip : "{i18n>APP.USERNAME_TOOLTIP}"  }),  // Button to link to "your company's" website  new sap.ui.commons.Button({  text : "{i18n>APP.COMPANY_LINK_TEXT}",  tooltip : "{i18n>APP.COMPANY_LINK_TOOLTIP}",  press : function(oEvent) {  window.open("{i18n>APP.COMPANY_LINK_URL}");  }

Here, instead of opening a new window with "http://www.yourcompany.co.uk" (the value of APP.COMPANY_LINK_URL in my i18n.properties file) I get this:

NewURL.jpg

Again, it appears that the i18n translation hasn't occured, just like it doesn't if I try to use it in an alert("") function or by setting the document.title property as shown above.

 

I've also tried to do something like this:

// store company url in variable for later  var companyURL = "{i18n>APP.COMPANY_LINK_URL}";

<snip>

// Button to link to "your company's" website  new sap.ui.commons.Button({  text : "{i18n>APP.COMPANY_LINK_TEXT}",  tooltip : "{i18n>APP.COMPANY_LINK_TOOLTIP}",  press : function(oEvent) {  window.open(companyURL);  }

With similar results.

 

 

So, in a round-about manner I finally get to my question - is there somewhere I have missed that explains how and when the "i18n>" data binding translation works as I can't find it but am conscious it will just be hidden somewhere in the DemoKit and I've missed it!

 

I do actually have a working solution, that makes use of direct access to my resource bundle via code, rather than depending upon the data binding method.  So in my app.js file I now have:

sap.ui.localResources("view");
sap.ui.localResources("model");
sap.ui.localResources("app");
sap.ui.localResources("img");
jQuery.sap.require("app.Component");
jQuery.sap.require("jquery.sap.resources");
// get resource bundle for use where a straight translation from data
// binding doesn't appear to work
var oBundle = jQuery.sap.resources({  url : "i18n/i18n.properties"
});
// store company url in variable for later
var windowTitle = oBundle.getText("APP.TITLE");
document.title = windowTitle;
// create app Component and place at dom element with id = content
new sap.ui.core.ComponentContainer({  name : "app"
}).placeAt('content');

This ensures the title of my window comes from my i18n files and hence will be translated at run time.

 

For my company url, I've done similar in my App.view.js:

jQuery.sap.require("jquery.sap.resources");

<snip>

// get resource bundle for use where a straight translation from data  // binding doesn't appear to work  var oBundle = jQuery.sap.resources({  url : "i18n/i18n.properties"  });  // store company url in variable for later  var companyURL = oBundle.getText("APP.COMPANY_LINK_URL");

<snip>

// Button to link to "your company's" website  new sap.ui.commons.Button({  text : "{i18n>APP.COMPANY_LINK_TEXT}",  tooltip : "{i18n>APP.COMPANY_LINK_TOOLTIP}",  press : function(oEvent) {  window.open(companyURL);  }

This now also works as expected.  In summary, it appears that the data binding route to i18n texts doesn't work in all scenarios and I'll be honest in stating I don't know why.  At least with the above snippets of code I do have a solution that works, isn't particularly untidy and doesn't seem to add any overhead or performance issues.

 

Hope this is of use to others.

Test driven development for advanced SAP UI5 Controls

$
0
0

This blog post covers test driven development (TDD) for SAPUI5 Controls by means of QUnit. It focuses on testing of animated UI control with interaction build with third party library.

For more details concerning TDD please see following blog post engineering-in-javascript.

 

What is a SAP UI5 Control?

A SAPUI5 Control is “… an object that defines the appearance and behavior of a screen area” (wiki). In general SAPUI5 provides a lot of controls for building an application.

 

Why should I create one?

You can create your own control in case of standard controls cannot meet your requirements. You can extend an existing standard control or you create your own. This blog post focuses on creating a new control.

 

What is our example?

  • Donut chart (Pie chart with hole in the middle)
    • Contains a number (e.g.  5 Customers)
  • Number section in the middle of the donut chart
    • Contains either total number of all wedges or number for selected wedge
  • Interaction
    • User can click on wedge
    • User can move mouse over wedge
    • User can move mouse out
  • Events
    • Select event triggered when user clicks or moves the mouse over one wedge
    • Deselect event triggered when user moves the mouse out
  • Animation
    • Wedges are drawed with animation during
      initialization or update
  • Third party D3.js (Data-Driven-Documents) is used for drawing the donut chart

Donut_Chart.png

What is the challenge?

Animation

The main challenge in our example is animation. Each time our donut chart is drawed an animation is triggered that takes about one second until the chart is rendered completely. This happens during startup but also when an update is triggered. What if we need to check the total number of wedges rendered in the
Document Object Model (DOM) but the animation takes some time to come to an end? One short-term solution is to start the test until everything is rendered
(Listing 1). In the following code snippet our donut chart is initialized outside the QUnit module.


Listing 1 (Delayed test unit):

var oDonutChart = new sap.hpa.grcaud.chart.DonutChart({
 id: "donut"
});
oDonutChart.placeAt("content");
module("Donut Chart is initialized", {
 setup : function() {
 },
 teardown : function() {
 }
});
test("shall create one wedge", function() {
 var iNumberOfWedge = oDonutChart.getNumberOfWedge();
 equal(iNumberOfWedge, 1, "wedge number is wrong");
});

Unfortunately all furthermore tests need to work with same donut chart instance. Moreover global variables should be avoided. Therefore a better solution is to setup and teardown the donut chart in the module.

In listing 2 the instantiation and destruction of the donut chart moved to the module. In addition we need to make sure there is some timeout logic to draw the wedges (including animation) otherwise our test will fail. Without delay donut chart is not rendered completely.

 

 

Listing 2 (Timeout during module setup):

 

File: DonutChart.qunit.html

module("Donut Chart is initialized", {        setup : function() {            this.oDonutChart = new sap.hpa.grcaud.chart.DonutChart();            this.oDonutChart.placeAt("content");            stop();            setTimeout(function() {                start();            }, 1000);        },        teardown : function() {            this.oDonutChart.destroy();            this.oDonutChart = null;        }
});
test("shall create one wedge", function() {  var iNumberOfWedge = this.oDonutChart.getNumberOfWedge();  equal(iNumberOfWedge, 1, "wedge number is wrong");
});
. . .

File: DonutChart.js

getNumberOfWedge : function() {
var iWedge = jQuery(".sapHpaGrcaudFindingTileChart-part").length;  return iWedge;
}

Unfortunately this delay is inaccurate and could break when animation duration changes. As a consequence our test needs to wait until the donut chart rendering has finished. Listing 3 enforces an immediate update of the donut chart (SAPUI5 Wiki) by means of SAPUI5 sap.ui.getCore().applyChanges() function. It will cause the donut chart to render completely without any animation.

 

Please see attachments for final source code of DonutChart.js and DonutChart.qunit.html.


Listing 3 (Usage of apply changes):

 

File: DonutChart.qunit.html

module("Donut Chart is initialized", {  setup : function() {  this.oDonutChart = new sap.hpa.grcaud.chart.DonutChart();  this.oDonutChart.placeAt("content");  sap.ui.getCore().applyChanges();  },  teardown : function() {  this.oDonutChart.destroy();  this.oDonutChart = null;  }
});
test("shall create one wedge", function() {  var iNumberOfWedge = this.oDonutChart.getNumberOfWedge();  equal(iNumberOfWedge, 1, "wedge number is wrong");
});

Interaction

A secondary challenge is interaction. Our donut chart does support following interactions:

  • User can click on wedge
  • User can move mouse over wedge
  • User can move mouse out of wedge

 

When clicking or moving the mouse over a wedge the corresponding text (e.g. Wedge 1) and count (e.g. 1) is received via event (listing 4). Before we can test the result it is necessary to trigger the appropriate event. Due to the fact that we are using Scalable Vector Graphics (SVG) with D3.js this is done by means of a native browser event e.g. click. The corresponding SAPUI5 event trigger function sap.ui.test.qunit.triggerEvent() cannot be used because “there is no event handler attached using jQuery´s event system that corresponds to these events” (jQuery). Instead we create a new native event and dispatch it on the corresponding wedge. This also applies to move mouse over wedge and move mouse out of wedge tests.

 

Listing 4 (Interaction):

module("Donut Chart is created with five wedges", {  setup : function() {                  this.sWedge1Text = "";  this.sWedge1Count = "";  . . .  this.oDonutChart = new sap.hpa.grcaud.chart.DonutChart({                          wedges : [new sap.hpa.grcaud.chart.DonutChartWedge({  text : "Wedge 1",  count : 1  }), …  . . .  selected : [function(oEvent) {  this.sWedge1Text = oEvent.getParameter("text");  this.sWedge1Count = oEvent.getParameter("count");  }, this]                    . . .  });  . . .  },  clickOnWedge1 : function() {  this.triggerEventOnWedge1("click");  },  . . .  triggerEventOnWedge1: function(sEventType){  var oEvent = new Event(sEventType);  var oWedge1 = this.getWedge1();  oWedge1.dispatchEvent(oEvent);  },  getWedge1: function(){  return jQuery(".sapHpaGrcaudFindingTileChart-part")[0];  }  });  . . .  test("when user clicks on wedge 1 then receive information for wedge 1",  function() {  this.clickOnWedge1();  equal(this.sWedge1Text, "Wedge 1", "wedge 1 text wrong");  equal(this.sWedge1Count, "1", "wedge 1 count wrong");  });  . . .
});

How to create custom control from scratch

$
0
0

Why create a custom control

 

SAPUI5 offers several simple controls like TextField, Label... and really complex controls like ThingCollecion, Table, etc. All SAPUI5 controls are listed here:

https://sapui5.netweaver.ondemand.com/sdk/#content/Controls/index.html

 

It is not necessary create a custom control from scratch if:

 

 

sap.ui.commons.Button.extend("MyButton", { //inherit Button definition                                           metadata: {    events: {      "hover" : {}  //new event definition hover    }  },  //hover event handler  onmouseover : function(evt) {    this.fireHover();  },  renderer: {} //Standard renderer method is not overridden
});

 

When our requirements doesn't fit standard SAPUI5 controls and we have no choice we can create custom controls.

 

What is a control and how it works

 

A control defines its appearance and behavior. All SAPUI5 controls extend from sap.ui.core.Control. In the other hand sap.ui.core.Element are parts of Controls but without renderer method. For example a Menu (Control) has different MenuItem (Element) and Menu renders its MenuItems.

Main structure of controls:

 

  • properties. Allows define its appearance and behavior on initialization.
  • aggregations. Lets group controls, variables, etc. Lets define some kind of containers inside a control. For example sap.ui.table.Table has different aggregations like columns, rows, etc.
  • associations. Controls can be associated with others that are not part of them. For example if we want to render a collection with next/prev functionality we could develop a previousItem / nextItem associations.
  • events. Control events should be related to higher level events more than standard DOM events (click, mouseover, etc). For example if we develop a Control which renders many tabs, tabSelect could be an event when a tab is selected.
  • appearance. Definition of our control in screen area. Every control has a render method in order to be rendered in HTML code.

 

My requirement: Custom autocomplete field like Google Gmail recipients.

 

I need a control that lets us:

  • add/remove different values
  • find values with autocomplete function
  • see all added values

 

Example Google Gmail recipients field:

 

sh000.png

 

Step by step

 

1. Create new library AutoCompleteValueHolder in new package /control:

 

sh001.PNG

 

 

2. Define a basic template on AutoCompleteValueHolder.js in order to test if it works:

 

sap.ui.core.Control.extend("control.AutoCompleteValueHolder", {     metadata : {      properties: {},      aggregations: {}    },    init: function() {            },    renderer : {              render : function(oRm, oControl) {          oRm.write('Hello, this is a new control :)');        }    }
});

 

3. Load your control library in html file:

 

sap.ui.localResources('control');
jQuery.sap.require("control.AutoCompleteValueHolder");

 

4. Use your new control in a view:

 

 

var yourNewControl = new control.AutoCompleteValueHolder('yourNewControl');

 

sh002.PNG

 

 

 

5. Add custom properties and aggregations:

 

 

  metadata : {    properties: {      "codePropertyName": {type : "string", defaultValue: "code"}, //Define a model property representing an item code      "descriptionPropertyName": {type : "string", defaultValue: "description"}, //Define a model property representing an item description      "path": {type : "string", defaultValue: "/"}, //Define our model binding path      "model": {type : "any", defaultValue: new sap.ui.model.json.JSONModel()} //Define our model    },    aggregations: {      "_layout" : {type : "sap.ui.layout.HorizontalLayout", multiple : false, visibility: "hidden"} //Grouping of selected items and search text field    }  

 

 

 

6. Initialize control:

 

 

This method will be called when an AutoCompleteValueHolder is instantiated

 

 

init: function() {   //Creation of search autocomplete field  var searchField = new sap.ui.commons.AutoComplete(this.getId() + '-searchField',{    maxPopupItems: 5,    displaySecondaryValues: true,     change: function change(event) {      if (event.mParameters.selectedItem != null) { //If user selects a list item, a new item is added to _layout aggregation    //Every new item consist in a TextField and a Image        var newValueField = new sap.ui.commons.TextField({          width: '100px',          editable: false        });  //TextField shares model with other TextFields and Autocomplete. We select correct path (user selection)        newValueField.setModel(event.getSource().getModel());        newValueField.bindProperty("value", event.getSource().getParent().getParent().mProperties.descriptionPropertyName);        newValueField.bindElement(event.mParameters.selectedItem.oBindingContexts.undefined.sPath);         newValueField.addStyleClass('autoCompleteValueHolder_valueField');  //Custom style  //Image let's us delete an Item. Press event will destroy TextField and Image from _layout aggregation        var newValueImage = new sap.ui.commons.Image({          src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gIKEw06nF/eLQAAAb9JREFUKM9lkkFIFHEUxn//mf3TrJVOLjvu7phBBB0M3VyNTYg85CU6ZLfqmpAwRVBseqg1CI0OzZ47ePRkh9xQiAgWgqIOXUpNhHbBDGdFSxYX2m3+HZoNpe/yHt/73oP3vgfADHsxI8T/XCOZDuIkGHO9SWcMZKM2CnL+VMqZAGO3lgkw8rFDBe/+qMrbllsFsQhiNhZxy9kxlbetwmTQpANcgesnhy6MSF+iW9H0sw2vWtwf7u8aOHsvrCRmsvNI+e2H9Vl4rwOcgI/11dJBX5I+IJtQLU3nTCs6aDVH+L72lYVXr3NLlZ1Hb8DXp4A74C9Xay+P/9zaEeLXoFnXCdcV3nqR4ufFuw/LP7LP4fcUECrJENTqfAKqvo+/+o2atw2AsJqpKsVysOtmWxsCYAjkmXjkcVqoWx0b28xHTUBw3tuiZJm8U1puac3LPIUaALeP2c6Xnk5VAfXicEJdam3JXGw1M3MdtqqAWulLqlxvyvlnymUw3PZEYSHVpa4l4i4gADEcj7krfT3qSXu8MBCclXFNA+AqGDeP2je6dxkHyOnT/U437AMYF+Ivm5WhPW8wrGmMBIMaeBCI/wB5M5PywZXUzgAAAABJRU5ErkJggg==',          press: function(event){            var valueLayout = event.getSource().getParent();            var autoCompleteHolderLayout = event.getSource().getParent().getParent().getParent().mAggregations._layout;            autoCompleteHolderLayout.removeContent(valueLayout);          },          width: '12px'        });                                   newValueImage.addStyleClass('autoCompleteValueHolder_valueImage'); //Custom style  //Wrapping container for TextField and Image        var valueLayout = new sap.ui.layout.HorizontalLayout({content: [newValueField, newValueImage]});        valueLayout.addStyleClass('autoCompleteValueHolder_valueLayout');  //Insert wrapping layout into 0 position        event.getSource().getParent().getParent().mAggregations._layout.insertContent(valueLayout, 0);        var content = event.getSource().getParent().getParent().mAggregations._layout.getContent();  //Reset value from autocomplete search field        var search = content[content.length-1];        search.setValue('');      }    }  });  searchField.addStyleClass('autoCompleteValueHolder_search'); //Custom style  //_layout aggregation creation  var layout = new sap.ui.layout.HorizontalLayout(this.getId() + '-valuesLayout',{allowWrapping: true});   layout.addContent(searchField);  layout.addStyleClass('autoCompleteValueHolder_valuesLayout');  //Set _layout aggregation into our control  this.setAggregation("_layout", layout);
}

 

7. Control rendering:

 

 

This method will produce html code:

 

renderer : {  render : function(oRm, oControl) {    var layout = oControl.getAggregation("_layout");    layout.getContent()[0].setModel(oControl.getModel());    var template = new sap.ui.core.ListItem({      text: "{"+oControl.getDescriptionPropertyName()+"}",      additionalText: "{"+oControl.getCodePropertyName()+"}"    });    layout.getContent()[0].bindItems(oControl.getPath(), template);  oRm.write("<span");    oRm.writeControlData(oControl);    oRm.writeClasses();    oRm.write(">");    oRm.renderControl(layout); //Reuse standard HorizontalLayout render method.    oRm.write("</span>");  }
}

 

8. Custom methods

 

 

This custom methods lets us get selected items or clear all selected items:

 

 

getSelectedValues: function() {  var content = this.getAggregation("_layout").getContent();  var result = [];  if (content != null && content.length > 1) {    //Get all selected item into result    for (var i=0; i<content.length-1; i++) {      var model = content[i].getContent()[0].getModel();      var path = content[i].getContent()[0].getBindingContext().sPath;      result.push(model.getProperty(path));    }  }  return result;
},
clearSelectedValues: function() {  if (this.getAggregation("_layout").getContent() != null && this.getAggregation("_layout").getContent().length > 1) {    //Delete all selected items (SubLayouts containing TextField+Image) from _layout aggregation    while (this.getAggregation("_layout").getContent().length > 1) {    this.getAggregation("_layout").removeContent(0);  }  this.getAggregation("_layout").rerender(); //ReRenders _layout aggregation
}

 

 

9. Final result:

 

JS Bin - Collaborative JavaScript Debugging

sh003.PNG

 

sh004.PNG

 

 

 

Any suggestion or feedback will be welcome

Enjoy!


Useful sapui5 coding help meta information files

$
0
0

    When we write sapui5 code, we often need get the UI5 controls basic meta information: the property, aggregation, events, associations. It is not convenient in current Eclipse environment, as the normal process would be:

  1. Download the latest UI5 source code
  2. Create a specific project and import all the ui5 source files
  3. When need see one control/element information, switch from current project to the specific project, open the xx-dbg.js file, then scroll to the control definition part.

  (In Eclipse use the F3 'Open Declaration' will jump to the embedded ui5 library file, but which only contain the function prototype, don't have the meta data information).

 

    And another annoying thing is for the enumerableproperty:  if developer want to know the available enum property value, he needs search it from the big whole source files. And find out which one is the property definition and which one is the reference. In Chrome debug window we can a better way: just type the property type in console, then it will list all the property, but it is still not convenient as we need switch often between different windows.

 

      How to solve this problem? One solution is use the sapui5 itself to generate basic meta information file.

 

sapui5 Metadata 

    For each ui5 class, we can get the metadata by call getMetadata(), then from the metadata you can get all the information such as property, aggregation, event....

var meta = sap.m.Button.getMetadata() 

var prop = meta.getAllProperties(), it looks like: 

 

text: Object

    1. defaultValue: null
    2. group: "Misc"
    3. name: "text"
    4. type: "string"
  1. type: Object
    1. defaultValue: "Default"
    2. group: "Appearance"
    3. name: "type"
    4. type: "sap.m.ButtonType"

  2. Library information

  3.     By call  sap.ui.getCore().getLoadedLibraries(),  we can get the library information, how many library, all the controls/element/types for one library.

  4. Type information:

        If the property start by 'sap', then it may be a enumerable property. We can get the object by eval the string, then do the further check:

        if all the value by key is string, then it is enumerable property, otherwise, like the 'sap.ui.core.CSSSize' it is a special type.


    Class hierarchy information:

        call the getParent() we can get the parent of one class.  Then for all the class, we can easily build the hierarchy information.


    How to generate the metadata information:

      With the available ui5 meta information, it is easy to build the files. I use a famous template http://handlebarsjs.com/ to do so.

  5.   In order for easy use, I integrated it into the 'Fast UI5 tool', you can get more information from http://scn.sap.com/community/developer-center/front-end/blog/2013/12/18/fast-sapui5-develop-tool, it look like
  6. MetaTool.png
  7. Just choose the 'Development Assistant'-->'Meta Information', then can get you wanted file by click 'Generate Content', also you can customize the library, content and file options. The content look like:
  8. Note:
  9.   In order for user easy access, for all the enumerable property, i also generate the property candidate. And generate the parent/child information.

//extends from: sap.ui.core.Control

sap.m.Button = function() {

    var metadata = {

  properties : {

        "busy"              : { type: "boolean"            , defaultValue: false    },

        "busyIndicatorDelay": { type: "int"                , defaultValue: 1000      },

        "text"              : { type: "string"            , defaultValue: null      },

        "type"              : { type: "sap.m.ButtonType"  , defaultValue: "Default" },  // Default, Back, Accept, Reject, Transparent, Up, Unstyled, Emphasized

        "width"             : { type: "sap.ui.core.CSSSize", defaultValue: null      },

        "enabled"           : { type: "boolean"            , defaultValue: true      },

        "visible"           : { type: "boolean"            , defaultValue: true      },

        "icon"              : { type: "sap.ui.core.URI"    , defaultValue: null      },

        "iconFirst"         : { type: "boolean"            , defaultValue: true      },

        "activeIcon"        : { type: "sap.ui.core.URI"    , defaultValue: null      },

        "iconDensityAware"  : { type: "boolean"            , defaultValue: true      },

        },

 

 

  aggregations : {

            "tooltip"    : { type: "sap.ui.core.TooltipBase", multiple: false,  altTypes: "string"  },

            "customData" : { type: "sap.ui.core.CustomData" , multiple: true , singularName: "customData",  },

            "layoutData" : { type: "sap.ui.core.LayoutData" , multiple: false, singularName: "layoutData",  },

        },

 

 

  events: {

            "validationSuccess": {},

            "validationError"  : {},

            "parseError"       : {},

            "formatError"      : {},

            "tap"              : {},

            "press"            : {},

        }

 

 

    };

};

 

sap.ui.core.TextAlign = {

  Begin  : "Begin",

  End    : "End",

  Left   : "Left",

  Right  : "Right",

  Center : "Center",

};

  1. sapui5 1.18.0 version meta files:

  2.     I attached it in this blog, so you can import it into the ui5 project then see all the information easily just in one file.
  3.      The zip file available from https://dl.dropboxusercontent.com/u/250426883/ui5_meta.zip


Public SAP Mentor Monday 24 Mar 2014: UI5 with Andreas Kunz

$
0
0

On Monday 24 March 2014 we will have a public SAP Mentor Monday session on the subject of UI5.

 

For those of you who don't know, a public SAP Mentor Monday is an hour-long webinar format where everyone is invited and the subject is a specific topic. The subject of this public SAP Mentor Monday is UI5. That is to say, SAPUI5 and OpenUI5 - the licenced and open source versions both. UI5 is the toolkit that is powering the UI/UX revolution at SAP, and we have a special guest that will join us from the UI5 team in Walldorf - Andreas Kunz.

 

These are exciting times for SAP, and for me there's no place nearer the epicentre of the visible renewal than UI5.

 

Join the webinar to hear about and discuss UI5, with folk who share your interest. I'll be hosting it, and if you want to submit questions in advance, you can do so using this form.

 

Hope to see you there!

 

NOTE: Time to be confirmed, but is usually around 2000CET.

 

Link to SAP Connect Session: https://sap.na.pgiconnect.com/sapmm +1-866-312-7353

Participant Passcode:  378 224 4518

Country        Number  

US and Canada       1-866-312-7353

US and Canada       1-720-897-6637

US and Canada       1-646-434-0499

US and Canada       1-484-427-2544

Argentina     0800 444 1292 

Australia, Melbourne        +61 3 8687 0624

Australia, Sydney   +61 2 9009 0688

Australia       1 800 651 017  

Austria, Vienna       +43 1 2530 21750

Austria           0 800 006 088  

Bahrain, Manama  +973 1619 9392

Bahrain         8000 4811

Belgium, Brussels   +32 2 404 0657

Belgium         0800 39675

Botswana     002 698 003 001 802 

Brazil, Porto Alegre           +55 51 4063 8328

Brazil, Rio de Janeiro         +55 21 4063 5267

Brazil, Sao Paulo     +55 11 3163 0498

Bulgaria, Sofia         +359 2 491 7542

Bulgaria         00 800 118 4451

Canada, Montreal  +1 514 669 5883

Canada, Toronto    +1 416 915 3225

Canada          1 877 252 4916

Chile, Santiago        +56 2 599 4973

Chile   123 0020 6704 

China, Beijing          +86 10 5904 5002

China, Northern Region   10 800 650 0630

China, Southern Region   10 800 265 2601

China +400 120 0519 

Colombia      01 800 518 1236

Croatia          0800 222 228   

Cyprus, Nicosia       +357 2200 7933

Cyprus           800 964 63

Czech Republic, Prague    +420 228 882 890

Czech Republic        800 701 387      

Denmark, Copenhagen    +45 32 71 16 49

Denmark      80 701 624

Dominican Republic          1 888 751 4814

Estonia, Tallinn        +372 622 6444 

Estonia          8000 111 358   

Finland, Helsinki     +358 9 2310 1631

Finland          0 800 770 120  

France, Paris            +33 1 70 70 17 77

France           0800 946 522   

France           0811 657 737   

Germany, Frankfurt          +49 69 2222 10764    

Germany, Munich  +49 89 7104 24682    

Germany      0800 588 9331 

Greece, Athens       +30 21 1181 3805

Greece           00800 128 573 

Hong Kong   +852 3051 2732

Hong Kong   800 905 843      

Hungary, Budapest            +36 1 778 9215

Iceland          800 9901

India, Bangalore     +91 80 6127 5055

India, Delhi   +91 11 6641 1356

India, Mumbai        +91 22 6150 1743

India   000 800 1007 702

Indonesia      001 803 657 916

Ireland, Dublin        +353 1 247 6192

Ireland           1 800 937 869  

Ireland           1890 907 125   

Israel, Tel Aviv         +972 3 763 0750

Israel  1809 212 927   

Italy, Milan   +39 02 3600 9839

Italy, Rome   +39 06 4523 6623

Italy    800 145 988      

Japan, Osaka           +81 6 4560 2101

Japan, Tokyo           +81 3 4560 1261

Japan 0120 639 800   

Jordan           800 22813

Kazakhstan  8800 333 4239 

Latvia, Riga   +371 6778 2556

Latvia 8000 4247

Lithuania, Vilnius    +370 5205 5165

Lithuania       8800 31308

Luxembourg            +352 2487 1454

Luxembourg            800 27071

Malaysia,Kuala Lumpur    +60 3 7723 7221

Malaysia       1 800 806 547  

Malta 800 62208

Mauritius      802 033 0006   

Mexico, Mexico City          +52 55 1207 7362

Mexico          001 800 514 8609

Netherlands, Amsterdam            +31 20 716 8291

Netherlands 0800 265 8462 

New Zealand, Auckland   +64 9 929 1760

New Zealand           0800 885 018   

Norway, Oslo          +47 21 50 27 61

Norway         800 510 67

Oman            800 73655

Pakistan        008 009 004 4138

Panama         00 800 226 9817

Peru   0800 54 762      

Philippines    1 800 1651 0726

Poland, Warsaw     +48 22 212 0699

Poland           00 800 121 3995

Portugal, Lisbon     +351 21 781 0275

Portugal        800 784 425      

Puerto Rico  1 855 693 8763

Romania, Bucharest          +40 21 529 3917

Romania       0800 895 807   

Russia, Moscow      +7 495 213 17 63

Russia            810 800 2106 2012    

Saudi Arabia            800 844 4276   

Singapore     +65 6654 9828 

Singapore     800 186 5015   

Slovakia, Bratislava            +421 2 3300 2610

Slovakia        0800 001 825   

Slovenia, Ljubljana            +386 1 888 8261

Slovenia        0800 80923

South Africa,Johannesberg         +27 11 019 7009

South Africa 0800 984 011   

South Korea, Seoul            +82 2 3483 1901

South Korea 007 986 517 503

Spain, Barcelona    +34 93 800 0782

Spain, Madrid         +34 91 769 9443

Spain  800 600 279      

Sweden, Stockholm          +46 8 5033 6514

Sweden         0200 883 436   

Switzerland, Geneva         +41 22 592 7995

Switzerland, Zurich           +41 43 456 9248

Switzerland  0800 740 352   

Taiwan, Taipei         +886 2 2656 7307

Taiwan          00 806 651 935

Thailand        001 800 658 151

Turkey           00800 448 825 462    

UAE    8000 444 1726 

UK, Belfast    +44 28 9595 0013

UK, Edinburgh         +44 13 1460 1125

UK, London  +44 20 3364 5639

UK, Reading +44 11 8990 3053

UK       0800 368 0635 

UK       0845 351 2778 

Ukraine         0800 500 254   

Uruguay        0004 019 0509 

Venezuela    0 800 100 8510

Vietnam        120 651 66

SAPUI5 Apps with secure and user-friendly logon and updates

$
0
0

This is my first blog on SCN which is somewhat late since I have been working with SAP since 1995. I am the CEO of a company called Neptune Software. My co-founder Njål Stabell has pestered me to become a more active SCN member for a while now so I finally gave in. Here we go.

 

There are many things that are important if you wish to succeed with a successful mobile implementation, in this blog I will focus on two aspects that are paramount but often forgotten:

 

  1. Easy and secure access to the backend system
  2. Automatic update of installed apps

 

I have been directly or indirectly part of over 40 SAP mobile implementations in the last two years. I have seen that one of the greatest barriers for user adoption of SAP apps is the authentication process. Imagine yourself having to enter your e-mail address and password every time you log on to your Facebook or Twitter app. The usage of these services on your phone or tablet will obviously decline drastically. The same is true for enterprise applications. Entering sap username and complex passwords using the keypad on your device is far from user-friendly. Unfortunately, most companies retain this, non-mobile friendly process, when launching their first enterprise apps which results in lower usage and thus reduces the return on investment which was the real reason to mobilize their workforce in the first place.

 

This was the reason that we introduced a new functionality called AppCache in the latest release of Neptune Application Designer - 2.20. This feature enables you to package your Hybrid SAPUI5 applications with passcode encrypted credentials and also provides you with a way to automatically update applications on the devices from the ABAP backend.

 

The end user now only has to enter his sap user and password the first time he uses an app on a specific device.

 

image.png

 

The next step for the user is then to choose a passcode that will AES encrypt the credentials.

 

 

image.png

 

Now to log into the app, the user only needs to add his passcode. Also the user can select to lock his app or you can use Phonegap events like onPause to protect access even if the device is stolen.

 

image.png

 

The setup of this feature is pretty simple. Just copy the standard Neptune_AppCache application and set the following parameters in the AppCacheSetup ScriptCode element in the Neptune Designer:

 

  • AppCache.StartApp -> this is the launch page or menu page
  • AppCache.PasscodeApp -> you can keep the default but are free to change it if you want more complex encryption
  • AppCache.numPasscode -> a setting for how many attempts is allowed before the local content is wiped
  • AppCache.enablePasscode -> If you do not want this feature (You then have to type username and password every time but still get the updating feature)
  • AppCache.Client -> client number
  • AppCache.Url -> External accessible url for your NW system

 

 

Here is a screenshot of how it looks in the designer:

 

image.png

 

 

Another useful feature is the ability to have several users on the same device. This is especially relevant where users share devices such as in the warehouse or retail stores.

 

image.png

 

Regarding updating of hybrid apps that are installed on the end users devices, we know that this has been a challenge for many SAP customers. The use of MDM (Mobile Device Management) and MAM (Mobile Application Management) solutions such as Afaria are a great help but we still see difficulties.

 

Updating the backend (importing a transport to the productive SAP system) at the same time as you are distributing the updated application and users actually perform the updating of the app is often a cumbersome and new area of software logistics the typical SAP department do not wish to add.

 

So to smoothen this process we implemented a storage of apps in the AppCache using the local storage. Whenever the user logs on both the version of the app as well as the language is checked. If there is a new version (which is automatically updated when code is activated in the designer) or the user changes his language, the app is automatically updated.

 

Here is a screenshot of how it looks in the local storage:

 

image.png

 

 

We also added a monitor for admin usage, and here is a screenshot from an iPhone of where AppCache is used on our standard HCM template

 

image.png

 

I hope you got something out of this first blog I created for SCN. And even if you do not use Neptune the general concept should be of interest anyhow.

SAPUI5 Javascript and UniqueID definitions

$
0
0

Does the title of the blog look familiar? Many might think what is going to be different from the way things are working with Java scripts and its UI definitions and the answer is No - Nothing different !!!


The intention of this blog is to understand the idea of how .byID() and this.createID() works in Javascript development. Though, this is a basic one and everyone might be aware of how to use this in your code I am putting my thoughts out here so someone in future (a beginner / doubtful mind) like me could use it as reference.


References:


SCN ThreadsController - Cannot get reference by Id from View, view.byId issues with JS-based UI

Thanks to Andreas Kunz for his extensive support and knowledge on this regard.


https://sapui5.netweaver.ondemand.com/sdk/#docs/guide/MVC.html→ Support for Unique ID’s section.

 

Familiar document...That’s right when everything is available in the document why write a blog can be a question? The references provided are detailed and there is a chance that we miss out few things when we read a lot of stuff. May be I did

 

Let’s analyze this with a simple example. I have an input field with a default value and a button in view. I will try to access the input field value from controller’s init and my button action methods to see how and what type of syntax need to be used to read the input field value.


My View has the following structure initially (defining ID's statically):


var oName = new sap.ui.commons.TextField("name", {value: "Nagarajan"});
var oButton = new sap.ui.commons.Button("button", {text: "Get From Controller", press: oController.getName });  oName.placeAt("content");  oButton.placeAt("content");

Controller Methods Definition:

 

  onInit: function() {  var viewname = view.byId("name").getValue();  var corename = sap.ui.getCore().byId("name").getValue();  var thisname = this.byId("name").getValue();  },  getName: function(){  var viewname = view.byId("name").getValue();  var corename = sap.ui.getCore().byId("name").getValue();  var thisname = this.byId("name").getValue();  }

Run the application in Chrome to see the working / non working syntaxes from the Developer Tool.


Controller Init Method -


var viewname = view.byId("name").getValue();

Uncaught TypeError: Cannot call method 'byId' of undefined


Reason: This will try to prefix the view before the ID and view is undefined at this point in the global variables


sap.ui.getCore().byId("name").getValue(); - Works good.


var thisname = this.byId("name").getValue();

 

Uncaught TypeError: Cannot call method 'getValue' of undefined


Reason: Only if 'this' refers to the View


Controller on Button Action -


var viewname = view.byId("name").getValue();

Uncaught TypeError: Cannot call method 'byId' of undefined

Reason: We get the view reference in this method but still the prefix is not set.


sap.ui.getCore().byId("name").getValue(); - Works good.


var thisname = this.byId("name").getValue();

Uncaught TypeError: Object [object Object] has no method 'byId'

Reason: 'this' refers to the button and not the View.


When you define the static ID’s in Javascript the one that works is the sap.ui.getCore().byId("name").

Note: This may not hold true when there are multiple views inside your application that uses the same static ID. This is the reason the recommendation is to use the this.createID function when defining the UI in Javascript.


Now, lets change the UI ID’s with this.createID(). This is how the new code looks -


var oName = new sap.ui.commons.TextField(this.createId("name"), {value: "Nagarajan"});
var oButton = new sap.ui.commons.Button(this.createId("button"), {text: "Get From Controller", press: oController.getName });

Controller Init Method -


var viewname = view.byId("name").getValue();

Uncaught TypeError: Cannot call method 'byId' of undefined

 

Reason: This will try to prefix the view before the ID and view is undefined at this point.

Work around: get the view reference like var view = this.getView();


sap.ui.getCore().byId("name").getValue();

Uncaught TypeError: Cannot call method 'getValue' of undefined

Reason: Here there are no references to the View prefix so we get an error


var thisname = this.byId("name").getValue(); - works good as this get the reference from the controller (here the controller and View name are the same)


Controller on Button Action -


var viewname = view.byId("name").getValue(); - Works good as view reference is here


sap.ui.getCore().byId("name").getValue();

Uncaught TypeError: Cannot call method 'getValue' of undefined

Reason: Here there are no references to the View prefix so we get an error


var thisname = this.byId("name").getValue();

Uncaught TypeError: Object [object Object] has no method 'byId'

Reason: “this” refers to the button

Workaround:  Set the "this" context when registering the event handler like the below

var oButton = new sap.ui.commons.Button(this.createId("button"), {text: "Get From Controller", press: [oController.getName, oController] });

 

Other alternative, to access via sap.ui.core is sap.ui.getCore().byId(this.createId("name")) in all the places.


Hopefully, this covers the idea of how the pieces work together. This is my understanding of this functionality.

CRITICS are welcome to change my understanding on how these are put together to shape myself a better learner.




Eclipse ain't all bad - a few tips & tricks that might help

$
0
0

Intro.

I've used Eclipse in one form or another for a long, long time now.  Mostly in it's NDS/NWDS guise it has always had a certain number of "features" that can drive you round the bend.  Often, the easy way to fix many issues is by performing a simple re-start of the software itself - my colleagues often have me shouting at them if they come to me with an NWDS issue and haven't restarted it before asking for help

 

Anyway, I thought I'd add a few hints and tips that make my life easier with NWDS/Eclipse and could be useful as more and more people turn to it for UI5 development.  Really, for me it is all about simplicity when typing code and I've got into a nice workflow with how I use NWDS/Eclipse for editing Java/JavaScript type files.  Of course, like with many things YMMV...

 

As a certain global burger chain used to say, have it your way

If you've spent any time at all looking at the Windows->Preferences options of Eclipse/NWDS you will see there are lots of settings to meddle with.  Many are Eclipse centric but also, as you add other software such as SAPUI5, ABAP, HANA, CE, etc plus other random plugins you quickly get a lot of options to make the software work your way.  I'd recommend you take a decent chunk of time to work through all of the options and see what they can do.  Here's my starter for 10:-

 

  • Under General->Editors->Text Editors increase the "Undo history size" from 200 to 500.  This usually works even after a file has been saved and can get you out of disasters!

  • Under General->Keys put some effort into setting your personal shortcut keys.  As you use Eclipse more and more, you'll quickly get the benefit of being able to quickly use the keyboard to achieve common tasks and functions, instead of reaching for the mouse and navigating around menus.  One of my favourites to assign a simple shortcut (instead of the default) is the Refactor->Rename option:
    • In Java or JavaScript, click on a defined variable and Eclipse will highlight instances of it (here I've clicked on the first "hdrHTML" instance:
      Eclipse4.png
    • Now right-click on one of these instances and choose Refactor->Rename and you will see this:
      Eclipse5.png
    • You can now simply delete, change and update the object name and when you hit enter, all identified instances are changed as one - it's like an intelligent search and replace

  • Refactor->Extract Function(JavaScript)/Extract Method(Java)
    • This is a step on from the above rename option, which allows you to pull out code where you have say a complex single line and create sub-functions that are simpler to edit, debug and code.  Say I start with this pretty basic Java class:
      Eclipse6.png
      This is pretty odd code I've just written to prove a point, it makes no real logical sense!  The final else-if condition is a bit more complex than the others and could benefit from being a standalone method in it's own right.  So I select it, hit right-click and choose Refactor-Extract Method:
      Eclipse7.png
      I give the new method a name, choose visibility and set parameter names and hit the OK button (I can hit Preview > too if I want to see what Eclipse is going to try and do first but I find just doing it and relying on Undo is just as good )  I'm left with this new & improved code:
      Eclipse8.png
      Ok, in this particular scenario it isn't the greatest improvement but if you had a real-life example with more complex nested code, or you were editing a simple bit of code and it was quickly becoming more complex and difficult to read, this is a great way of modularising and tidying things up.


  • For both Java & JavaScript nodes:
    • Editor->Content Assist - choose "Insert best guessed arguments"/"Guess filled function arguments".  This means that the code complete will try to guess the correct object to insert when you use auto-complete, at the same time I also lower the Auto-Activation delay down to 10ms as I just prefer it to kick in almost instantly:
      Eclipse1.png
      This means I can type something like:
      String myStr = new String("Some text");
      myOtherObject.setStri
      When I then press CTRL-space, it will auto complete with the correct object name - a small thing but a great help when you are writing lots of code:
      myOtherObject.setString(myStr);
    • Editor->Save Actions - activate the "Perform the selected actions on save" option and as a minimum, I'd recommend you select to "Format source code" (you can change how this works elsewhere in the preferences.)  For Java I also perform an "Organize imports" - you can extend this function with other actions you want performed whenever you save a file.  With the increased undo buffer mentioned above, it allows you to quickly change, save and then undo code as needed
    • Editor->Typing - activate both "Semicolons" & "Braces" underneath "Automatically insert at correct position", which at first will feel a little strange as you can essentially hit the ; button anywhere in your line of code and it appears auto-magically at the end but once you are used to it, I feel it greatly increases speed when coding:
      Eclipse2.png

Aside from these couple of Preferences tweaks, given UI5 makes use of ResourceBundles as properties files, I remembered a plugin I used years ago to help out with translations in an SAP CRM ISA WebShop installation I worked on - you can download it from here.  It is a simple tool but for me, makes working with ResourceBundles that bit less painful as it gives a much nicer UI to handling the files.  Download the zip from Sourceforge, extract and copy the correct folder into the plugins folder within your Eclipse installation.  You are then able to right click on your "i18n.properties" file and Open With -> Resource Bundle Editor, which gives you this view:

Eclipse3.png

 

Wrap-up

Whilst I appreciate many will simply not like the complex nature of Eclipse and its many "features" I find with perseverance you can make it work in a really productive manner.  Sublime Text is another favourite tool but for me, NWDS supports CE, PO, BPM, BRM, WD, ABAP, etc. hence I'd prefer to put efforts into making it work as I want, instead of using different tools for different jobs.

 

One final tip - don't forget to use the File->Export->General->Preferences option to save your Eclipse pref's locally and apply them to any installations you use.

 

Hope some of this is useful.

My First SAPUI5 App, feeling excited!!!

$
0
0

Hi All,

 

Just happened to try some hands on SAPUI5. Created a simple SAPUI5 application which just fetches data from public OData service Northwind into a table.

 

The given application can be used as a starting point to anyone who wants to have a feel of the new framework.

 

After due diligence, setting up the development environment. Just launched myself onto creating the application. I am pasting the source code for cross reference.

 

 

index.html

**********************

 

 

<!DOCTYPE HTML>

<html>

  <head>

  <meta http-equiv="X-UA-Compatible" content="IE=edge">

 

 

  <script src="resources/sap-ui-core.js"

  id="sap-ui-bootstrap"

  data-sap-ui-libs="sap.ui.commons,sap.ui.table"

  data-sap-ui-theme="sap_goldreflection">

  </script>

  <!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required -->

 

 

  <script>

  sap.ui.localResources("test16feb");

  var view = sap.ui.view({id:"idTest16feb1", viewName:"test16feb.Test16feb", type:sap.ui.core.mvc.ViewType.JS});

  view.placeAt("content");

  </script>

 

 

  </head>

  <body class="sapUiBody" role="application">

  <div id="content"></div>

  </body>

</html>

*******************************

 

 

Controller.js

 

 

*****************************

 

 

sap.ui.controller("test16feb.Test16feb", {

 

 

/**

* Called when a controller is instantiated and its View controls (if available) are already created.

* Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.

* @memberOf test16feb.Test16feb

*/

onInit: function() {

 

   var oModelObject = new sap.ui.model.json.JSONModel(); 

      var url = "http://services.odata.org/Northwind/Northwind.svc/Order_Details_Extendeds?$format=json"; 

      oModelObject.loadData(url); 

      sap.ui.getCore().getElementById("testTbl").setModel(oModelObject); 

//

  },

 

 

/**

* Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered

* (NOT before the first rendering! onInit() is used for that one!).

* @memberOf test16feb.Test16feb

*/

// onBeforeRendering: function() {

//

// },

 

 

/**

* Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.

* This hook is the same one that SAPUI5 controls get after being rendered.

* @memberOf test16feb.Test16feb

*/

//onAfterRendering: function() {

//

 

  },

 

 

/**

* Called when the Controller is destroyed. Use this one to free resources and finalize activities.

* @memberOf test16feb.Test16feb

*/

// onExit: function() {

//

// }

 

 

});

 

 

 

 

***************************************

 

 

View.js

 

 

************************************

 

 

sap.ui.jsview("test16feb.Test16feb", {

 

 

  /** Specifies the Controller belonging to this View.

  * In the case that it is not implemented, or that "null" is returned, this View does not have a Controller.

  * @memberOf test16feb.Test16feb

  */

  getControllerName : function() {

  return "test16feb.Test16feb";

  },

 

 

  /** Is initially called once after the Controller has been instantiated. It is the place where the UI is constructed.

  * Since the Controller is given to this method, its event handlers can be attached right away.

  * @memberOf test16feb.Test16feb

  */

  createContent : function(oController) {

 

 

  // Create layout where UI control will be placed 

        var layout = new sap.ui.commons.layout.MatrixLayout('layout');    

        layout.setWidth('100%'); 

     

        // Create table UI 

        var oTable = new sap.ui.table.Table("testTbl"); 

        oTable.addColumn(new sap.ui.table.Column({   

        label: new sap.ui.commons.Label({text: "OrderID"}),     

                    template: new sap.ui.commons.TextView().bindProperty("text", "OrderID")  

        } 

            )); 

        oTable.addColumn(new sap.ui.table.Column({   

  label: new sap.ui.commons.Label({text: "ProductID"}),     

            template: new sap.ui.commons.TextView().bindProperty("text", "ProductID")  

  } 

     )); 

        oTable.addColumn(new sap.ui.table.Column({   

  label: new sap.ui.commons.Label({text: "ProductName"}),     

            template: new sap.ui.commons.TextView().bindProperty("text", "ProductName")  

  } 

        )); 

        // Data binding into table 

        oTable.bindRows("/value"); 

        oTable.invalidate(); 

        layout.createRow(oTable); 

        this.addContent(layout);      

         

 

  }

 

 

});

 

 

 

 

*******************************************

 

The result:

Mocking up the Payroll Control Center Fiori App

$
0
0

Following on from a great debate about Fiori and Freeori that stemmed from a post by John Appleby there were some comments about HCM app renewals. Latterly John Moypointed out a post "Improve payroll data validation with SAP Payroll control center add-on" where some very Fiori-like UIs were being shown.

 

Coffee Time

 

I thought it would be a nice little coffee-time exercise to try and reproduce one of the Fiori app pages shown in the screenshots in that post:

 

http://scn.sap.com/servlet/JiveServlet/downloadImage/38-101981-386999/620-326/Pic+1.png

So I did, and as I did it I recorded it to share. I thought I'd write a few notes here on what was covered, and there's a link to the video and the code at the end.

 

Developer tools

 


  • With that editor I'm using the SublimeUI5 package which gives me UI5 flavoured snippets and templates.

 

  • Specifically I started out with the "indexmspmvc" snippet (Index Mobile Single-Page MVC) which gives me everything I need to build MVC-based examples with XML views, controllers, and more ... all in a single page, a single file. Not recommended for productive use, but extremely useful for testing and demos.

 

In-line XML views

 

The XML views in this single-page MVC are defined in a special script tag

 

<script id="view1" type="sapui5/xmlview">    <mvc:View        controllerName="local.controller"        xmlns:mvc="sap.ui.core.mvc"        xmlns="sap.m">        ${6:<!-- Add your XML-based controls here -->}    </mvc:View></script>

and then picked up in the view instantiation with like this:

 

var oView = sap.ui.xmlview({    viewContent: jQuery('#view1').html()
})

Controls Used

 

This is a Fiori UI, so the controls used are from the sap.m library.

 

 

  • That Page's content is a single control, an IconTabBar.


 

 

  • For the info and infoState properties of the StandardTile I'm using a couple of custom formatters.


Video

 

 

 

Code

 

I have of course made the code available, in the sapui5bin repo on Github:

 

https://github.com/qmacro/sapui5bin/blob/master/SinglePageExamples/PayrollControlCenterMockup.html

 

Share and enjoy!


Getting started with OpenUI5 using WampServer

$
0
0

In this post we’ll set up OpenUI5 version 1.16.8 for use in WampServer and create a Hello-world OpenUI5 app. OpenUI5 can also be used with LAMP, MAMP and XAMPP servers in a similar fashion. Lets get started.

 

 

Installing WampServer

1)Download: Go to WampServer site at http://www.wampserver.com/en

  => Click "Start using WampServer"

  => Choose the appropriate package for your platform (e.g. Wampserver (32 bits & PHP 5.4) 2.4)

  => Download the installation file (e.g. Wampserver2.4-x86.exe).

wamp1.jpg

2)Install: To install the WampServer, simply run the downloaded installation file. Choose your installation directory. I shall assume that WampServer is installed in "C:\wamp".

wamp2.jpg

 

 

Starting WampServer

To start the WampServer, double click "WampServer" icon on your desktop. Or run "wampmanager.exe" from the WampServer installed directory. An icon will appear on the icon tray. "Green" icon indicates that all the services have started. "Red" indicates that all the services have stopped. Make sure that you are getting green tray icon as shown below.

  wamp3.jpg

 

 

Verifying server Installation

To verify WampServer installation:

=> Start a browser and issue URL http://localhost

You should be able to view server configuration as shown below.

my_wamp_localhost.jpg

 

Getting OpenUI5 SDK

Download: Visit the homepage for OpenUI5 http://sap.github.io/openui5/ on Github and download UI5 SDK Version 1.16.8-SNAPSHOT

wamp5.jpg

 

Configuring OpenUI5

1) Extract the downloaded file openui5-sdk-1.16.8-SNAPSHOT.zip to "openui5-sdk-1.16.8-SNAPSHOT" folder.

2) Place the "openui5-sdk-1.16.8-SNAPSHOT" folder with extracted content inside "www" folder of your WampServer.

3) Your file/folder structure should now look like this:

wamp6.jpg

4) From your browser hit the URL http://localhost/openui5-sdk-1.16.8-SNAPSHOT/ You should be able to view UI5 Overview page. You can browse through the UI5 documentation here.

wamp7.jpg

 

 

Writing a Hello-world program

1) Create a folder inside C:\wamp\www\ named "hello_ui5".

Copy the 'resources' folder from previous 'openui5-sdk-1.16.8-SNAPSHOT' folder into new "hello_ui5" folder and create the following hello.html file:

<!DOCTYPE html>

<html>

<head>

    <meta http-equiv="X-UA-Compatible" content="IE=edge" />

    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>

    <title>OpenUI5 using WAMPServer</title>

 

 

    <!-- 1.) Load OpenUI5, select theme and control library -->

    <script id="sap-ui-bootstrap"

        src="resources/sap-ui-core.js"

        data-sap-ui-theme="sap_bluecrystal"

        data-sap-ui-libs="sap.ui.commons"></script>

 

 

 

 

    <!-- 2.) Create a UI5 button and place it onto the page -->

    <script>

        // create the button instance

        var myButton = new sap.ui.commons.Button("btn");

 

 

        // set properties, e.g. the text (there is also a shorter way of setting several properties)

        myButton.setText("Hello World!");

 

 

        // attach an action to the button's "press" event (use jQuery to fade out the button)

        myButton.attachPress(function(){$("#btn").fadeOut()});

 

 

        // place the button into the HTML element defined below

        myButton.placeAt("uiArea");

 

 

        // an alternative, more jQuery-like notation for the same is:

        /*

        $(function(){

            $("#uiArea").sapui("Button", "btn", {

                text:"Hello World!",

                press:function(){$("#btn").fadeOut();}

            });

        });

        */

    </script>

</head>

 

 

<body class="sapUiBody">

 

 

    <!-- This is where you place the UI5 button -->

    <div id="uiArea"></div>

 

 

</body>

</html>

Your file/folder structure should now look like this:

wamp8.jpg

2) View the hello.html file in the browser using URL http://localhost/hello_ui5/hello.html and you see:

wamp9.jpg

 

 

I hope this post is helpful.

Thanks for reading.

Nested UI Routing in OpenUI5

$
0
0

Hi all,

 

I’m a big fan of the SAP javascript framework called OpenUI5. It’s a big change when you come from WDA but it has a lot more advantages. Besides SAP, there are other Javascript frameworks like AngularJS, BackboneJS, EmberJS, DurandalJS, … . Javascript frameworks are a growing trend in the web/mobile technology.

 

The idea

I’ve tested a lot of these frameworks. Most of the time I enjoyed working with OpenUI5 of course. One of the AngularJS plugins that I really like is the Angular UI-Router: https://github.com/angular-ui/ui-router

 

This plugin has a feature which can change multiple components in the view while navigating as you can see here: https://github.com/angular-ui/ui-router#multiple--named-views

 

The concept

For example, when surfing to your Javascripte website with the URL pattern #/home, different subviews/components will be loaded in different places (header, menu, content and footer). When you for example navigate to the “Company” page, you will see that the page stays the same. Only the different subviews/components will change.

 

pic1.png

 

This is in my opinion a really nice feature! Navigate and change multiple components. Some of the advantages:

  • Reusable components/views
  • One master page with the design
  • Separation of code logic

 

Standard OpenUI5

I thought this could be a nice feature for OpenUI5. Wouldn’t it be nice if you could navigate in OpenUI5 without changing the whole page but only some parts of it? Yes, indeed. I’ve been looking at the OpenUI5 SDK and I’ve found an OpenUI5 Router component .

 

Do you want to take a look? Open UI5 SDK: resources\sap\ui\core\routing.

 

That’s exactly what I was looking for! Of course this component was not working exactly the same as the AngularJS UI Router, so I did some changes.

 

I started analyzing the standard OpenUI5 Router component and found out following issues, to have the same feature as in AngularJS Routing you will need to change the following:

 

  • Only one view definition for an URL pattern possible
  • You can use an URL pattern only once
  • Views have to be created in a parent OpenUI5 component

 

So in every view you will have to add the required content when maybe you want some parts from the previous page…

 

pic2.png

An example of how to use a default OpenUI5 Router:

 

var shell = new sap.ui.ux3.Shell("shell", {  appTitle:'SAPUI5 Routing Example',  showLogout: false,  showLogoutButton: false,  showSearchTool: false,  showInspectorTool: false,  showFeederTool: false,
});
shell.placeAt('content');
var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_main",  view: "customsapui5.main",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "shell",  targetAggregation: "content",  clearTarget: true
},
{  pattern: "secondPage",  name: "_secondPage",  view: "customsapui5.secondView",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "shell",  targetAggregation: "content",  clearTarget: true
}]);
oRouter.register("appRouter");
oRouter.initialize();

You have to define a parent OpenUI5 component where the router component will place the views. This can be for example the shell component:

 

https://openui5.hana.ondemand.com/#test-resources/sap/ui/ux3/demokit/Shell.html

 

Or you could use layout components like HorizontalLayout:

 

https://openui5.hana.ondemand.com/#test-resources/sap/ui/layout/demokit/HorizontalLayout.html

 

Customizing OpenUI5

So what if you want to place your views directly in a DIV container? What if you want to use multiple DIV containers that have to change when you navigate to another page? Why the shell or another parent component?

 

The Shell component of OpenUI5 is nice, but I like to have some more freedom for creativity.

 

I’ve started by taking a copy of the OpenUI5 Router component which you can find under the folder:

 

resources\sap\ui\core\routing --> file: Router-dbg.js

 

In my OpenUI5 project I’ve created a folder “components”. I placed a copy of the “Router-dbg.js” in it and renamed it to “RouterCustom.js”.

 

With just one easy change I already had achieved my first goal, I just added changed the parameter “greedy” from “false” to with the value “true” and I was able to navigate multiple views:

 

pic3.png

The this._oRouter object is an instance of the thirdparty library crossroads. By adding the parameter greedy with value "true", it will function differently. By default this value is false in the crossroads object:

OpenUI5 SDK: resources\sap\ui\thirdparty --> crossroads-dbg.js

greedy.png

 

By using two DIV’s and a VerticalLayout Component for every DIV as parent I could use multiple parents. With following code I could change multiple components while navigating

 

var oLayout = new sap.ui.layout.VerticalLayout("oLayout");
oLayout.placeAt('header');
var oLayout2 = new sap.ui.layout.VerticalLayout("oLayout2");
oLayout2.placeAt('content');
var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_header1",  view: "customsapui5.header1",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "oLayout",  targetAggregation: "header",  clearTarget: true
},{  pattern: "",  name: "_content1",  view: "customsapui5.content1",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "shell",  targetAggregation: "content",  clearTarget: true
},
{  pattern: "secondPage",  name: "_header2",  view: "customsapui5.header2",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "oLayout",  targetAggregation: "content",  clearTarget: true
},
{  pattern: "secondPage",  name: "_content2",  view: "customsapui5.content2",  viewType: sap.ui.core.mvc.ViewType.HTML,  targetControl: "oLayout2",  targetAggregation: "content",  clearTarget: true
}]);

This is going good but still not exactly what I want. It would be better when you just have to define one pattern for multiple views and to add views directly to a DIV without the parent OpenUI5 component!


While looking deeper in the Router component I came in the “sap.ui.core.routing.Route” component.  Because this component changes the views I definitely needed this one. Took a copy of it and placed it in my OpenUI5 project under the folder components.

 

resources\sap\ui\core\routing --> file: Route-dbg.js

 

I also renamed it to “RouteCustom.js”

 

pic4.png

 

After some research I came to the conclusion that the most important part for the navigation is  in the method: “_routeMatched”

 

In this method I’ve added following code:

 

if(oConfig["views"]){  $.each(oConfig.views,function(key,value){  var sViewName = value.view;  if (value.viewPath) {  sViewName = value.viewPath + "." + sViewName;  }  oView = oRouter.getView(sViewName, value.viewType);  if(oView && oView["getController"] && oView.getController()["onBeforeShow"]){  oView.getController().onBeforeShow();  }  oView.placeAt(value.div,"only");  });
}else

 

With this code I check if there are multiple views for one pattern. If that’s the case I do the following:

  1. loop over the views
  2. get the view name
  3. get an instance of the view by using the Router object
  4. If the view has a method “onBeforeShow” go to that method
    1. This is something I have implemented to do some manipulations before showing the view. This could be handy in some cases, but is not required!
  5. Place the view in the div which you can now add to the configuration of the pattern
    1. The OpenUI5 standard logic of the Route component uses the following statement for placing the div: “oTargetControl[oAggregationInfo._sMutator](oView);” . With this statement you always needed a parent OpenUI5 component. In my code this is replaced by “oView.placeAt(value.div,"only");” so we just need a DIV as parent.

 

In the “RouterCustom.js” I’ve changed the reference to my custom route component.

 

pic5.png

 

Now I could use the Router just the way I wanted

 

var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_main",  views[{  view: "customsapui5.header1",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"header"  },{  view: "customsapui5.content1",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  }]
},
{  pattern: "secondPage",  name: "_secondPage",  views:[{  view: "customsapui5.header2",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"header"  },  {  view: "customsapui5.content2",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  }]
});

In my customized router component I can add multiple views to one pattern!

 

The full package

To get a better picture of how it works and what’s the benefits of this changes are, here an example of how to use.

 

In my example, I’ve created six views:

pic6.png

One view will be used for the navigation which always will be on the page. All the others I will use for content of the page.

 

I will create two pages:

 

First page will exist out of following views/components:

  • Header widget: TitleWidget
  • Left content: GeneralWidget
  • Right content: main

 

On the second page I will add the following views/components:

  • Header widget: TitleWidget
    • For the title we want to keep the same content as on the first page.
  • Left content: WeatherWidget
  • Right content: WeatherContent

 

To do this I coded the following in the index.html:

 

<script>  jQuery.sap.registerModulePath("sap.ui.core.samples.routing", "./");     jQuery.sap.registerModulePath('sap.custom.routing.Router', 'components/RouterCustom/');  jQuery.sap.require("sap.custom.routing.Router");
sap.ui.localResources("customsapui5");
var oRouter = new sap.ui.core.routing.Router([{  pattern: "",  name: "_main",  views:[{  view: "customsapui5.main",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  },  {  view: "customsapui5.GeneralWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"contentWidget"  },  {  view: "customsapui5.TitleWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"headerWidget"  }]  }  ,  {  pattern: "weather",  name: "_weather",  views:[{  view: "customsapui5.WeatherContent",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"content"  },  {  view: "customsapui5.WeatherWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"contentWidget"  },  {  view: "customsapui5.TitleWidget",  viewType: sap.ui.core.mvc.ViewType.HTML,  div:"headerWidget"  }]  }  ]);
oRouter.register("appRouter");  oRouter.initialize();
var oModel = new sap.ui.model.json.JSONModel({ title: "Nested UI Routing in SAPUI5" });  sap.ui.getCore().setModel(oModel);  jQuery(function() {sap.ui.template();});  var view = sap.ui.view({id:"idmenu1", viewName:"customsapui5.menu", type:sap.ui.core.mvc.ViewType.HTML});  view.placeAt("menu");</script>

In this code I start loading my customized components, create a router object , set title in my model and add the menu to the menu DIV.

 

In the index.html I also created my layout with the required DIV’s  for navigation:

<body class="sapUiBody" role="application">  <div id="overview" class="center">       <div id="header">            <div id="headerImage" class="left"><img src="images/sapui5.png"></div>            <div id="subheader" class="right">                 <div id="headerTitle" data-type="text/x-handlebars-template"><h1>{{text path="/title"}}</h1></div>                 <div id="headerWidget" ></div>            </div>       </div>       <div id="menu"></div>       <div id="main">            <div id="contentWidget" class="left"></div>            <div id="content" class="right"></div>       </div>  </div>  </body>

The navigation itself is being handled in the controller of the menu view. With the name of the route, which is defined in the router object, it will navigate to the second page and change all the different parts (as defined in the router object).

 

goToPage: function(oEvent){  var oRouter = sap.ui.core.routing.Router.getRouter("appRouter");  oRouter.navTo(oEvent.getParameter("item").getKey());  }

 

Because I use the "NavigationBar" OpenUI5 component for the menu and I use the same name for a route as for the key of "NavigationItem", the key is in the event.


Menu HTML View:

<div data-sap-ui-type="sap.ui.ux3.NavigationBar" data-select="goToPage">  <div data-sap-ui-type="sap.ui.ux3.NavigationItem" data-key="_main" data-text="User information" ></div>  <div data-sap-ui-type="sap.ui.ux3.NavigationItem" data-key="_weather" data-text="Weather information" ></div>  </div>


Result

For the layout I also added some basic CSS..

 

All the views contain some dummy content just for showing you the principal. In the view “main” I implemented the “onBeforeShow” method, just for showing.

 

pic7.png

User information page

pic8.png

Weather information page

pic9.png

The title on the index page and the titleWidget are both connected to the same model. Changing the title in the titleWidget will also change the title in the index page.

 

 

It looks like it’s a new page but actually there are just three components changed.

 

pic10.png

You can find the full example on github: lemaiwo/Nested-UI-Routing-OpenUI5 · GitHub

 

I also added the index and custom components of the router to the attachements

 

When working with this concept, you are able to do some CSS on the master page (index.html). Therefore you could use other UI frameworks as defined in the following blog:

http://scn.sap.com/community/developer-center/front-end/blog/2014/02/18/openui5-with-bootstrap-for-the-design

 

Hope it's useful!

 

Kind regards,

Wouter

Draggable Controls in UI5 !

$
0
0

Hi All,

 

As some were asking about draggable controls in UI5, instead of scratching the head to build draggable UI5 Control i have accomplished it with jQuery draggable and the use of native HTML in UI5

 

Here is the working example. Modified & better versions are welcomed

 

JS Bin - Collaborative JavaScript Debugging&lt;/title&gt; &lt;link rel=&quot;icon&quot; href=&quot;http://static.jsbin.…

 

// code

 

<!DOCTYPE html>

<html>

<head>

    <meta http-equiv='X-UA-Compatible' content='IE=edge' />

    <meta http-equiv='Content-Type' content='text/html;charset=UTF-8' />

    <title>Draggable in UI5</title>

    <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">

    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>

    <script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>

    <script id='sap-ui-bootstrap' type='text/javascript' src='https://sapui5.hana.ondemand.com/resources/sap-ui-core-nojQuery.js' data-sap-ui-theme='sap_bluecrystal' data-sap-ui-libs='sap.m'></script> // bootstrapping sap-ui-core-nojQuery.js

    </script>


    <script id="view1" type="sapui5/xmlview">

        < core: View

         controllerName = "my.own.controller"

        xmlns = "sap.m"

        xmlns: l = "sap.ui.layout"

        xmlns: core = "sap.ui.core"

        xmlns: html = "http://www.w3.org/1999/xhtml" >

         < Page title = "Sakthi"

        id = "page1" > < content > < Button text = "I'm Native UI5 button" > < /Button>

                <html:style>

                  .draggable { width: 250px; height: 50px; padding:0.1em; display:inline-block; }

              </html: style > < html: div id = "draggable"

        class = "ui-widget-content draggable" > < html: p > I 'm Draggable in UI5 using Native HTML !</html:p>

                    </html:div>

  </content>

  </Page>

  </core:View>

    </script>


    <script>

        sap.ui.controller("my.own.controller", {});

         // instantiate the View

        var myView = sap.ui.xmlview({

            viewContent: jQuery('#view1').html()

        });

        var app = new sap.m.App("myApp", {

            initialPage: "page1"

        });

        app.addPage(myView);

        app.placeAt("content");

    </script>


    <script>

        $(function () {

            debugger

            $("#__xmlview0--draggable").draggable(); // Make it Draggable for the id.( the id gets generated as __xmlview0--draggable)

        });

    </script>

</head>

 

 

<body class='sapUiBody'>

    <div id='content'></div>

</body>

 

</html>


Regards

Sakthivel

Fighting the corner for Eclipse (still)

$
0
0

Intro.

Following my last blog post that contained a few hints and tips on how to be more productive with Eclipse, I saw this video that DJ Adams posted over the weekend, along with this supporting blog post.  As mentioned in my comments, the thing that stands out the most to me is the speed and ease DJ gets a nice looking application up and running, mostly due to his familiarity with UI5 but also because of how he is working with Sublime Text.  I'd not seen the snippets and template shortcuts in action before but liked what I saw.

 

However, I'm a supporter of Eclipse for my SAP development, so after seeing how quickly DJ can get files and indeed applications up and running, I felt the gauntlet had been thrown down in terms of bringing similar templates and snippets to the world of Eclipse, for those brave/stupid* enough to use it as their IDE of choice.  I haven't historically made use of the snippets functions in Eclipse but felt with UI5 taking over the world, now might be the time to change my position...

* Delete as appropriate.

 

What have I done?

No, not a cry of despair (well, maybe just a little!) - prompted by my current meddling with the boilerplate that HP Seitz demonstrated in his blog posting recently, I've been working on a kind of Desktop version.  It's painful (as HP suggested it might be in the comments) trying to get a fully resolved template project for a desktop application as, by its very nature, it is much more complex than the cleaner sap.m based app's can be.  I'm on the verge of chucking it up to GitHub so other, more able coders, can tidy up my mess (and in the process have a good chuckle at my code )

 

Getting back on track, I've just spent 10mins creating a couple of Snippets for Eclipse to mirror the type of functionality DJ shows in his video.  I've uploaded them to GitHub (details to follow) so others can use & enhance them.  Over time, I'll try to build up some more templates and snippets, along with some baseline Eclipse preferences files that support some of the content I put in my last blog posting.  I hope more and more will contribute to this effort and share more snippets for Eclipse based UI5 work.

 

On top of this, I've saved a basic Eclipse preferences file that includes some of the settings I mentioned in the last posting.  Unfortunately, Eclipse Kepler doesn't allow you to export/import perspectives, which is a shame, however I'm now working with this SAPUI5 perspective I've set up to be a bit less cluttered than the standard perspectives:

SAPUI5 Perspective.jpg

 

DetTheDevilails

DescriptionURL
Root GitHub repositorywizbongre/EclipseUI5 · GitHub
snippets.xml containing Desktop & Mobile index.html files for UI5EclipseUI5/Snippets/snippets.xml at master · wizbongre/EclipseUI5 · GitHub
Preferences file (.epf) for baseline configuration from last blog postEclipseUI5/Preferences/Eclipse Kepler Preferences.epf at master · wizbongre/EclipseUI5 · GitHub

 

Summary

This is again only a starter for 10.  As mentioned, I hope more Eclipse fans add further useful snippets and templates to the repo on GitHub.  Hopefully DJ won't mind me using his GitHub repo for inspiration, as there is already some great and useful snippets in there for Sublime Text fans.

 

Also, as I mentioned in my last post, to get the most out of Eclipse you really need to invest time in customising it to work your way - as homework* from this posting, I'd like people to use the Window->Save Perspective As... and Window->Customise Perspective... options to have a play with how their Eclipse looks.  Remember, you can create a completely new perspective that brings in views from others, as well as adding/deleting menu and toolbars.

*There is no reward or incentive whatsover for this.


As usual, I hope people find this useful.  Please don't be shy with mocking adding to my GitHub repo.

OpenUI5 with bootstrap for the design

$
0
0

Hi,

 

This blog is an extension on my previous blog about Nested UI routing in OpenUI5.

 

Nested UI Routing in OpenUI5

 

There I talked about navigation between pages without changing the whole page but only some parts of it. One of the advantages of this concept is that it can have a master page, which is the index.html page. In the previous blog I've created this page by using HTML and CSS. You can't design this page with the OpenUI5 components when working with this concept. The OpenUI5 components are only available when they are attached to the different parts (DIVs) of the master page.

 

So we have one design in the master page (index.html) with multiple components/views in it. Besides HTML and CSS we could also use a front-end framework like bootstrap! With this framework you can easily create your design, as well for web as for mobile! You can even use it to create responsive designs:

 

http://getbootstrap.com/

 

 

By just including the bootstrap css, we are now able to use all the features of bootstrap.

 

<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">

Now I’m able to use bootstrap for my css. So I changed the body of my index page to the following:

 

<body class="sapUiBody" role="application">       <div class="container-fluid" >            <div class="row">                 <div class="col-md-8 col-md-offset-2">                      <div class="panel panel-default">                            <div class="panel-body">                                <div class="row">                                     <div id="headerImage" class="col-md-4"><img src="images/sapui5.png"></div>                                     <div id="subheader" class="col-md-8">                                          <div id="headerTitle" class="row" data-type="text/x-handlebars-template"><h1>{{text path="/title"}}</h1></div>                                          <div id="headerWidget" class="row"></div>                                     </div>                                </div>                                <div class="row"><hr /></div>                                <div id="menu" class="row text-center"></div>                                <div class="row"><hr /></div>                                <div id="main" class="row">                                     <div id="contentWidget" class="col-md-4"></div>                                     <div id="content" class="col-md-8"></div>                                </div>                              </div>                      </div>                 </div>              </div>       </div>  </body>

Which will result in the following output:

 

bootstrap1.png

bootstrap2.png

In my opinion, Bootstrap is really powerful and makes the life of a web developer easier. This is one of the reasons to use Nested UI Routing in OpenUI5.

 

It’s also a good example of using multiple frameworks/libraries together!

 

You can find the index.html in the attachment and the full project at github: lemaiwo/OpenUI5-with-BootstrapUI · GitHub

 

Hope your seeing the benefits of it!

 

Kind regards,

Wouter

Viewing all 789 articles
Browse latest View live




Latest Images