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

Starting with Test Driven Development in JavaScript

$
0
0

Are you scared of making changes to our code? Do you spent a lot of time debugging or maintaining your code? Are you new to JavaScript and do not know the language’s many pitfalls? Do you fear cross browser issues? Then you should start with Test Driven Development (TDD). Manual Testing of SAPUI5 applications is time consuming and error-prone. Since your application should run on a lot of different browsers, testing them all manually in detail is impossible. Kent Beck states that the goal of test-driven development is clean code that works. TDD not only provides fast feedback, but also improves the structure of the code.


There is already a lot of great content out there dealing with Unit Testing in JavaScript Environments. In general, you can and should apply these concepts also for developing SAPUI5 applications. Since it is always hard to start and find the most relevant information, this blog post aggregates some links to useful content:


So start now to get out of the mess.


Setup for Local Testing in Eclipse

$
0
0

Wouldn't it be nice to get instant feedback if your application still works after each change? Having the correct setup of your development environment, this can be easily achieved. With local testing, you get instant feedback after each change and can execute your unit test and your local test page before committing your changes to the repository. There are some pitfalls regarding local testing in Eclipse. This post provides an overview and links for more information.

 

What is OpenUI5 / SAPUI5 ?

$
0
0

A basic overview for anyone who has not heard of OpenUI5 / SAPUI5 before

 

Intro

 

So now this JavaScript library named "OpenUI5" (the Open-Source-licensed version of SAPUI5) has left its SAP niche and stepped into public. And you are web developer and wonder what to make of it.

The easiest thing would be to just ignore it. You know, it comes from that big business software company, so it hardly can be cool or useful. And there are plenty of other libraries, so why care?

Because you might be missing something. Seriously.

 

First of all, relax: UI5 is in no way limited to business software and its developers are a bunch of people like you who just happen to work for that company named SAP. And who happen to love Open Source, so they not only used LOTS of Open Source libraries when building UI5, but also fought for open sourcing UI5 itself.

 

 

So now what is OpenUI5 / SAPUI5?

 

It is a JavaScript UI library consisting of a feature-rich core and a really large number of UI controls which are organized in a handful of libraries.

 

By "feature-rich" I mean stuff like data binding and models for different data sources, an efficient engine for creating and updating the HTML of the controls, support for a Model-View-Controller concept, support for declarative UI construction and HTML templates, automatic loading of the appropriate language resources and many other features along these lines.

 

The controls (around 100 or 140 of them? Hard to count exactly...) range from simple Button controls to complex ones like the sap.m.SplitContainer (with a responsive master-detail behavior and animated page navigation) and provide support for accessibility, keyboard navigation, touch interaction, right-to-left languages etc. out of the box. Different visual themes are available and can be adapted by either modifying the CSS or using the Theme Designer (a WYSIWYG tool, which is currently not yet released as Open Source, though).

SAP is using this UI library for business applications, where standards like security and supportability are crucial, so protection against cross-site scripting and other attacks and a powerful error analysis/inspection tool is integrated. And for sure SAP is investing a lot into automated and manual tests, from unit tests to screenshot comparisons.

 

UI5 is based on jQuery, which of course can be directly used in UI5 applications, but there's a lot on top of it, so most of the time you might rather find yourself working with the controls.

Many other Open Source libraries are used in UI5 and come bundled with it, e.g. LESS, the famous CSS processor, or datajs, the OData library.

 

This blog entry is not going to be a programming introduction (I might well write one soon), but if you want to see more code and more about the usage of UI5, you will find links with plenty of resources at the end of this blog entry.

 

 

A bit of History

 

Even though UI5 is only now stepping out into the Open Source space, it has in fact been developed and matured for years (initially under the codename "Phoenix", hence the bird-like logo). So don't expect a beta.

 

SAP has been using UI5 in a couple of products already, most notably the family of "Fiori" applications revealed earlier this year (see https://experience.sap.com/fiori for information and screenshots). Decoupling UI development (where customers expect frequent iterations to follow web UI trends and to get support for the newest devices) from backend updates (where typical SAP customers expect to run the same version for many years without expensive and disruptive upgrades) has been a major driving force behind the development and adoption of UI5. As well as the desire for a UI technology which is more standards-based, flexible and extensible than before and at the same time still fulfills all the stability, accessibility and security standards required from business software.

 

If you do a Google Image Search on "sapui5", you will get an impression of SAP's software using UI5 as well as prototypes and applications of customers/partners.

 

 

Why is UI5 Open Source now?

 

One reason was there from the very beginning of UI5, when it was still called "Phoenix": we always felt it should be Open Source. It just felt right and in line with the basic principles of UI5 which were all about openness and standards. But as you can imagine, in big companies and considering all the legal implications, open-sourcing something is not an easy task, so among all the pressure for features this topic got pushed back again and again.

However, with the growing popularity of SAPUI5 within the SAP developer community demand to open it up has increased (e.g. Jan Penninkhof and others have compiled a list of good reasons to open-source UI5).

And we also felt it was time to do it before it was too late - UI5 will profit when more people are using it. Be it due to feedback, bug reports, contributions, or simply because there is a bigger pool of UI5-aware developers in the world who can help each other.

 

 

How and why is "OpenUI5" different from "SAPUI5"?

 

"OpenUI5" is the free version available under the Apache 2.0 Open Source license.

"SAPUI5" is the version that may only be used by SAP customers with a certain kind of license. (It's still free for them, but they have paid some other SAP product.)

The good news is: most importantly, the entire core of "OpenUI5" and "SAPUI5" (containing all central functionality) is identical. The most-commonly used control libraries, containing the vast majority of all existing controls, are identical. All of this can now be used freely, when using OpenUI5.

The difference is in some more exotic control libraries which have not (or not yet) been put under an Open Source license. There are several reasons, including legal issues, organizational clarifications (developed in different parts of SAP) - and after all SAP is still a company which has to pay its employees (thanks! ;-)), so it may choose not to give everything away for free. You find the same pattern with other Open Source libraries and products...

But anyway, the vast mayority of all things UI5, including all things which have been developed close to the core of it are already free.

 

This was the technical perspective. Beyond that, there is a difference regarding support: for SAP customers with that certain sort of license the "commercial version" SAPUI5 comes with support agreements, so SAP guarantees bugfixing (don't ask me for the terms in detail...). These support guarantees are naturally not coming with the completely free Open Source version of UI5. But still, it is not a fork or so: OpenUI5 also gets the bugfixes done for SAPUI5. It's the same thing, just a subset. A big one.

 

 

How open is OpenUI5?

 

Very simple:

  • It is now available under an Open Source license. So you can use it without any payment to do more or less whatever you want. (So it is "free as in 'free beer'".)
  • A contribution process has not been set up yet and the sources are not yet available on Github to fork and modify. Of course you can read all the source code in the downloaded runtime files - the debug version is not obfuscated in any way - and you can modify your copy of it. But these runtime sources have been processed from the raw sources to a format which is more efficient to be used by browsers. So there is a build/packaging step involved and we need to make this step easier and document it properly before you can fork and modify and build UI5 on your own. This is definitely planned. (So arguably it is also "free as in 'freedom'" - but currently like a somewhat complicated version of it).

 

 

So what should I use OpenUI5 for? And for what shouldn't I?

 

When you want to put a clickable button on an otherwise entirely static web page, or when you want to layout a couple of HTML elements on the screen, using UI5 (or any other similar JS library, for that matter) would probably be too much overhead.

But when you want to access JSON or XML data from a server, present it nicely with a rich set of controls, when you want an interactive app to inspect or edit data, when you want users to be able to sort and filter, when you want all these things to happen in a solid and proven way, then you have good reasons to use UI5.

Especially when you want this to happen on all kinds of platforms, from mobile phones to big desktop PC screens.

 

Comparing OpenUI5 to other JavaScript UI toolkits is a huge task... I'll leave it for later. It is definitely more feature-rich - sure: and bigger - than low-level toolkits like jQuery or slim UI toolkits like Bootstrap. There are advantages and disadvantages on both sides which have to be considered case by case.

 

 

And now?

 

What I would suggest is:

  • go to the OpenUI5 web page and explore it from there.
  • if you are into code, study the small mobile app (not in IE9 and lower due to cross-domain issues) and try to understand how it works. Should not be too hard, actually, it's just 1-2 screens of code but already giving you master-detail page animations, data binding etc.
  • if you rather want to try it right away, check out the demo apps
  • if you prefer understanding the concepts first, start with the Developer Guide which describes loads of things from the first steps to really advanced topics

 

 

Be open for new things!

We are open for feedback and comments.

Item index in aggregation template

$
0
0

We would like to have the index of aggregated element in the template generation. There are a lot of reasons someone needs the index of the aggregation item.

  1. Display the sequence of the item
  2. Display a control when it is the first, last, even or odd template
  3. Pass it as a parameter to an event
  4. Name an object according to the index

 

As an example we would like to use the index of aggregation in the name of a TextField

 

_addressEditFormElementTemplate: function(oController) {


     return new sap.ui.commons.form.FormElement(this.createId('fldAddress'), {

            label: new sap.ui.commons.Label(this.createId('lblAddress'), {

                text: 'Address'

                }

            }),

            fields: [

                new sap.ui.commons.TextField(this.createId('inpAddress'), {

                    value: '{address}',

                    name: 'addresses[${index}].address'

                })

          ]

     });

}

 

Is it possible now?

Engineering in JavaScript

$
0
0

Having talked to many developers, we found out that there is a huge demand for topics around engineering of SAPUI5 applications. The dynamic nature of JavaScript make the language very powerful, but it is also challenging to build maintainable products with huge codebases. To counteract the tendency to messy codebases, we have to establish conventions and apply good engineering techniques. This blog post shall act as an overview about the different topics like testability, test driven development, local testing, continuous integration, agile software engineering, tools, setup of development environments, and clean code.

 

We are a group of developers, architects, and agile software engineering coaches going to publish a series of blog posts around these topics. So stay tuned, share and bookmark this page. We will update when a new blog is published.

 

Setup of Development Environment:

 

Testability:

Using XML Schemas when working with UI5 XML Views

$
0
0

There has been much Twitter discussion in recent time about using XML Views when building UI5 apps. Having experienced }})}]]})))])); hell using JS Views myself I was keen to try this approach - it appeared to be more concise than its JS counterpart, and arguably easier to read (I do vaguely remember that XML was designed with human readability in mind even though it never really achieved this). As an integration guy, working with XML appealed, and assuming that the XML can be validated by an associated schema (XSD), most XML editors should provide intellisense and on the fly validation. This all seemed like good stuff!

 

Personally I use the Oxygen XML suite for XML work, but in this case Eclipse will be the tool of choice. Its XML validation is not too bad - it's not as good as Oxygen, but it will do for our purposes.

 

Before we get into the details, I just want to say that this approach is not perfect, and there are still some subtle issues (which I will discuss later). Hopefully someone can take what I've done so far and work out how to fix these

 

So, how do we get started?

 

First, you'll need the XSD's for the UI5 libraries. You can find these by looking inside the libraries folder inside the JAR file for each UI5 library. For my example I'm only using the core and commons libraries, so i'll need these 2 XSD's to begin. Copy the 2 files and store them in a location of your choice on your computer.

 

XSD Libraries.png

 

Figure 1. sap.ui.commons.xsd is found in the commons jar file.

 

If you open the XSD's in an XML aware editor and validate the schemas you'll see immediately that these XSD's are not syntactically correct (version 1.14.6).

 

The errors I found were:

 

  1. Core has an invalid Regex defined for _URIType. You can either correct this or just comment out the pattern definition.
  2. Core has a type reference to _anyType which doesn't exist in the schema. This type is used to define a value attribute in the _CustomDataType definition. To correct this, either remove the attribute definition or define _anyType in this schema.
  3. Commons has the type _OrientationType defined twice in the schema. You'll need to remove one of these to make the schema valid. I kept the one with the lowercase horizontal and vertical enumerations as these seemed to correlate with some of the JS examples I have seen.


Now that the XML schemas are valid we can use these in Eclipse to help us build and validate our XML Views.

 

The easiest way to do this is to add the schemas to the Eclipse XML catalog. This can be done by opening the Eclipse preferences window (Eclipse->Preferences...) and then selecting XML->XML Catalog. Use the add button to load the 2 schemas into the XML catalog. You should notice that the key will automatically be set to the value of the schema namespace. When you have loaded the 2 schemas, click OK and return to the editor.

 

XML Catalog.png

Figure 2. Adding the UI5 XSD files to the XML Catalog in Eclipse.

 

You're now ready to try it out. Start by creating a new UI5 application with an initial view of type XML.

Go into the View and between the View tags type "<Panel " (notice the space after the l) and then press control-space. You should something like the image below.

 

Panel Example.png

Figure 3. With the XSD files available, attributes and element information is provided by the IDE.

 

So what happens if you just press control-space without typing "<Panel "? Based on the schema definition and your location in the View definition, the editor will propose what the possible elements are that can be used. When you do this you'll start to see the limitations of the Eclipse XML editor. For example, when I click control-space immediately after the View element, I only see options from the Core namespace, but really I should also see all of the controls from the Commons namespace as these are also valid here. Try the same thing in Oxygen XML and you will get the result you expect.

 

View_Eclipse.png

Figure 4a. Possible child elements suggested by Eclipse.

 

 

View_Oxygen.png

Figure 4b. Possible child elements suggested by Oxygen XML.


Why does this happen? We'll I can't say exactly, but my guess would be that the Eclipse XSD processor doesn't understand the schemas correctly. The schemas used to define the UI5 libraries are using a concept called "Substitution Groups" to implement Polymorphism - The View XML expects the next element to be a Control (you can see this in the possible options), but doesn't understand that Button, Accordion, TextField etc. are also controls. Unfortunately this appears to be a limitation in Eclipse (does anyone know a fix??), but overall it's not a huge issue. As seen earlier, if you type the control name, the schema does identify it and provides additional help. What it does mean is that your view will most likely never be 100% valid (based on the schema definition), due to the validation looking for a Control and not understanding that Button is a valid substitute. Of course once you're inside a concrete Control element (say DropdownBox), the intellisense will automagically provide the valid child elements and attributes (such as items, ListItem etc), as well as any documentation provided for the control.

 

I initially started using this approach to build a form. The form controls are in the Commons namespace. Typing <Form into the editor will start the intellisense, and from then on the correct child elements are offered by the editor - FormContainer, FormElement etc. But there is a problem. Try running a form built this way and you get an error saying that sap.ui.commons.Form.js doesn't exist. If you look in the Commons Jar file, you can see that the form elements are in a sub-folder called 'form' (obviously) . What I did to correct this issue was to extract the object definitions that reside in the /form folder from the commons schema into a new schema with the namespace sap.ui.commons.form. By using this new schema the form library is correctly identified as sap.ui.commons.form.Form.js.

 

Form_1.png

Figure 5. The IDE provides only valid child elements for <formElements>.

 

Form_2.png

Figure 6. Once inside a <formElement> all Controls are available.

 

Anyway, that's it. Its not perfect, but it's easier than guessing from the API guide what should go where and in what order. You can achieve a fully validated schema by using a better XML editor, and then copy/pasting to Eclipse, but unlike Java, the XML validation errors will not stop the View being rendered, so as long as you understand why the error is there I believe this approach is absolutely usable.

 

Enjoy.

Excel -like custom grid control in SAPUI5 #OpenUI5

$
0
0

In this blog I want to share my experience about how to create a custom #SAPUI5 control extending sap.ui.core.HTML standard control.

My goal is to create a new control to display an 'Excel-like data grid' where the user can edit/merge/change/delete rows and cells with an "Excel-like" look & feel.

 

Thanks to this tweet Twitter / alavazzi: jQuery plugin Handsontable, ... by Alessandro Lavazzi   I discovered some days ago an awesome jQuery plugin called Handsontable (http://handsontable.com/) : but how can I use it in my #SAPUI5 application? How can I use it as a control inside a SAPUI5 view?

How can I bind the data model using the SAPUI5 binding mechanism?

 

Exploring the official documentation ( https://sapui5.hana.ondemand.com/sdk/#docs/guide/OnTheFlyControlDefinition.html ), we know It is possible to extend existing Controls and to create completely new Controls: so I decided to create my ExcelGrid custom control extending the sap.ui.core.HTML control

 

excelgrid.js


sap.ui.core.HTML.extend("ExcelGrid",    {    metadata:    {        properties :        {            "options":"object",            "data":"object",            "content": {type : "string", defaultValue : "<div></div>"},            "preferDOM": {type : "boolean", defaultValue : false}        }    },    init: function(){              this.attachAfterRendering(function(e){                                var constructorOptions = this.getOptions();                      constructorOptions.data = this.getData();                                  this.$().handsontable(constructorOptions);                   });                },            getInstance: function(){        return this.$().handsontable('getInstance');    },                renderer: "sap.ui.core.HTMLRenderer"    });

Properties Definition

The first step is the properties definition for the new control, the documentation tell us :

  • a property is defined by at least its name and its type
  • setter and getter methods are created behind the scenes , including data binding and type validation (this is very important , we'll be able to bind values of properties using #SAPUI5 binding concepts)

 

I defined 2 properties of type [object]: 'options' and 'data' .

 

The handsontable constructor options has a lot of properties (https://github.com/warpech/jquery-handsontable/wiki/Options#constructor-options) , I don't want to manually define all properties so we'll use the generic "options" object to set the properties.

In "options" object I can set  width , height , headers , contextMenu (used to create new rows/columns) and so on.

The "data" property is very important because we'll bind the property to the model to display our array of arrays to the Excel Grid

 

Methods

from the official doc:

"You can add any method to a new Control by providing the implementation, without adding it to the metadata. By convention, all methods are public"

"There are some method names you may use but which have a special meaning:

  • on...: Methods starting with "on" are event handlers that are automatically bound to browser events
  • init: Is the name of the initialization function called right after Control instantiation
  • renderer: Is a special name that holds either
    • the function that creates the Control's HTML or
    • a complete structure that contains this function and more"

 

I redefine the "init" method because I need to handling the "afterRendering" event : here the effective grid rendering is done , I have the jquery instance of my empty rendered div ("<div></div>" as default value of the "content " property ) and executing  "handsontable" method the div is filled with the magic provided by the jQuery plugin.  You may have noticed I used the get Methods to retrieve  my "options" and "data" control properties  , I didn't implement them because as we said setter and getter methods are created behind the scenes.

 

(Note: I tried to implement the onAfterRendering Method instead of attachAfterRendering in "init" method but with no luck..)

 

The getInstance method is an helper method , it returns the jquery instance of the grid : we'll be useful to call specific plugin methods.

 

The Renderer method: from the offical doc

This method is responsible for creating the HTML structure that makes up the Control

I want to use the sap.ui.core.HTML standard renderer so my renderer is "sap.ui.core.HTMLRenderer".

 

 

Controller - main.controller.js

sap.ui.controller("excelgrid.main", {
/**
* 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 excelgrid.main
*/    onInit: function() {         var data = {"data":[["HANA", "HCP", "SAPUI5", "ABAP", "Mobile", "OData"],                             ["SQL Views", "JDBC", "open source!", "Gateway", "SMP", "REST"],                             ["Attributes Views", "Document Service", "sap.m", "Bsp", "Phonegap", "Entity"],                             ["Analytic Views", "CMIS", "FIORI", "", "Afaria", "$filter"],                             ["Calculation Views", "XS", "MVC", "", "Html5", "$skip"]]};               var oModel = new sap.ui.model.json.JSONModel();        // set the data for the model        oModel.setData(data);             // set the model to the core        //sap.ui.getCore().setModel(oModel);             //Control-Specific Model        //var oExcel = sap.ui.getCore().byId("excelgrid");        //oExcel.setModel(oModel);             this.getView().setModel(oModel);    },
});

I create a JSON model with dummy values and set the model for the current view.

 

 

View - main.view.js

sap.ui.jsview("excelgrid.main", {    /** 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 excelgrid.main    */    getControllerName : function() {        return "excelgrid.main";    },    /** 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 excelgrid.main    */    createContent : function(oController) {             //create my Excel Grid control        var oExcelGrid = new ExcelGrid("grid", {            data : "{/data}",  //binding the model            options: {minSpareRows: 1,colHeaders: true,contextMenu: true,manualColumnResize:true}        });             //create the ApplicationHeader control        var oAppHeader = new sap.ui.commons.ApplicationHeader("appHeader");        //configure the branding area        oAppHeader.setLogoSrc("http://www.sap.com/global/images/SAPLogo.gif");        oAppHeader.setLogoText("SAPUI5 'Excel-like data grid' Control ");        //configure the welcome area        oAppHeader.setDisplayWelcome(false);        //configure the log off area        oAppHeader.setDisplayLogoff(false);             var oLayout = new sap.ui.commons.layout.MatrixLayout({            id : "matrix1",            layoutFixed : false,            width: "100%"            });             var oButtonModel = new sap.ui.commons.Button({            text : "alert( oExcelGrid.getModel().getJSON())",            tooltip : "Alert grid model",            press : function() {alert( oExcelGrid.getModel().getJSON());}        });        var oButtonMethod = new sap.ui.commons.Button({            text : "Call method countRows()",            tooltip : "Test Call Method",            press : function() {alert( oExcelGrid.getInstance().countRows() );}        });        oLayout.createRow(oAppHeader);         oLayout.createRow(oExcelGrid);         oLayout.createRow(oButtonModel);         oLayout.createRow(oButtonMethod);                 return oLayout;    }
});

With  var oExcelGrid = new ExcelGrid I create my custom instance control defining the data binding for my model view and some properties to define the look and feel of the grid (options property).

Check the official documentation of the plugin to discovery all the options  Options · warpech/jquery-handsontable Wiki · GitHub

 

The "oButtonModel" writes the model using the javascript alert and the oButtonMethod writes the result of the method "countRows".

To call the method we use the getInstance() method of the ExcelGrid control , it returns the handsontable instance that we can use it to call the countRows() ; check all the available methods here! Methods · warpech/jquery-handsontable Wiki · GitHub

 

and finally....the index.html

<!DOCTYPE HTML><html>    <head>        <meta http-equiv="X-UA-Compatible" content="IE=edge">        <script src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"                id="sap-ui-bootstrap"                data-sap-ui-libs="sap.ui.commons"                data-sap-ui-theme="sap_bluecrystal">        </script>        <!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required -->       <script src="http://handsontable.com/dist/jquery.handsontable.full.js"></script>        <script src="js/excelgrid.js"></script>        <link href="http://handsontable.com/dist/jquery.handsontable.full.css" media="screen" rel="stylesheet">        <script>                sap.ui.localResources("excelgrid");                                 var view = sap.ui.view({id:"idmain1", viewName:"excelgrid.main", type:sap.ui.core.mvc.ViewType.JS});                view.placeAt("content");        </script>    </head>    <body class="sapUiBody" role="application">        <div id="content"></div>    </body></html>

 

Now we are ready to execute the web application!ExcelGrid1.JPG

We can edit a cell

ExcelGrid2.JPG

ExcelGrid3.JPG and writes the Model

ExcelGrid4.JPG

Insert a new row using the contextual menu

ExcelGrid5.JPG

ExcelGrid6.JPGand call a method to check the count

ExcelGrid7.JPG

 

If you want to try it how it works I uploaded the project here! goo.gl/dxMlqI

 

In the next blog I'll try to describe how to handle grid events

Unit versus Integration-Tests in JavaScript

$
0
0

The main focus of unit tests is to focus on small units in isolation. But only focusing on the small units does not touch all the code. So this blog post clarifies the difference between unit and integration tests.

 

Unit Tests should run fast, since the tests should be executed after each change and provide instant feedback. So unit tests should not contain too much DOM manipulation, reliance on timed behavior or contain any network activity. 


Therefore, unit tests should focus on the smallest piece of functionality and be decoupled from unreliable or slow dependencies. With unit tests you focus on a specific responsibility, e.g. formatting, error handling, data transformations or validations. In the last blog post, I showed how to start with Test Driven Development. Such kinds of unit tests are not specific to SAPUI5.


There are different kinds of Integration Tests: One kind of integration tests is a so called Smoke or System Test that tests the complete stack including the backend. Then there are Integration Tests without dependencies to fragile components. Non-isolated tests are fragile and result often in a lot of maintenance effort. This blog post series first concentrates on unit tests and isolated integration tests with QUnit. You can test your application against the SAPUI5 view and isolate your application, so that no synchronization with the backend is done. Compared to unit tests, you do not isolate from the DOM and the tests are, therefore, slower. With such kind of tests, you test the integration of the single modules, the event handling, user interaction, styling, cross-browser issues or binding of an application.


Compared to writing unit tests for single modules, such kinds of integration tests require more knowledge about the inner workings of SAPUI5 application, asynchrony and JavaScript in general. Within the next weeks, we are going to publish several blogs dealing with these topics.


So, first start with TDD for small modules and stay tuned (make a bookmark to or like the overview blog post). In the next blog posts I propose some patterns how to increase the testability for integration tests.




This blog post is part of a series, see  overview blog post:

http://scn.sap.com/community/developer-center/front-end/blog/2013/12/12/engineering-in-javascript




Speed Up Mobile / OData Development with AppBuilder

$
0
0

Whilst patiently waiting for SAP to deliver the highly anticipated AppDesigner for RAPID SAP UI5 development, SAP have released and pimped Mobile Development Tool which appears to be a sideways evolution of the previously titled Sybase Power builder.

 

APP BUILDER IS HERE !

 

Screen Shot 2013-12-09 at 12.49.07.png

 

Only1 Hour to install, learn and build my first application with SAP AppBuilder - A UI5 Mobile App showing users from our Gateway System in a SuperList

 

The once Sybase product has now been greatly updated to run as a UI5 Drag and Drop generation tool, with built in runtime preview.

 

The initial thing you notice when creating your first APP is that you are given two options, Phone or Tablet. Currently AppBuilder doesn't support desktop application modelling in UI5, this is what we expect in much greater detail from the AppDesigner.

 

Although if you wish to use app builder to generate all the code to integrate with your OData services, then move it into Eclipse????? thats probably not a bad idea.

 

Featuringall the standard fields / buttons and attributes that you expect in a Mobile builder like this and providing seamless integration with OData services, AppBuilder quickly allows you tomodel and test a mobile application. (A Pre Sales Dream!)

 

But this tool isn't just a gimmick for Pre Sales, it has genuine merit as a rapid development tool.

 

Once you have created your application and tested it with the browser runtime tool, it doesn’t end there.  You are given the option to export your application to either an iPhone application (this was slightly tricker than expected but it does work) or even better, to a Cordova application which could then be deployed as an app for all platforms!

 

Getting up and running with AppBuilder couldn't be easier, download it from here - SAP Development Tools for Eclipse

and then why not try this great tutorial from Ming Kho - AppBuilder Tutorial - Hello World App

 

No JavaScript, OData or HTML skills are required, but if you dohave those skills you will quickly appreciatethe ease of creating applications hooking into an SAP system in no time!

 

Get Building

Hiding the SAP UI5 file uploader behind an icon

$
0
0

Why

In one of our cloud portal admin pages I was asked to add a file upload option.

The request was to display a small icon which will trigger the browser file upload dialog.

Since the page is all built in UI5 I obviously used the standard UI5 FileUploader.

When trying to style the control as requested I found out that it is not as easy as it sounds.

 

For several reasons, browsers are not quick to modify the upload file control (<input type="file"> tag) that is normally rendered as an edit field and a browse button depending on the browser family.

When you want the edit field or the button or both of them to look differently, you need to be a little creative.

 

 

How I did it

There could be other ways, and there probably are many, but I believe that the way I eventually implemented this is both easy and light weight and pretty much frees you of the above mentioned constrains of the browser.

Most importantly, I used no hacks or external libraries.

Disclosure– I have not yet tested this solution on all available browsers but for late version market leaders it works.

 

And to the point, what I did is, after adding the FileUploader and hiding it in plain sight, I created a UI5 label, styled it to my desire and used the "labelFor" attribute to trigger the upload action.

 

Here are the snips:

  1. Add the FileUploader to your layout and give it an ID and any other attributes if relevant.

    var myFileUploader = new sap.ui.commons.FileUploader({
         id:'myFileUploaderID',
         name:'files',
         uploadOnChange: true
    })

  2. Hide your uploader.
    It is not just setting the display tag because you want to make it invisible but also un-clickable.
    For this purpose I used 2 simple CSS rules:

    #myFileUploaderID {
         height:0px;
         overflow:hidden;
    }


    No need for anything else.
    If you don’t set both these attributes, or something similar to that affect, you will get unwanted behavior like clicking in an empty area opens the upload dialog.

     

  3. Add the Label to your layout,you.

    var myFileUploadLabel = new sap.ui.commons.Label({
         id:'myFileUploadLabelID',

         labelFor:'myFileUploaderID-fu',
         icon:'myIcon.png',
         tooltip:'My Custom Upload Label'

    })

     

    And now here is the trick, notice that in the "labelFor" attribute, I put the ID I used for the FileUploader followed by "–fu".

    If you drill down into the FileUploader you will see that this is the automatically generated ID for its <input type="file"> tag.

 

After all this I added some UI5 classes to make it look like a regular button with an icon but of course you can do whatever suits your design:

 

upload1.png

 

That's it. Simple and easy as promised.
Have fun.

Yaron.

 

Relevant links:

Label

FileUploader

How to improve the testability of a SAPUI5 app

$
0
0

How can I get a better testability of an SAPUI5 application? The essential precondition for efficient unit testing, a more adaptable application and better code is to master the handling of dependencies. For more background information about the general principles of Dependency Management, see a blog post by Martin Fowler: http://martinfowler.com/articles/injection.html

and for more information about Test Doubles, see a blog post by Gerard Meczaros:

http://xunitpatterns.com/Using%20Test%20Doubles.html.

 

In the following, four techniques are shown how to inject test doubles: View Constructor Injection, Controller Setter, Overwriting global Objects and Adapter.

 

First, the view constructor injection allows injecting an object into the view with the parameter viewData. This allows replacing objects, which are already needed before the controller is instantiated. This could, e.g., be the case for the resource bundle. In the following example, a Test Double of the Shell is created and injected using the view parameter viewData.

 

setup injects Test Double via viewData Parameter

    var assignmentPage = sap.ui.view({

      viewName : "ViewName" ,

      type : sap.ui.core.mvc.ViewType.JS,

      viewData : oTestDoubleFactory.createShellTestDouble()

    });

 

Second, the controller setter allows injecting the object into the controller with a setter method.

 

setup injects TestDouble in Controller

this.oAlertActionAdapter = oTestDoubleFactory.createAlertActionAdapterTestDouble();

this.oController = assignmentPage.getController();

this.oController.setAlertActionAdapter(this.oAlertActionAdapter);

 

Third, a global object like OData can be overwritten. However, this technique is not recommended, because it could break other tests, which depend on the original state of the object. Therefore, the test should put the original object into a variable and afterwards revert to the original state of the object. But avoid this technique. You should avoid globale state and singletons as far as possible and design for good test isolation from the beginning. Otherwise it takes later a lot of effort to get the overall application testable.


     setup saves original state and injects TestDouble

             this.odata_request_original = OData; //remember original state

          OData = oTestDoubleFactory.createODataTestDouble();   

     teardown reverts to original state

          OData = this.odata_request_original; //revert to original state

    

 

Fourth, an alternative is the wrapper or adapter pattern combined with the parameter injection. This avoids the disadvantages of overwriting global objects. AlertActionAdapter, for instance, encapsulates all OData batch requests with regards to alerts. So the controller calls the AlertActionAdapter method assignAlerts instead of building and firing OData Batch Request. In the test, an instance of OData Test Double can be created and passed into the wrapper method assignAlerts with parameter injection.

 

     setup create TestDouble

this.oModelTestDouble = oTestDoubleFactory.createODataModelTestDouble();

    

     test function calls assignAlerts with TestDouble

        this.oAlertActionAdapter.assignAlerts(this.oModelTestDouble,this.aAlerts,"MEI");

  

     productive implementation of assignAlerts

      this.assignAlerts =

          function(oModel, aAlertIDs, sUserName, fnSuccess, fnError) {

            varaBatchOperations = [];

            for ( var i = 0; i < aAlertIDs.length; i++) {

              aBatchOperations.push(oModel.createBatchOperation(

               "/SetResponsiblePerson?AlertID='" + aAlertIDs[i].alertID +

   "'&ResponsiblePersonID='" + sUserName + "'", 'GET' ));

    }

    oModel.addBatchReadOperations( aBatchOperations);

    oModel.submitBatch( function(oData, oResponse, aErrorResponse) {

fnSuccess(aErrorResponse);

}, function(oError) {

fnError(oError);

    }, false);

};

Fast SAPUI5 Develop tool

$
0
0

When we develop Fiori application ( other SAPUI5 development also similar), we meet following challenges:

  1. We need write View using XML format but without good tool, it is boring and easy make mistake (such as tag not matching...)
  2. SAPUI5 have so many properties, it is hard to remember all of them. Especially for some enum property, we first need find out the enum define then know how to set it
  3. Data binding syntax is complex, we need write like "{parts:[{path:'birthday/day'},{path:'birthday/month'},{path:'birthday/year'}],  formatter:'my.globalFormatter'}"
  4. It is hard to study and understand other's complex view
  5. After do some modification, we can't see result immediate. It is hard to test on different device
  6. Wave 1 using HTML View, Wave 2 use the XML view, so we need change the format manually.

In order to make our life easy, recently I spent some effort developed one tool named "Fast Fiori Designer" which can help you

  1. Design View easily:  Just select controller/property/aggregation/event/assocation from list, ( so the Enumable property can select from a ComboBox
  2. Easily reuse others view by copy/paste
  3. Preview it immediately, can choose preview in iPhone / iPad Full Screen / iPad Master page / iPad Slave page model.  Also provide the 'Fast Model' means even you have so many event/data bining, you can see result immediate without the controller.
  4. Generate Controller/XMLView source file,  for the data binding also generate the sample demo data
  5. Convert from HTML format  to XML formate batch.

Want to try it? Just open http://10.58.67.2:8080/FastDesign/WebContent/index.html  by Chrome Browser (Note: It is based on sapui5 1.16, only inside SAP can access this url, i will try to make it work for outside soon).  It is very easy use. Here i just list some screen snapshot for reference.

Coming soon new features ( After new version come out, i will update this blog):

  1. Generate JavaScript code also.
  2. Smart data generate ( Help to create sample test data)
  3. Export preview result to picture
  4. Edit property when Preview
  5. Provide common view/design as template
  6. Project level open/save

Any comments/feedback/found some bug, please feel free contact Lucky li ( email: lucky.li01@sap.com)

 

 

Design a view

design-view.png

 

Generate XMLView

xml-view.png

Generate the Controller (include the event binding/ formatter/ default data binding)

controller.png

 

Preview, also provide the navigation tree in the left part, so you can easily see what control is what.  And can easily see result under different screen size.

preview.png

 

 

Want to know UI5 class Hierarchy?  From "Development Assist"-->"UI5 Class Hierarchy", you can see it

tree.png

When work for one control, want to see the corresponding document? In the view designer, just click the link, it will open the corresponding document.

jump-to-doc.png

OpenUI5 or AngularJS? How about both?

$
0
0

SAPUI5 has just been open sourced as OpenUI5 and provides a lot of features.  AngularJS is a great javascript library that can also add a lot of power to your application.

Let's compare some of the features of the two frameworks.  Here’s two input fields beside some text. They both look similar.

here_is_some_text_both.gif

Let’s look a bit behind the scenes at how to code them. First in UI5 based on the OpenUI5 sap sdk example:1.png

Something similar in Angular can be done directly in the html:

2.png

There are similar templating options in OpenUI5 so we can do something almost the same without livebinding by using handlebars:

handlebars.png

Along with just a bit of model binding in javascript (see the live examples).

handlebars-js.png

Wouldn’t it be nice if we could use Angular model binding with OpenUI5 controls? What if we could also include OpenUI5 controls such as the datepicker  directly in our html using some custom tags?

Imagine we could type the following:

3.png

to produce an OpenUI5 datepicker and then just use the curly braces to show the information somewhere else:

datepicker.png

Angular can help us do this with its concept of directives. Directives are a way to extend html new tricks and is also how we can use Angular models to bind data. Once we have the directive we can re-use it very easily in our html.

First is the Angular controller. In the controller the model is plain old javascript:

4.png

Then we create an Angular directive and initialize the datepicker. The main steps are:

  1. Angular setup to tell it to link to an element and use the model passed into the element
  2. Setup the OpenUI5 datepicker and initialize with the model and some attribute tags
  3. Setup the binding on the change of the datepicker
  4. Setup the binding on the Angular change of a model

 

5.png

All that’s left is to add the datepicker to our html. We’ve even added a custom attribute to set the locale as shown for the start date.

6.png

This will give us:

datepicker_example.gif

 

Checkout the live example on jsbin. In the jsbin example I’ve also added a filter to format the date. Be sure to open in Chrome or Firefox since the cdn version of OpenUI5 doesn't work well with IE.

While there is a bit of code for the setup of the control and the directive, we gain some nice things:

  • the controller doesn’t need to know about the ids of the html. Placeat is done with a tag. Wherever the tag is placed, the new control is created.
  • binding can be done in the html view just by having the same ng-model

I hope this shows some ways that Angular can be used along with OpenUI5. Now you don’t have to choose between the two, you can use both!

How to test against SAPUI5 views in a non-fragile way

$
0
0

A common issue of many UI tests is their fragility. The tests have often to be changed when the underlying structure of the UI has changed. So, how can we get rid of this fragility? We need a more abstract API for testing. There are three techniques: Semantic IDs, getter functions or Page Object Pattern.

 

First, with semantic IDs you create a unique identifier for each element you want to access with a test. This allows an easy access of the element. Disadvantages are that you have to ensure the uniqueness of the ID, and the ID is a kind of implementation detail, and the JavaScript code should be agnostic to them. In the following example, the uniqueness of an element for a list of persons is ensured by creating an ID with the username, which is unique for each element of the list.

 

In the productive implementation the element investigatorPhoto is created with an ID. Therefore, this.createId( "assignInvestigatorPhoto_" + uniqueID) creates a unique ID for the element.

 

var investigatorPhoto = new sap.m.Image(this.createId( "assignInvestigatorPhoto_" + username),...

 

In the element is accessed via the unique ID.

  this.getElement = function(vID) {

       return sap.ui.getCore().byId(assignInvestigatorListView.createId(vID));

  };

  test(

      "Should sent Request When alerts selected and user clicks on photo,

      function() {

        var assignIcon = this.getElement("assignInvestigatorPhoto_HAEUPTLE" );

    this.oController.setAlertIDs([2,3]);

assignIcon.firePress();

equal( this.oAlertActionAdapter.bAlertsActionCalled, true, "Request sent");

      });

 

 

The second technique deals with an interface allowing easier access to the elements. In JavaScript view, you could implement getter methods for the relevant elements. This is the recommended approach. The getter functions should also be used in the productive implementation instead of accessing elements via ID, since that involves some overhead and your code should be agnostic to such implementation details.

 

Another technique is the page object explained in Martin Fowlers blog post: http://martinfowler.com/bliki/PageObject.html

 

 

This blog post is part of a series,like the following blog postto stay tuned and get updates about more topics around software engineering with SAPUI5 and JavaScript:

http://scn.sap.com/community/developer-center/front-end/blog/2013/12/12/engineering-in-javascript

Asynchronous load of SAPUI5

$
0
0

Inspired by following blogs/posts:

Why I want my Fiori to run like a Ferrari

SAPUI5 Mobile Splash Screen

SAPUI5 Content Compression

 

With a SAPUI5 mobile working via 3G you may need to wait up to a couple of seconds, before you get a first screen of the app. The reason is, that SAPUI5 is relative heavy library and need to load some megabytes of data to start working.

 

If you check the most of sapui5 examples - you'll see something like

0002.png

 

This code will be processed by your browser as followed.

 

PR.001.png

SAPUI5 content consists of 2 libraries: sap.ui.commons and sap.m. That's why library-preload.json, library.css and library-parameters.json loaded 2 times.

 

In the browser it looks like

 

0003.png

Blue vertical line at almost 2 seconds mean the user will first see some content after loading about 500KB and 2 seconds awaiting (even more on mobile device with 3G).

 

The idea to show the loading screen as soon as possible to the user, and load SAPUI5 stuff after that. If user have to wait a couple of seconds before start to work - you may want to show some kind of a loading progress. For example, first show a spinner and then a content.

spinner.gif

I desided to move all js code into a separate file application.js and include a spinner js code from github.

 

App logic should look as following

 

PR.003.png

 

First page load time should reduce from 2+ seconds to 100-200 milliseconds. Initial load size from 500 KB should reduce to about 5 KB.

 

So, the new index.html consists of 3 js scripts: spinner, application.js and call of initialization.

0006.png

And now the magic begins. There is no loading of SAPUI5 here.

 

Let's check the code of application.js.

 

oApplication = { // Application is an object  views: {}, // Application views  load: function(src, id, libs, theme, callback) {  var opts = {  length: 12, // The length of each line  width: 4, // The line thickness  radius: 12, // The radius of the inner circle  };  var target = document.getElementById('content');  this.spinner = new Spinner(opts).spin(target);  setTimeout(this.loadSAPUI5(src, id, libs, theme, callback));  },  loadSAPUI5: function (src, id, libs, theme, callback)  {  var s,r,t;  r = false;  s = document.createElement('script');  s.type = 'text/javascript';  s.src = src;  s.id = id;  s.setAttribute("data-sap-ui-libs", libs);  s.setAttribute("data-sap-ui-theme", theme);  s.onload = s.onreadystatechange = function() {      //console.log( this.readyState ); //uncomment this line to see which ready states are called.  if ( !r && (!this.readyState || this.readyState == 'complete') ){  r = true;  callback();  }  };  t = document.getElementsByTagName('script')[0];  t.parentElement.insertBefore(s, t);  },  onSAPUI5Loaded: function(){  oApplication.initializeUI5();  $("body").fadeOut("slow",function(){  $("#content").empty();  $("#content").removeAttr('style');  oApplication.app.placeAt("content");  $(this).fadeIn("slow");  });  },  initializeUI5: function(){  var oApp = new sap.m.App( "mApp" );  var oPage = new sap.m.Page({  id : "mPage", // sap.ui.core.ID  title : "Mobile page", // string  showFooter : false, // boolean, since 1.13.1  });  var oContent = new sap.m.ObjectHeader({  id : "mObjHeader", // sap.ui.core.ID  title : "Title", // string  number : "250", // string  numberUnit : "EUR", // string  markFavorite : true, // boolean, since 1.16.0  markFlagged : true, // boolean, since 1.16.0  showMarkers : true, // boolean, since 1.16.0  attributes : [ new sap.m.ObjectAttribute({  id : "mAttribute", // sap.ui.core.ID  visible : true, // boolean  text : "This is a test attribute of ObjectHeader", // string  }) ], // sap.m.ObjectAttribute  });  oPage.addContent(oContent);  oApp.addPage(oPage);  this.app = oApp;  }
} 

So, whole file is a definition of the js object, which has following methods

load - to call from index.html

loadSAPUI5 - to include SAPUI5 into document and load if asynchronously

onSAPUI5Loaded - to hide a spinner and show the app

initializeUI5 - to create SAPUI5 user interface (consists of the code from previous version of index.html).

 

Logic look like

 

PPR.001.png

LoadSAPUI5 is called asynchronously from oApplication.load via setTimeout() function. If you test your app in browser - you'll get following.

0004.png

at the bottom of page you can see, that DOMContentLoaded is in 21 ms (it's due to local test) with 3 files. Let's upload it to the hosting and make a fair test.

 

0007.png

So, before show the application is just 5kb size and 146ms time.

 

In comparison with the first test:

Diagram.png

 

Example on jsbin.

 

P.S.: This idea helps to speedup only a first application response. SAPUI5 libraries have to be loaded in any case. Synchron or asynchron - it's your choice.

 

P.P.S.:  English language is not my native language, and any person is not insured from mistakes and typing errors. If you have found an error in the text, please let me know - I'll correct the post.

 

P.P.P.S.: If you have some ideas, how to correct/improve this post - please don't hesitate to leave a comment.


This is why I like #openUI5!

$
0
0

Last week at SAP TechEd Bangalore, SAP made a great step forward for the embracement of the Open Source development: OPENUI5 was released; Andreas Kunz wrote a very detailed blog What is OpenUI5 / SAPUI5 ? some days ago.

 

So I decided to use these libraries in order to create an application that could show the power of OPENUI5 as an Open Source framework for enterprise-ready web applications. In my case I decided to create a very simple Phone Book that consumes data form a Google Drive Form.

 

Phonebook_form.PNG

 

Google saves this data as a Google Spreadsheet on Google Drive

Phonebook_data.PNG

Here comes the power of open standards, from Google Drive you can expose your data in order to be consumed from different resources. Form File menu select Publish to the web

 

Phonebook_publish.PNG

Now your data is available as a web resource, in my example using the URL OpenUI5 demo (Responses)

Phonebook_web.PNG

It is not explicitly mentioned but this data is now available also as a JSON REST service via the URI https://spreadsheets.google.com/feeds/cells/0AsSW06f14AJYdFg0Tmg1QkttU2hvaTJLeUEzaHdKeFE/od6/public/values?alt=json

 

Phonebook_json.PNG

Via a simple PHP proxy (base source) that I adapted with the snipped of code below, I retrieved this data and exposed as a formatted JSON that can be easily consumed by an OPENUI5 application.

 

function convertData($data) {  $phonebook = array();  $contact = array();  foreach ($data['feed']['entry'] as &$entry) {  $col = $entry['gs$cell']['col'];  $row = $entry['gs$cell']['row'];  $value = $entry['content']['$t'];  if ( $lastRow != $row && $row > 2) {  $lastRow = $row;  $phonebook[] = $contact;  $contact = array();  }  if ($row > 1) {  switch ($col) {  case "2":  $contact['name'] = $value;  break;  case "3":  $contact['lastname'] = $value;  $contact['fullname'] = $contact['name']." ".$contact['lastname'];  break;  case "4":  $contact['address'] = $value;  break;  case "5":  $contact['address2'] = $value;  break;  case "6":  $contact['city'] = $value;  break;  case "7":  $contact['state'] = $value;  break;  case "8":  $contact['pcode'] = $value;  break;  case "9":  $contact['country'] = $value;  break;  case "10":  $contact['dob'] = $value;  break;  case "11":  $value = ltrim($value,"'");  $contact['phone'] = $value;  break;  case "12":  $contact['email'] = $value;  break;  case "13":  $contact['skype'] = $value;  $contact['skypeURL'] = "skype:".$value."?call";  break;  case "14":  $contact['twitter'] = $value;  $contact['twitterURL'] = "https://twitter.com/".$value;  break;  case "15":  $contact['image'] = $value;  break;  default:  break;  }  }  }  $phonebook[] = $contact;  return $phonebook;
}

 

Phonebook_json2.PNG

 

Here comes the fun, OPENUI5 is a library that allows you to create a professional application rapidly and easily; it took me just a couple of days during my free time to create a fully working Enterprise Mobile Phonebook that works on desktop, tablet and phone:

 

Phoonebook_desktop.PNGDesktop

Phoonebook_iPhone.PNGSmartphone

 

This is just an idea that would show the versatility of OPENUI5 (aka SAPUI5) also in not SAP contexts.

 

If you want to play around and study the source code of this application you can access to the Phonebook form to add new data and the Phonebook application to see the final output.

 

Notes

  • Security: this form is public visible without any restriction for demo purposes, you can easily protect your data using Google authentication.
  • Validation: for the purpose of this demo very few validations have been implemented, in a productive solution you should consider implementing validation logic.

How to replace an ODataModel by a JSON Model

$
0
0

For local testing and unit testing, it is essential to replace an ODataModel by a JSON model. The semantics of these two models is quite different. For testing, the following differences are relevant.

 

First, you have to replace the ODataModel by a JSONModel.  The function createAlertListWith2Alerts creates a JSON Model with 2 Alerts.

 

    this.createAlertListWith2Alerts = function() {

      var aAlerts = {

        "AlertQueries(name'Alerts')" :

  { Alerts :

                [{      RawKey : "A1",

                         AlertID : "1",

                         InvestigationObjectType : "CLAIM_RS", ...

  };

       returnnew JSONModel(aAlerts);

   };

 

After the JSONModel has been created, the model can be injected with setModel().

 

Second, the ODataModel knows keys that the JSON Model does not know. The JSON model is purely string based. E.g. with the following statement a list of alerts shall be bound to items of a list.


    alertList.bindAggregation( "items", {

      path : "/AlertQueries(name'Alerts')/Alerts" ,

 

The name 'Alerts' is interpreted by the ODataModel as key for calling a specific query in the backend. Since JSON does not know keys, the JSON data format is as follows: In the JSON data, the name'Alerts' is part of the attribute name. For the binding to JSON model the path AlertQueries(name'Alerts') is just a string.

      var aAlerts = {

        "AlertQueries(name'Alerts')" :

  { Alerts :

                [{ RawKey : "A1",

                    AlertID : "1",

                   InvestigationObjectType : "CLAIM_RS", ...

 

Third, the OData expand parameter is ignored by JSON. But this does not matter. You just have to provide the required parameters in the JSON data.

 

Fourth, an ODataModel has also a function for reading, synchronizing from the backend and for performing changes on the backend. The JSON model does not have these functions. If the productive code uses these functions (e.g. read), you have to implement the used functions in the JSON model test double. In most cases it is sufficient to implement these functions as empty functions.


This blog post is part of a series,like the following blog postto stay tuned and get updates about more topics around software engineering with SAPUI5 and JavaScript:

http://scn.sap.com/community/developer-center/front-end/blog/2013/12/12/engineering-in-javascript

Export sap.ui.table.Table as CSV

$
0
0

Hi all!,

 

This is an example to create a link to download a sap.ui.table.Table to CSV at client side (JS, supported by HTML5 browsers).

 

The next JS code, extracts header and content from table column&binding definition to generate a CSV B64 string. The download link uses 'download' HTML5 attr and Data Uri scheme (Data URI scheme - Wikipedia, the free encyclopedia) to export a filename at client side.

 

table_and_link.PNG

 

file_csv.PNG

 

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 src="util/Base64.js"></script>

 

 

  <script>

  //Default view PortalUserView

  sap.ui.localResources("view");

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

  view.placeAt("content");

  </script>

 

 

  </head>

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

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

  </body>

</html>

 

View:

 

sap.ui.jsview("view.ExampleView", {

 

 

  /** 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 view.ExampleView

  */

  getControllerName : function() {

  return "view.ExampleView";

  },

 

 

  /** 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 view.ExampleView

  */

  createContent : function(oController) {

 

  //Define some sample data

  var aData = [

  {lastName: "Dente", name: "Al", checked: true, linkText: "www.sap.com", href: "http://www.sap.com", gender: "male", rating: 4},

  {lastName: "Friese", name: "Andy", checked: true, linkText: "www.sap.com", href: "http://www.sap.com", gender: "male", rating: 2},

  {lastName: "Mann", name: "Anita", checked: false, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "female", rating: 3},

  {lastName: "Schutt", name: "Doris", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "female", rating: 4},

  {lastName: "Open", name: "Doris", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "female", rating: 2},

  {lastName: "Dewit", name: "Kenya", checked: false, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "female", rating: 3},

  {lastName: "Zar", name: "Lou", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 1},

  {lastName: "Burr", name: "Tim", checked: true, linkText: "www.sap.com", href: "http://www.sap.com", gender: "male", rating: 2},

  {lastName: "Hughes", name: "Tish", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 5},

  {lastName: "Lester", name: "Mo", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 3},

  {lastName: "Case", name: "Justin", checked: false, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 3},

  {lastName: "Time", name: "Justin", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 4},

  {lastName: "Barr", name: "Gaye", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 2},

  {lastName: "Poole", name: "Gene", checked: true, linkText: "www.sap.com", href: "http://www.sap.com", gender: "male", rating: 1},

  {lastName: "Ander", name: "Corey", checked: false, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 5},

  {lastName: "Early", name: "Brighton", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 3},

  {lastName: "Noring", name: "Constance", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "female", rating: 4},

  {lastName: "Haas", name: "Jack", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "male", rating: 2},

  {lastName: "Tress", name: "Matt", checked: true, linkText: "www.sap.com", href: "http://www.sap.com", gender: "male", rating: 4},

  {lastName: "Turner", name: "Paige", checked: true, linkText: "www.sap.com", href: "http://www.sap.com",  gender: "female", rating: 3}

  ];

 

  var oTable = createTable(aData);

  var csvText = generateTableCSV(oTable, aData);

  var oLink  = createDownloadLink(csvText);

 

  var layout = new sap.ui.commons.layout.VerticalLayout("layout", {

         content: [oLink, oTable]

  });

 

  return layout;

  }

 

 

});

 

 

/**

* Example table creation.

* See https://sapui5.netweaver.ondemand.com/sdk/#test-resources/sap/ui/table/demokit/Table.html#__2

* @param aData Table json data

*/

function createTable(aData) {

 

 

  //Create an instance of the table control

  var oTable = new sap.ui.table.Table({

  title: "Table Example",

  visibleRowCount: 7,

  firstVisibleRow: 3,

  selectionMode: sap.ui.table.SelectionMode.Single

  });

 

 

  //Define the columns and the control templates to be used

  var oColumn = new sap.ui.table.Column({

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

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

  sortProperty: "lastName",

  filterProperty: "lastName",

  width: "200px"

  });

  var oCustomMenu = new sap.ui.commons.Menu();

  oCustomMenu.addItem(new sap.ui.commons.MenuItem({

  text:"Custom Menu",

  select:function() {

  alert("Custom Menu");

  }

  }));

  oColumn.setMenu(oCustomMenu);

  oTable.addColumn(oColumn);

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

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

  template: new sap.ui.commons.TextField().bindProperty("value", "name"),

  sortProperty: "name",

  filterProperty: "name",

  width: "100px"

  }));

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

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

  template: new sap.ui.commons.CheckBox().bindProperty("checked", "checked"),

  sortProperty: "checked",

  filterProperty: "checked",

  width: "75px",

  hAlign: "Center"

  }));

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

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

  template: new sap.ui.commons.Link().bindProperty("text", "linkText").bindProperty("href", "href"),

  sortProperty: "linkText",

  filterProperty: "linkText"

  }));

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

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

  template: new sap.ui.commons.ComboBox({items: [

  new sap.ui.core.ListItem({text: "female"}),

  new sap.ui.core.ListItem({text: "male"})

  ]}).bindProperty("value","gender"),

  sortProperty: "gender",

  filterProperty: "gender"

  }));

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

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

  template: new sap.ui.commons.RatingIndicator().bindProperty("value", "rating"),

  sortProperty: "rating",

  filterProperty: "rating"

  }));

 

 

  //Create a model and bind the table rows to this model

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

  oModel.setData({modelData: aData});

  oTable.setModel(oModel);

  oTable.bindRows("/modelData");

 

 

  //Initially sort the table

  oTable.sort(oTable.getColumns()[0]);

 

 

  //Bring the table onto the UI

  return oTable;

}

 

 

/**

* Export table header and data into a CSV string.

*/

function generateTableCSV(table, jsonData) {

  var info = '';

 

  for (var i =0; i<table.getColumns().length; i++) {

  info+= encodeURIComponent(table.getColumns()[i].getLabel().getText()) + ';';

  }

 

  info += '\r\n';

 

  if (jsonData.length != undefined) {

  for (var j=0; j<jsonData.length; j++) {

  for (var i =0; i<table.getColumns().length; i++) {

  if (table.getColumns()[i].getTemplate() != undefined && table.getColumns()[i].getTemplate().getBinding('text') != undefined) {

  var valor = eval('jsonData[j].'+table.getColumns()[i].getTemplate().getBinding('text').sPath);

  info+= encodeURIComponent(valor) + ';';

  } else if (table.getColumns()[i].getTemplate() != undefined && table.getColumns()[i].getTemplate().getBinding('value') != undefined) {

  var valor = eval('jsonData[j].'+table.getColumns()[i].getTemplate().getBinding('value').sPath);

  info+= encodeURIComponent(valor) + ';';

  } else

  info+= ';';

  }

  info += '\r\n';

  }

  } else {

  $.each(jsonData, function(key,value){

  for (var i =0; i<table.getColumns().length; i++) {

  if (table.getColumns()[i].getTemplate() != undefined && table.getColumns()[i].getTemplate().getBinding('text') != undefined) {

  var valor = eval('jsonData[j].'+table.getColumns()[i].getTemplate().getBinding('text').sPath);

  info+= encodeURIComponent(valor) + ';';

  } else if (table.getColumns()[i].getTemplate() != undefined && table.getColumns()[i].getTemplate().getBinding('value') != undefined) {

  var valor = eval('jsonData[j].'+table.getColumns()[i].getTemplate().getBinding('value').sPath);

  info+= encodeURIComponent(valor) + ';';

  } else

  info+= ';';

  }

  info += '\r\n';

  });

  }

 

  return info;

}

 

 

/**

* Creates a link target to base64 data

*/

function createDownloadLink(b64text) {

  var oLink = new sap.ui.commons.Link("linkExportCsv", {

  text: 'Download as CSV',

  href: 'data:application/csv;charset=utf-8;base64,' + (Base64.encode(b64text))

  });

 

  initDownloadAttr('FileName-Example.csv')

 

  return oLink;

}

 

 

/**

* Creates download attribute to set filename

*/

function initDownloadAttr() {

  if ($( "#linkExportCsv" ).length > 0) {

  $( "#linkExportCsv" ).attr('download', 'filename.csv');

  } else {

  setTimeout(initDownloadAttr, 1000);

  }

}


Base 64 util:


/**

*

*  Base64 encode / decode

http://www.webtoolkit.info/

*

**/

 

var Base64 = {

 

  // private property

  _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

 

  // public method for encoding

  encode : function (input) {

  var output = "";

  var chr1, chr2, chr3, enc1, enc2, enc3, enc4;

  var i = 0;

 

  input = Base64._utf8_encode(input);

 

  while (i < input.length) {

 

  chr1 = input.charCodeAt(i++);

  chr2 = input.charCodeAt(i++);

  chr3 = input.charCodeAt(i++);

 

  enc1 = chr1 >> 2;

  enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);

  enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);

  enc4 = chr3 & 63;

 

  if (isNaN(chr2)) {

  enc3 = enc4 = 64;

  } else if (isNaN(chr3)) {

  enc4 = 64;

  }

 

  output = output +

  this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +

  this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

 

  }

 

  return output;

  },

 

  // public method for decoding

  decode : function (input) {

  var output = "";

  var chr1, chr2, chr3;

  var enc1, enc2, enc3, enc4;

  var i = 0;

 

  input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

 

  while (i < input.length) {

 

  enc1 = this._keyStr.indexOf(input.charAt(i++));

  enc2 = this._keyStr.indexOf(input.charAt(i++));

  enc3 = this._keyStr.indexOf(input.charAt(i++));

  enc4 = this._keyStr.indexOf(input.charAt(i++));

 

  chr1 = (enc1 << 2) | (enc2 >> 4);

  chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);

  chr3 = ((enc3 & 3) << 6) | enc4;

 

  output = output + String.fromCharCode(chr1);

 

  if (enc3 != 64) {

  output = output + String.fromCharCode(chr2);

  }

  if (enc4 != 64) {

  output = output + String.fromCharCode(chr3);

  }

 

  }

 

  output = Base64._utf8_decode(output);

 

  return output;

 

  },

 

  // private method for UTF-8 encoding

  _utf8_encode : function (string) {

  string = string.replace(/\r\n/g,"\n");

  var utftext = "";

 

  for (var n = 0; n < string.length; n++) {

 

  var c = string.charCodeAt(n);

 

  if (c < 128) {

  utftext += String.fromCharCode(c);

  }

  else if((c > 127) && (c < 2048)) {

  utftext += String.fromCharCode((c >> 6) | 192);

  utftext += String.fromCharCode((c & 63) | 128);

  }

  else {

  utftext += String.fromCharCode((c >> 12) | 224);

  utftext += String.fromCharCode(((c >> 6) & 63) | 128);

  utftext += String.fromCharCode((c & 63) | 128);

  }

 

  }

 

  return utftext;

  },

 

  // private method for UTF-8 decoding

  _utf8_decode : function (utftext) {

  var string = "";

  var i = 0;

  var c = c1 = c2 = 0;

 

  while ( i < utftext.length ) {

 

  c = utftext.charCodeAt(i);

 

  if (c < 128) {

  string += String.fromCharCode(c);

  i++;

  }

  else if((c > 191) && (c < 224)) {

  c2 = utftext.charCodeAt(i+1);

  string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));

  i += 2;

  }

  else {

  c2 = utftext.charCodeAt(i+1);

  c3 = utftext.charCodeAt(i+2);

  string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));

  i += 3;

  }

 

  }

 

  return string;

  }

 

}

Talk: JavaScript - Programming Style & Your Brain by Douglas Crockford

$
0
0

Be a better Programmer

 

This keynote talk from YUIConf 2011 is one of my favorites! Yahoo! JavaScript architect Douglas Crockford explains why good code style is important for every programmer. He shows several examples in JavaScript and illustrates the benefits of JSLint. A very interesting and amusing contribution of a real mentor!

 

UI5 SDK on Node.js

$
0
0

I recently had to rebuild my laptop and one of the first things I did was to install Node.js and have it statically serve my UI5 SDKs, I thought I would share how easy it is to setup.

 

"Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications". Node.js has an easy to use command line utility npm for managing the JavaScript dependencies in your applications, npm can also be used for loading one of the many Node.js applications from the npm (Node Packaged Module) registry.  Besides using Node.js as an application platform it provides many tools to aid UI5 development, I use node hosted apps inside of Sublime Text for formatting and checking (linting) my code and use gruntjs for automating a lot of the repetitive test, build and deploy tasks, I even use generator-openui5 to create my UI5 mobile projects, views and controllers.

 

Start at the end

C:\sapui5\sdk is where i store the various UI5 SDK versions, also in that folder i have a JavaScript file static_server.js which has simple code to create a node server and publish the content within the directory

sapui5_sdk_folder.PNG

To run the server open up a windows command line window, <shift> <right click> on folder -> "Open command window here" then enter

node static_server

This command starts the server and opens up the default browser with directory listing

sapui5_sdk_directory.PNG

 

from here you can choose to run the SDK

sapui5_latest.PNG

or browse the directory and easily navigate and view the debug files in the browser

debug_example.PNG

The code

static_server.js - put this file in the directory

var express = require('express'),  open = require('open');  app = express(),  port = process.env.PORT || 8888,  sapui5 = '/sapui5'  url = 'http://localhost:' + port + sapui5,  year = 60 * 60 * 24 * 365 * 1000;
// Use compress middleware to gzip content
app.use(express.compress());
//set default mime type to xml for ".library" files
express.static.mime.default_type = "text/xml";
// Serve up content directory showing hidden (leading dot) files
app.use(sapui5,express.static(__dirname, { maxAge: year, hidden: true }));
// enable directory listening
app.use(sapui5,express.directory(__dirname));
app.listen(port);
open(url); //open in default browser
console.log("Static file server running at\n  => " + url + " \nCTRL + C to shutdown")

In the above code expressjs a lightweight app framework creates compressed and cache-controlled content with directory listing and serves it at http://localhost:<port>/sapui5 , the open module opens the default browser application.

 

Installation

Installing Nodejs is very simple - goto nodejs.org and click on "Install",  the default installation has enough to get started.

 

Once you have Node.js installed open a command prompt in the directory where the SDK is located, then use npm to install expressjs and node open

npm_install.png

this will load the module into the node_modules directory and do the same for open

npm install open

the preferred alternative to manually installing would be to create a package.json file in the directory and define the dependencies

{  "dependencies" : {    "express" : "3.x",    "open" : "0.0.x"  }

then run  "npm install" from the command line.

 

Once the dependencies are installed you should now be able to run the server

node static_server

Enjoy :-)

Viewing all 789 articles
Browse latest View live




Latest Images