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

Code checking and formatting in Sublime Text

0
0

In my previous blog I highlighted that recently i had to rebuild my laptop, next on the list of things to do was set up Sublime Text my prefered UI5 development environment.

 

Two sites i use a lot with JavaScript development are JS Beautifier kind of a pretty printer for JavaScript and JSHint, a JavaScript Code Quality Tool similar to the ABAP code inspector. For me JSHint is my first unit test, it tells me very quickly if my code has errors or has some bad practices. Both of these applications can be run on Node.js and are available as packages to install in Sublime Text. In this blog i will explain the steps i took to install these packages.

 

To install packages on Sublime Text I use the Package Control, its very easy to use, to install follow the instructions here Installation - Package Control

 

Once the Package Control is installed you can start searching for and installing available packages.

 

To run goto Tools -> Command Pallete or <Ctrl> <Shift> p and type "install" select "Package Control: Install Package" and <Enter>

install.png

to install the package based on JS Beautifier type "prettify" and select "HTML-CSS-JS Prettify" and <Enter>.

prettify.png

If you look at the status bar at the bottom you should see updates on how the install is going. When the package isinstalled you will see an entry in the context menu as shown in the pic below.

 

To get there <Right Click> anywhere inside a document and choose "HTML/CSS/JS Prettify" -> "Prettify Code"

pretty1.png

and the result

pretty2.png

works for your xml views also.

 

You can find more information about the features and options of this package at victorporof/Sublime-HTMLPrettify · GitHub

 

There are a few versions of JSHint on Sublime Text the one i like to use is JSHint Gutter.

 

To install, as before <Ctrl><Shift> p "install" <Enter> and type "JSHint Gutter"

jsihint_gutter.png

Once installed, similar to the previous package to run you <Right Click> inside the document you want to check and select "JSHint" -> "Lint Code"

jsihint1.png

the results first time will look something similar to below.

codelint1.png

The default preferences needs to be changed to recognise the acceptable UI5 application global variables, for a full list of the options JSHint Options Reference

 

<Right Click> in file are choose JSHint -> Set Linting Preferences,

 

below is my first attempt from memory, please share improvements

{    "browser": true,    "esnext": true,    "globals": {        "jQuery": true,        "sap": true,        "$": true,        "util": true,        "view": true,        "model": true    },    "globalstrict": false,    "quotmark": false,    "smarttabs": true,    "trailing": true,    "undef": true,    "unused": true
}

For the  features and options of the package victorporof/Sublime-JSHint · GitHub

 

Happy coding

jsp


Building a CRUD Application with SAPUI5 Using Odata Model

0
0

This blog explains how to create a CRUD application using SAPUI5 Using Odata Model.

Blog Content :

Creating the User Interface

  • Creating the project
  • Setting up the SAPUI5 bootstrap and required libraries
  • Creating the view components
  • Implementing the Controller’s methods

Read

Create

Update

Delete

 

 

 

Prerequisites

 

  • You have to installed the SAPUI5 Application Development Tool into your Eclipse
  • UI Development Toolkit for HTML5 Version SAPUI5 1.16

 

Steps to be followed :-

  1. 1. Create the Project :

create a SAPUI5 MVC project in eclips

It shows like below mentioned in the picture :

Picture1.gif

Setting up the SAPUI5 bootstrap and required libraries in index.html file

Add the required libraries in the  "data-sap-ui-libs" tag

Picture2.jpg

  1. 2. Creating the view components and fetching the Bank data :

Add following code in to the bank.view.js

  1. sap.ui.jsview("bankcrud.bank", {

/**SpecifiestheControllerbelongingtothisView.

*Inthecasethatitisnotimplemented,orthat"null"isreturned,thisViewdoesnothaveaController.

*@memberOfbankcrud.bank

*/

getControllerName : function() {

return"bankcrud.bank";

},

/**IsinitiallycalledonceaftertheControllerhasbeeninstantiated.ItistheplacewheretheUIisconstructed.

*SincetheControllerisgiventothismethod,itseventhandlerscanbeattachedrightaway.

*@memberOfbankcrud.bank

*/

createContent : function(oController) {

var layout = new sap.ui.commons.layout.MatrixLayout({

id : 'matrix4',

layoutFixed : false,

});

var appHeader = new sap.ui.commons.ApplicationHeader('appHeader',   {

logoText : "Bank Application",

displayLogoff : false,

displayWelcome : true,

userName : "Welcome"

});

layout.createRow(appHeader);

var rPannel = new sap.ui.commons.Panel('rPannel', {

text : "BankCollection List",

});

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

id : "bankTableID",

title: "Bank CRUD Application",

width : "100%",

visibleRowCount: 10,

selectionMode : sap.ui.table.SelectionMode.Single,

//setEditable : false,

rowSelectionChange : function(oEvent) {},

toolbar: new sap.ui.commons.Toolbar({

                 items: [

                      new sap.ui.commons.Button({

                                text: "Create",

                                press: function() {

                                     oController.Create();

                                }

                      }),

                      new sap.ui.commons.Button({

                             text: "Update",

                                press: function() {

                                     oController.Update();

                                }

                      }),

           new sap.ui.commons.Button({

                   text: "Delete",

                    press: function() {

                   oController.Delete();

                                }

                      }),

                     ]

}

});

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

label : new sap.ui.commons.Label({

text : "Bank ID"

}),

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

"bankID"),

sortProperty : "bankID"

}));

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

label : new sap.ui.commons.Label({

text : "Bank Country"

}),

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

"bankCountry"),

sortProperty : "bankCountry"

}));

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

label : new sap.ui.commons.Label({

text : "Bank Name"

}),

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

"bankName"),

sortProperty : "bankName"

}));

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

label : new sap.ui.commons.Label({

text : "Region"

}),

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

"region"),

sortProperty : "region"

}));

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

label : new sap.ui.commons.Label({

text : "Street"

}),

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

"street"),

sortProperty : "street"

}));

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

label : new sap.ui.commons.Label({

text : "City"

}),

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

"city"),

sortProperty : "city"

}));

// Add table to the Panel

rPannel.addContent(oTable);

// Add panel to the Layout

layout.createRow(rPannel);

// Display Layout

this.addContent(layout);

}

});

Then bind the bank table in controller file. Add the  following code in to the "onInit" function

onInit: function() {

//Initialize the Model

var oModel = new sap.ui.model.odata.ODataModel("http://domain_name/sap/opu/odata/sap/Z_TM_BANK_SRV",false, "Username", "Password");

//Set the Model to the Table

var oTable = sap.ui.getCore().byId("bankTableID");

oTable.setModel(oModel);

// Filter the DATA

var FilterOperator = sap.ui.model.FilterOperator;

var filter = new sap.ui.model.Filter("bankCountry",FilterOperator.EQ, "AR");

//Bind the Data to the Table

oTable.bindRows("/BankCollection", null, null,[ filter ]);

},

Output :

After binding the table :

Create Operation :

  1. 1. Add the button in view file (already created-in view file)
  2. 2. Create the function for create operation in controller file

Add the following code mentioned below :

Create: function() {

// Create a dialog to get the information of the bank to be created

var oDialog = new sap.ui.commons.Dialog("Dialog", {

    modal: true,

closed: function(oControlEvent){

  sap.ui.getCore().getElementById('Dialog').destroy();

     }

  });

  oDialog.setTitle("New Bank Collection");

  // Create a layout to place the controls in the dialog

  var oLayout = new sap.ui.commons.layout.MatrixLayout({

columns: 2,

width: "100%"

  });

  // Create text field for the bankCountry

  var oTF = new sap.ui.commons.TextField("tbankCountry", {

          tooltip: 'Bank Country',

          editable: true,

          width: '200px',

        required: true

  });

var oLabel = new sap.ui.commons.Label("lbankCountry", {

    text: 'Bank Country',

   labelFor: oTF

  });

// Create the first row

  oLayout.createRow(oLabel, oTF);

  // Create text field for the bankID

  oTF = new sap.ui.commons.TextField("tbankID", {

tooltip: 'Bank ID',

   editable: true,

required: true,

width: '200px'

  });

oLabel = new sap.ui.commons.Label("lbankID", {

    text: 'Bank ID',

labelFor: oTF

});

  oLayout.createRow(oLabel, oTF);

 

  oTF = new sap.ui.commons.TextField("tbankName", { 

          tooltip: 'Bank Name',

             editable: true,

          required: true,

          width: '200px'

  });

  oLabel = new sap.ui.commons.Label("lbankName", {

   text: 'Bank Name',

  labelFor: oTF

  });

  oLayout.createRow(oLabel, oTF);

  oTF = new sap.ui.commons.TextField("tregion", {

      tooltip: 'Region Name',

          maxLength:3,

          editable: true,

               width: '200px

  });

  // Label for the last name field

  oLabel = new sap.ui.commons.Label("lregion", {

          text: 'Region Name',

          labelFor: oTF

  });

  // Create the 4th row

  oLayout.createRow(oLabel, oTF);

  oTF = new sap.ui.commons.TextField("tstreet", {

tooltip: 'Street Name',

editable: true,

width: '200px'

});

  oLabel = new sap.ui.commons.Label("lstreet", {

       text: 'Street Name',

     labelFor: oTF

  });

  oLayout.createRow(oLabel, oTF);

  oTF = new sap.ui.commons.TextField("tcity", {

     tooltip: 'City Name',

     editable: true,

       width: '200px'

});

  oLabel = new sap.ui.commons.Label("lcity", {

        text: 'City Name',

        labelFor: oTF

  });

  oLayout.createRow(oLabel, oTF);

// Add the layout to the dialog

  oDialog.addContent(oLayout);

// Add a button to post the bank's data to the odata interface

  oDialog.addButton(new sap.ui.commons.Button({text: "Save", press:function(){

  // Add a button to post the bank's data to the odata interface

 

    var bankCountry_var    = sap.ui.getCore().getControl("tbankCountry").getValue(); 

         var bankID_var      = sap.ui.getCore().getControl("tbankID").getValue(); 

         var bankName_var  = sap.ui.getCore().getControl("tbankName").getValue(); 

         var region_var   = sap.ui.getCore().getControl("tregion").getValue(); 

         var street_var   = sap.ui.getCore().getControl("tstreet").getValue(); 

         var city_var    = sap.ui.getCore().getControl("tcity").getValue(); 

         OData.request 

         ({  

              requestUri:      "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV/BankCollection/?$filter=bankCountry eq'AR'",  

                    method: "GET",  

                    headers:  

                        {       

    "X-Requested-With": "XMLHttpRequest"

   "Content-Type": "application/atom+xml"

"DataServiceVersion": "2.0",          

"X-CSRF-Token":"Fetch"                                 }                    

                 },  

                 function (data, response) 

                 { 

     header_xcsrf_token = response.headers['x-csrf-token']; 

                  OData.request 

                  ({  

                       requestUri: 

                        "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV/BankCollection",  

                             method: "POST",  

    headers: {   "X-Requested-With": "XMLHttpRequest",                        

"Content-Type": "application/atom+xml"

     "DataServiceVersion": "2.0",  

"Accept": "application/atom+xml,application/atomsvc+xml,application/xml"

"X-CSRF-Token": header_xcsrf_token    },  

                             data:  

                                 {  

                              bankCountry: bankCountry_var,  

                              bankID:bankID_var, 

                              bankName:bankName_var, 

                              region: region_var, 

                              street: street_var, 

                              city: city_var, 

                      }  

                          },

                          function (data, response) 

                            {  

                           document.location.reload(true);

                                             $("<div>Returned data " + window.JSON.stringify(data) + "</div>").appendTo($("#MessageDiv")); 

                            },  

                                   function (err)  

                                   { 

                                        $("<div>Returned error " + window.JSON.stringify(err.response) + "</div>").appendTo($("#MessageDiv")); 

                                   } 

                  ); 

        },  

        function (err)  

                       { 

                            var request = err.request; // the request that was sent. 

                            var response = err.response; // the response that was received. 

                            alert("Error in Get -- Request "+request+" Response "+response); 

                       } 

        );                      

  oDialog.close();

}}));

  oDialog.open();

},

Output :

Before creation :

After Creation :


Update Operation :

  1. 1. Add the button in view file (already created-in view file)
  2. 2. Create the function for update operation in controller file

Add the following code mentioned below :

Update : function() {

var oTable = sap.ui.getCore().getElementById('bankTableID');

       var i = oTable.getSelectedIndex();

       var ServiceURL = "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV";

       if (i == -1) {

               alert("Please Select a row to Update");

               return;

             }elseif(i>=0){

        var selectedId = selectedRow.getCells()[0].getValue();

        var selectedCount = selectedRow.getCells()[1].getValue();

        OData.read(ServiceURL + "/BankCollection(bankCountry='"

+ selectedCount + "',bankID='"

+ selectedId + "')",

        function(response) {

      var oDialogu = new sap.ui.commons.Dialog("Dialogu", {

                  modal: true,

           closed: function(oControlEvent){

          sap.ui.getCore().getElementById('Dialogu').destroy();

             }

  });

      oDialogu.setTitle("Update Bank Collection");

var oLayout = new sap.ui.commons.layout.MatrixLayout({

            columns: 2,

        width: "100%"

  });

  varoTF = new sap.ui.commons.TextField("tbankCountry", {

               tooltip: 'Bank Country',

    editable: false,

             value:response.bankCountry,

          width: '200px',

        required: true

  });

var oLabel = new sap.ui.commons.Label("lbankCountry", {

        text: 'Bank Country',

      labelFor: oTF

  });

  oLayout.createRow(oLabel, oTF);

   oTF = new sap.ui.commons.TextField("tbankID", {

          tooltip: 'Bank ID',

         editable: false,

          required: true,

          width: '200px',

      value:response.bankID

  });

  oLabel = new sap.ui.commons.Label("lbankID", {

          text: 'Bank ID',

          labelFor: oTF

  });

         oLayout.createRow(oLabel, oTF);

      oTF = new sap.ui.commons.TextField("tbankName", {

          tooltip: 'Bank Name',

          editable: true,

          required: true,

          width: '200px',

       value:response.bankName

  });

oLabel = new sap.ui.commons.Label("lbankName", {

          text: 'Bank Name',

          labelFor: oTF

  });

  oLayout.createRow(oLabel, oTF);

  oTF = new sap.ui.commons.TextField("tregion", {

          tooltip: 'Region Name',

          maxLength:3,

          editable: true,

          value:response.region ,

          width: '200px' 

  });

  oLabel = new sap.ui.commons.Label("lregion", {

        text: 'Region Name',

         labelFor: oTF

  });

 

oLayout.createRow(oLabel, oTF);

  oTF = new sap.ui.commons.TextField("tstreet", {

tooltip: 'Street Name',

editable: true,

width: '200px',

  value:response.street

  });

  oLabel = new sap.ui.commons.Label("lstreet", {

text: 'Street Name',

    labelFor: oTF

  });

  oLayout.createRow(oLabel, oTF);

  oTF = new sap.ui.commons.TextField("tcity", {

          tooltip: 'City Name',

          editable: true,

          value:response.city,

          width: '200px'

  });

  oLabel = new sap.ui.commons.Label("lcity", {

text: 'City Name',

labelFor: oTF

  });

  oLayout.createRow(oLabel, oTF);

  oDialogu.addContent(oLayout);

  oDialogu.addButton(new sap.ui.commons.Button({text: "Update", press:function(){

  var bankCountry_var    = sap.ui.getCore().getControl("tbankCountry").getValue(); 

         var bankID_var      = sap.ui.getCore().getControl("tbankID").getValue(); 

         var bankName_var  = sap.ui.getCore().getControl("tbankName").getValue(); 

         var region_var   = sap.ui.getCore().getControl("tregion").getValue(); 

         var street_var   = sap.ui.getCore().getControl("tstreet").getValue(); 

         var city_var    = sap.ui.getCore().getControl("tcity").getValue(); 

  OData.request 

         ({  

  requestUri: 

               "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV/BankCollection/?$filter=bankCountry eq'AR'",  

                    method: "GET",  

                    headers:  

                        {       

"X-Requested-With": "XMLHttpRequest"

"Content-Type": "application/atom+xml"

"DataServiceVersion": "2.0",          

"X-CSRF-Token":"Fetch"   

                          }                    

                 },  

                 function (data, response) 

                 { 

           header_xcsrf_token = response.headers['x-csrf-token']; 

                  OData.request 

                  ({  

                       requestUri: "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV/BankCollection(bankCountry='"+ selectedCount + "',bankID='"+ selectedId+ "')",  

                             method: "PUT",  

                             headers: {  

     "X-Requested-With": "XMLHttpRequest",                        

"Content-Type": "application/atom+xml"

"DataServiceVersion": "2.0",  

  "Accept": "application/atom+xml,application/atomsvc+xml,application/xml"

  "X-CSRF-Token": header_xcsrf_token  

  },   data:  

    {    bankCountry: bankCountry_var,  

      bankID:bankID_var, 

       bankName:bankName_var, 

       region: region_var, 

        street: street_var, 

       city: city_var, 

}  

                          },

   function (data, response) 

                            {

var oSubDialog = new sap.ui.commons.Dialog( {title: "Updated",

                           content : [new sap.ui.commons.Label({

                           text : "Data Updated Successfully"

                           })]});

                           oSubDialog.open();

                           oSubDialog.addButton(new sap.ui.commons.Button({text: "OK", press:function(){oSubDialog.close();}}));                       

                      

                                             $("<div>Returned data " + window.JSON.stringify(data) + "</div>").appendTo($("#MessageDiv")); 

                                       //      document.location.reload(true);

                            },  

                                   function (err)  

                                   { 

                                        $("<div>Returned error " + window.JSON.stringify(err.response) + "</div>").appendTo($("#MessageDiv")); 

                                   } 

                  ); 

        },  

   function (err)  

   var request = err.request; // the request that was sent. 

var response = err.response; // the response that was received. 

alert("Error in Get -- Request "+request+" Response "+response); 

                       } 

        );

  oDialogu.close();

     }}));

  oDialogu.open();     

        });

       }

    },

Output :

Before Update :

 

 

 

After Update :

 

Delete Operation

  1. 1. Add the button in view file (already created-in view file)
  2. 2. Create the function for create operation in controller file

Add the following code mentioned below :

Delete : function(oEvent) {

         var oTable = sap.ui.getCore().getElementById('bankTableID');

  // Retrieve the selected index, i.e., the index of the selected row

      var i = oTable.getSelectedIndex();

varServiceURL = "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV";

        if (i == -1) {

               alert("Please Select a row to Delete");

               return;

             }

             elseif(i>=0){

        var selectedRow = oTable.getRows()[i];

        var selectedId = selectedRow.getCells()[0].getValue();

        var selectedCount = selectedRow.getCells()[1].getValue();

       }

     OData.request 

        ({  requestUri: "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV/BankCollection/?$filter=bankCountry eq'AR'",  

                   method: "GET",  

                   headers:  

                       {       

  "X-Requested-With": "XMLHttpRequest"

   "Content-Type": "application/atom+xml"

  "DataServiceVersion": "2.0",          

  "X-CSRF-Token":"Fetch"   

                         }                    

                },

                function (data, response) 

                {  

                       header_xcsrf_token = response.headers['x-csrf-token']; 

           OData.request 

           ({ 

            requestUri: "http://server_name/sap/opu/odata/sap/Z_TM_BANK_SRV/BankCollection(bankCountry='"+ selectedCount + "',bankID='"+ selectedId+ "')",

  method: "DELETE",  

  headers: {  

                    "X-Requested-With": "XMLHttpRequest",                        

                    "Content-Type": "application/atom+xml"

                    "DataServiceVersion": "2.0",  

                    "X-CSRF-Token": header_xcsrf_token  

                         } 

                   },  

                     function (data, request) 

                     { 

                    document.location.reload(true);

                                 $("<div>Returned data in Delete " + window.JSON.stringify(data) + "</div>").appendTo($("#MessageDiv")); 

                      },  

                    function (err)  

                    { 

                                 $("<div>Returned error in Delete " + window.JSON.stringify(err.response) + "</div>").appendTo($("#MessageDiv"));                              

                     } 

           ); 

       }, 

         function (err)  

             { 

                      var request     = err.request;  

                      var response = err.response;  

                      alert("Error in Get -- Request "+request+" Response "+response); 

             } 

       );       

    }

});

Output:

Before Delete :

 

Before delete four records are there in the table.


After Delete :

 

After delete three records are there in the table.

In this way we can create the desktop application in sapui5 with MVC structure and for the bacnkend data we  have to consume the Nerweaver Gateway Service.In this application Odta Model is used.

UI5 XML Views - Another Example

0
0

I've been diving into UI5 XML views and sharing the love recently - see Mobile Dev Course W3U3 Rewrite - XML Views - An Intro (as part of Mobile Dev Course W3U3 Rewrite - Intro), and the XML view based templates and snippets in my SublimeUI5 Package for the "developer's choice" Sublime Text editor.

 

Recently John Patterson supplied a JSBin example of an OData sourced table with a filter on dates, in answer to Using Table filter when a formatter function is used.

 

This was a very nice example but I thought it would be an interesting exercise to convert it to XML, for a number of reasons:

 

  • XML views are important (in the context of learning about and extending Fiori apps)
  • it would be a good test of my latest #SublimeUI5 snippet "index - Mobile - Single Page - MVC" (indexmspmvc) (Note 1)
  • all of the XML view work I've done so far has involved controls predominantly from the sap.m library (as they're also what the Fiori apps are built with) so I wantd to try using non-sap.m controls (Note 2)

 

So I did, and have made it available in the sapui5bin repo on Github here:

 

sapui5bin/SinglePageExamples/ODataDateTableFilter.html at master · qmacro/sapui5bin · GitHub

 

Open up this link in a separate window to view it and read the rest of the post.

 

I'll cover the "single page MVC" concept in another post; for now, here are a few notes to help you navigate:

 

  • the XML view is defined in a script tag with the id "view1" and brought to life with sap.ui.xmlview({ viewContent:jQuery('#view1').html() })
  • I've specified the XML namespace declarations (xmlns) for the relevant libraries, having the most common controls' namespace (sap.ui.table) as the default
  • I've used the extended binding syntax for the Table's "rows" aggregation, to include the OData select parameters
  • I've declared the date formatting 'dateShort' (for the third column) in a namespace (util.formatting) with jQuery.sap.declare
  • We have a local controller (which doesn't actually have anything to do)

 

The one thing I'm not entirely convinced about is having to set the filterType property on the BirthDate column "procedurally" (in JavaScript); perhaps I'll get round to looking into how to do this particular bit a different way at some stage.

 

Anyway, I thought this might be more useful insight into XML views and single page MVC examples.

 

Share & enjoy!

dj

 

 

 

Note 1: This "single page MVC" idea is something I've wanted to put together and share for a while; it's easy to write a single page demo UI5 app but not so easy to do that and involve the MVC concept as well - in a single file ... until now.

 

Note 2: The SAP Fiori Wave 1 apps have views that are written declaratively in HTML; the SAP Fiori Wave 2 and 3 apps have views written in XML, using the sap.m controls, with a smattering of sap.ui.layout controls too

Custom sorter and filter in SAPUI5 Table

0
0

 

Hi!

 

I've been working with sap.ui.table.Table to adding some sorters and filters. The main purpose of this blog is overwrite default sorting by adding a custom menu to table column header. This code fragment will create a column with default sorting:

 

//Define the columns and the control templates to be used     
oTable.addColumn(new sap.ui.table.Column({         label: new sap.ui.commons.Label({text: "years"}),         template: new sap.ui.commons.TextView().bindProperty("text", "years"),         sortProperty: "years",         filterProperty: "years",         width: "200px"
}));

SAPUI5 table sort column algorithm depends on model type. number = number sorting, text = text sorting, etc.

 

Sometimes we use datasources (external or public services, etc) which contain bad typed attributes like numeric values typed as string. It is possible override default table column header menu to add a custom sorter.

 

This is an example of wrong sorting:

 

screenshot1.PNG

 

Step 1. Create function addColumnSorterAndFilter


This function will add a custom menu with sorting asc, desc and filtering functionality.

 

/**
 * Adds a custom sort menu for a given table
 * 
 * @param oColumn Target table column to add custom menu
 * @param comparator Function to compare two values of column oColumn
 */
function addColumnSorterAndFilter(oColumn, comparator) {  var oTable = oColumn.getParent();  var oCustomMenu = new sap.ui.commons.Menu();        oCustomMenu.addItem(new sap.ui.commons.MenuItem({                text: 'Sort ascending',                icon:"/com.sap.scn.demo/resources/sap/ui/table/themes/sap_goldreflection/img/ico12_sort_asc.gif",                select:function() {                 var oSorter = new sap.ui.model.Sorter(oColumn.getSortProperty(), false);                 oSorter.fnCompare=comparator;                 oTable.getBinding("rows").sort(oSorter);                                  for (var i=0;i<oTable.getColumns().length; i++) oTable.getColumns()[i].setSorted(false);                                  oColumn.setSorted(true);                 oColumn.setSortOrder(sap.ui.table.SortOrder.Ascending);                }    }));    oCustomMenu.addItem(new sap.ui.commons.MenuItem({     text: 'Sort descending',        icon:"/com.sap.scn.demo/resources/sap/ui/table/themes/sap_goldreflection/img/ico12_sort_desc.gif",        select:function(oControlEvent) {             var oSorter = new sap.ui.model.Sorter(oColumn.getSortProperty(), true);             oSorter.fnCompare=comparator;             oTable.getBinding("rows").sort(oSorter);                              for (var i=0;i<oTable.getColumns().length; i++) oTable.getColumns()[i].setSorted(false);                          oColumn.setSorted(true);             oColumn.setSortOrder(sap.ui.table.SortOrder.Descending);        }    }));        oCustomMenu.addItem(new sap.ui.commons.MenuTextFieldItem({  text: 'Filter',  icon: '/com.sap.scn.demo/resources/sap/ui/table/themes/sap_goldreflection/img/ico12_filter.gif',  select: function(oControlEvent) {      var filterValue = oControlEvent.getParameters().item.getValue();      var filterProperty = oControlEvent.getSource().getParent().getParent().mProperties.sortProperty;      var filters = [];      if (filterValue.trim() != '') {      var oFilter1 = new sap.ui.model.Filter(filterProperty, sap.ui.model.FilterOperator.EQ, filterValue);      filters = [oFilter1];          }      oTable.getBinding("rows").filter(filters, sap.ui.model.FilterType.Application);  }    }));        oColumn.setMenu(oCustomMenu);    return oColumn;
}

 

Step 2. Create your custom comparator


/**
 * Integer comparator
 */
function compareIntegers(value1, value2) {  if ((value1 == null || value1 == undefined || value1 == '') &&  (value2 == null || value2 == undefined || value2 == '')) return 0;  if ((value1 == null || value1 == undefined || value1 == '')) return -1;  if ((value2 == null || value2 == undefined || value2 == '')) return 1;  if(parseInt(value1) < parseInt(value2)) return -1;  if(parseInt(value1) == parseInt(value2)) return 0;  if(parseInt(value1) > parseInt(value2)) return 1;            
};


Step 3. Apply new menu to column

 

var oColumn = new sap.ui.table.Column({  label: new sap.ui.commons.Label({text: "years"}),  template: new sap.ui.commons.TextView().bindProperty("text", "years"),  sortProperty: "years",  filterProperty: "years"  });  oTable.addColumn(oColumn);
addColumnSorterAndFilter(oColumn, compareIntegers);

 

Result


screenshot2.PNG


Any suggestions are welcome!

 

Kind regards


Introducing JADS - A SAPUI5 Development Web Server

0
0

What is JADS

JADS is a by developers for developers light-weight web server designed to cater specifically for SAPUI5 development with OData proxying.

 

 

Background

I do quite a bit of work with SAPUI5/Fiori and web development and one of the challenges for every machine I work on is getting the environment setup - naturally every machine is slightly different so that can take some time but the basics never change - you will always need to:

- Be able to access a SAPUI5 installation (sometimes multiple)

- Be able to develop locally but proxy OData/REST calls to another server

- Be able to see web server logs when something is not quite right

 

JADS is not designed to be a production server but rather a local developer server where they can tweak these scenarios easily & quickly.

 

 

Where did JADS come from:

JADS was born out of an interest by me to try out Node.js, a lightweight runtime which is built on top of Chrome's JavaScript run-time. I started to play with the framework on my commute over 2 days using the scenario of SAPUI5 development. Once I realized I was creating something that I might actually use on a daily basis I decided to get it to a stage where it worked and release it to the community.

 

 

Where is JADS now and where is it going

Right now, I am releasing JADS version 1 which works for standard web development and OData proxying to a SAP System. My next goals for features include:

- Specific support for the customization of SAP Fiori apps

- Pass-through of SAP Auth requests through the proxy to the client

- Adding a folder listing feature for directories

- Support for proxy PUT and POST requests (need to add the body payload)

 

I am open-sourcing the code behind JADS in the hope that if I have missed anything (guaranteed I have! ), others can contribute to suit their scenarios. The JADS source can be found in my github repository https://github.com/bocallaghan/JADS

 

I hope this is useful for somebody else and any comments/suggestions are most welcome,

 

Brenton.

 

P.S. in case anybody is wondering - JADS = Just Another Development (web) Server

Getting Started with UI5

0
0
There's a "Look and Feel" example at  https://sapui5.netweaver.ondemand.com/sdk/#demoapps.html which demonstrates the sort of stuff we want to be building, and if you're feeling brave, you can leave this blog and go straight there!

 

 

 

So far, what I've learned:

FIORI - SAP Developed SAPUI5 Applications.

 

 

It's called SAPUI5 - If you want to develop from scratch. There's a SAPUI5 Reference which seems to be more geared towards HANA, but also gives step-by-step.

 


First bit of coding

The "Hello World" example is basically an HTML and Javascript combo, which you post into a notepad, then run.

It uses a public version of SAPUI5, i.e. a library stored at sapui5.hana.ondemand.com, rather than the library downloaded into Eclipse.

 

One way of looking at this is that you've just coded a UI5 app. Another way of looking at it is that you've just cut and pasted into Notepad, depends how competent you're feeling!

 


Second bit of coding

This requires that you've downloaded Eclipse. This can be done from http://www.eclipse.org/downloads/. I use the "Kepler" version. Eclipse is a development tool totally external to SAP. That's right ABAPers, no more SE80 for you!

 

You also need to download the SAPUI5 Development Toolkit (SDK) which I got from http://scn.sap.com/community/developer-center/front-end. The instructions there were pretty good, explaining how to tie the SDK to the Eclipse installation.

 

 

The second bit of coding is done in Eclipse Proper. You run the wizard and get a View and a Controller created for you. The index.htm ( found in the WebContent folder in Eclipse ) can be run to test the app. But since you've not put anything in yet, it looks empty.

 

The CreateApp describes using Javascript in the index.html.

 

 

 

Now the sad thing is, on the face of it, this was more work, to achieve less. But what you have done is used the toolkit that you'll use for UI5 development, and dipped your toes in the MVC architecture.

 


Getting it onto SAP

 

The guide to getting it on to SAP is here. It describes an upload program, which I didn't need to use in the end. You create an empty BSP, and corresponding SICF node, then go into Eclipse, and share your project as a "SAP UI5 Repository". This bit is really cool, it uses your SAP Logon pad to pick out the systems it could connect to (I used TED). Then asks for your credentials for that system. It then looks at the BSPs available, and asks which one can be looked at.

Let's go to reuse SAPUI5

0
0

Motivation

 

First time we develop complex UIs/Views in SAPUI5 could write 1000-2000 lines in a view. Table creations, forms, layouts, etc. could produce several lines of code. Also we want to standarize our components with same aspect, same styles, same button icons, etc.


SAPUI5 offers a simple mechanism to reuse code: Fragments (https://sapui5.netweaver.ondemand.com/sdk/#docs/guide/Fragments.html).

 

This blog describes a simple example to create two SAPUI5 tables with one Fragment and reuse table code creation.


NOTE: As of the initial release of Fragments in version 1.15.0 of SAPUI5, they are marked as experimental, so the API can change at any time and they are not guaranteed to work properly.

 

Step by step

 

1. Declare your new fragments package.

fragments_1.png

 

Remember load new fragment resources in your html file:

 

sap.ui.localResources("fragment");

2. Create a fragment

 

This fragment creates a table with data binding, different type columns, etc.

 

sap.ui.jsfragment("fragment.Table", {    createContent: function(oController) {       //Obtain data from controller       var aData = oController.fragmentInitData.data;       //Create an instance of the table control       var oTable = new sap.ui.table.Table({            title: oController.fragmentInitData.title, //Obtain title from controller            visibleRowCount: 7       });       //Obtain column definition from controller       for (var i=0; i<oController.fragmentInitData.columns.length; i++) {            var template;            //Define the columns and the control templates to be used            if (oController.fragmentInitData.columns[i].type == "Text")                 template = new sap.ui.commons.TextView().bindProperty("text", oController.fragmentInitData.columns[i].bindingPath);            else if (oController.fragmentInitData.columns[i].type == "Rating")                 template = new sap.ui.commons.RatingIndicator().bindProperty("value", oController.fragmentInitData.columns[i].bindingPath);            else if (oController.fragmentInitData.columns[i].type == "Checkbox")                 template = new sap.ui.commons.CheckBox().bindProperty("checked", oController.fragmentInitData.columns[i].bindingPath)            if (template != undefined) {                 oTable.addColumn(new sap.ui.table.Column({                 label: new sap.ui.commons.Label({text: oController.fragmentInitData.columns[i].title}),                 template:  template,                 sortProperty: oController.fragmentInitData.columns[i].bindingPath,                 filterProperty: oController.fragmentInitData.columns[i].bindingPath,                 width: "200px"            }));       }  }  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;    }
});

 

3. Use your new fragment

 

In this view, we reuse the previous fragment twice:

 

//Define some sample data
var aData = createSampleData();
//Define fragment init data
oController.fragmentInitData = {       title: "This is a Table Fragment Example",       data : aData,       columns: [{title: 'Last Name', bindingPath: 'lastName', type: "Text"},                            {title: 'Name', bindingPath: 'lastName', type: "Text"},                            {title: 'Selected', bindingPath: 'checked', type: "Checkbox"},                            {title: 'Rating', bindingPath: 'rating', type: "Rating"}]   };
//Instantiate fragment.Table with this init data "oController.fragmentInitData"
var table1 = sap.ui.jsfragment("fragment.Table", oController);
//Define some sample data again
var aData2 = createSampleData();
//Define fragment init data. Different init data this time (different title and less columns).
oController.fragmentInitData = {       title: "This is a Table Fragment Example 2",       data : aData,       columns: [{title: 'Last Name', bindingPath: 'lastName', type: "Text"},                            {title: 'Name', bindingPath: 'lastName', type: "Text"}]   };
//Instantiate fragment.Table with this init data "oController.fragmentInitData"
var table2 = sap.ui.jsfragment("fragment.Table", oController);
var oDivider1 = new sap.ui.commons.HorizontalDivider("divider1");
oDivider1.setHeight(sap.ui.commons.HorizontalDividerHeight.Large);
//Present data into VerticalLayout
var oLayout = new sap.ui.layout.VerticalLayout({       content: [table1, oDivider1, table2]
})

Result

 

fragments_2.png

 

EDIT: JSBin Example

 

We could reuse our JS code with functions (for example: createTable()), protyping "classes", etc. This is a different SAPUI5 standard way to create common Fragments to be reused in our apps as the same way with "Views".

 

Enjoy!

UI5 Application: Approaches for Performance Improvement

0
0

I was recently asked by a customer to review an UI5 application and suggest improvements. One of its main complaint was performance. I thought of blogging my experience here so that it may be useful to fellow community members. So here it goes.

 

Reason for the slow performance can be put into two broad buckets.

1. Application: Application itself taking log time to respond, meaning slowness due to javascript code and libraries

2. Services: Slow performance of the data supplying services (REST services)

 

So it is important to identify the issues to find out the bottleneck and take actions.

 

The first step would be to open Chrome Development Tool and run the application. Go to Network Tab

 

See the below screenshot

F12.PNG

 

All files starting with ..../resources/sap/ are SAPUI5 library files. As you can see loading sap-ui-core.js itself has taken 1.52 seconds. SAPUI5 library is a heavy library and loading of this depends on the libraries you choose in your index file.

 

See the snippet below

libraries.PNG

 

In the above snippet, I have used controls both from desktop and mobile libraries hence browser has to load both the libraries which will delay your application load. Ensure that you do not have unwanted modules mentioned here.

 

Advice 1: It is better to avoid mixing the controls and also ensure that only required modules are specified in index file.

 

It is also possible that you might be using many third party libraries. Trying to load all of them initially (synchronously) would make your initial application load too slow. In such cases load libraries when required and also consider asynchronously loading libraries.

Read on how to load libraries asynchronously here written by Konstantin Anikeev.


Other options to explore are minification, and compression of files by the server before sending. Note that SAP Netweaver system's ICM component supports GZIP compression.


Advice 2: Explore asynchronous loading of JS libraries. Also think about distributed library loading using jQuery.sap.require(sModuleName, fnCallback), minification and compression.

 

You may also explore using cache buster mechanism to cache your application's resources permanently. This feature seems to be useful for static resources like fonts, icons, images etc. (new thing to try!)

 

Both the above advises will speed-up the initial application load. Since the browser caches these files, further loading of the application does not get impacted by this.

 

Let us look at the second bucket of reasons. REST Services

We will consider REST services from SAP Netweaver Gateway system here.

 

In the application I was reviewing, I saw that there was an extra call to a Collection before every Insert/Update/Delete action. That was for getting the CSRF token. Calling a data intensive Collection just to get a CSRF token is not a right approach. Instead a READ should be employed, so that there is less load on the system and network. Also note that if you use methods of sap.ui.model.odata.ODataModel, you can completely avoid this call. When you create an instance of ODataModel, it fetches the CSRF token and automatically handles refetching and supplying with edit operations.

 

In Chrome Developer Tool, in the Network tab you can also see REST service calls. The other option is to use Gateway Performance Trace. Goto Transaction Code '/IWFND/TRACES' in your Hub system. Activate the trace for the user, perform an operation on your application and find out number of calls to the OData service.

 

So here comes Advice 3: To fetch the CSRF token, use a least resource intensive call. Think about using ODataModel to avoid this call all together.

 

The other aspect with REST services is the amount of data fetched as well as sent.

Think about a data table, or a list where you are showing a array of data. At a time you may not be showing more than 100 rows for example. So it makes sense to use a $top=100 system query option. If you use UI5 APIs this will be default many a time. You need to ensure that the implementation of $top is right and efficient.

In the application I was reviewing, I saw that a Select was done fetching more than 20,000 entries, an authorization check made on them to filter those records and then only 100 records were returned. That was so non-efficient!!

 

So the Advice 4: Ensure that right amount of data is fetched and transmitted. Make sure to use $top and paging options and implement them right in the service.

 

Let me know your comments and experiences.


Retina support for sap.m.Image

0
0

Found an interesting feature in OpenUI5.

 

If you use a device with retina display and component sap.m.Image with src="your_image.png" OpenUI5 tries additionally to load "your_image@2.png" and show it instead of original image.

 

From official Doku:

Density related image will be loaded if image with density awareness name in format [imageName]@[densityValue].[extension] is provided. The valid desity values are 1, 1.5, 2. If the original devicePixelRatio isn't one of the three valid numbers, it's rounded up to the nearest one.

That's cool!!!

SAPUI5 and hidden gems of the SDK

0
0

One of the great benefits of downloading the SDK locally is the vast amount of sample code in the SDK that is available.


Some of these samples are hidden away, starting here test-resources\sap\m will uncover some of these.


The first one is the demokit folder, which contains the apps you can see running when you look at the SDK and Demp Apps tab.


These include the below samples

  • cart
  • poa
  • mvc
  • splitapp

 

At the root level of test-resources\sap\m, we have sample pages for each control, and numerous script examples of different configurations and usages.  "A picture is worth a thousand words" I think the same goes for code

 

And under the folder called quint we have examples of QUnit for each control as well, so a great way to see how you can use QUnit for testing your new applications and web pages.

 

Hope this helps with the learning.

Needfull Things around UI5

0
0

Since SAP has open SAPUI5 under an Apache Open Source licence i have created a blog site to share some knowledge to the community.

 

The Blog can be found here:

OpenUI5 Developer

 

I put together all the needed things around UI5 development.

 

There will be a lot of future post concerning

- 4-Way-Model-Binding using Web Sockets

- Automatic ValueHelps without coding needs

- Generic Class Value Selector

- LOVC Variant Configuration Service as UI5 Widget without IPC

- ...

 

On sepcial section is about using node.js with OpenUI5.

 

This site is for SAPUI5 and OpenUI5 developer.

 

Maybe you will have a look at it and will find something useful.

 

We just started migrating our Sencha HTML5 KnowHow from the last years to UI5.

 

Will be a very interessting year.

 

Cheers Holger

UI5 Mobile SplitApp Boilerplate

0
0

There are different approaches to start a new project with SAP UI5 or OpenUI5 (let's call it simply UI5). One way is to use the SAP UI5 Eclipse Tools described in the blog post "SAP UI5 – Quickstart".  The generated UI5 Application Project is very, very basic and you are bound to Eclipse to do your development, which is not everybody's favorite IDE of course.

 

On the other hand there are already some really nice UI5 Tools, which can help you. For example the Yeoman generator for OpenUI5, which is based on node.js and can generate a nice application structure or only single components. Another example is the Sublime Text 2 Package for UI5/OpenUI5, which contains Snippets and Templates and is very useful.

 

So whatever your favorite IDE is, either Eclipse, Sublime Text, WebStorm, IntelliJ, Xcode, Visual Studio, emacs, notepad or vi, you should be able to use Git and clone the UI5 Mobile SplitApp Boilerplate from GitHub (if not, you should try hard) resulting in a nice app structure with base features, which can be used as starting point for development:

 


Get UI5 Mobile SplitApp Boilerplate from Github


The Boilerplate can be found under the following

Github Location: https://github.com/6of5/UI5SplitApp-Boilerplate

 

Open a terminal/console and run the following command to clone the source to your local file system.

 

git clone https://github.com/6of5/UI5SplitApp-Boilerplate.git

A new folder "UI5SplitApp-Boilerplate" is generated which contains the app source code with the following file structure:

UI5BoilerplateFileStructure.png

Run UI5 SplitApp with Node.js


Assuming you have node.js (with npm) already installed on your local computer, you now have to do the following steps within the "UI5SplitApp-Boilerplate" folder. This will install express and open as node modules and finally  start a static server web server (More Infos on SCN Blog Post from John Patterson):


npm install express
npm install open
node static_server.js

You now can access the App unter the following URL:  http://localhost:8877/UI5SplitApp-Boilerplate/

SplitAppinChrome.png

Run directly from file system (in Chrome)

The most easiest way without the need of a web server, is running the app directly from the file system. Therefore you need to start Chrome with some start parameters to disable web security and enable File Access (which is ok while development):

 

 

On Windows:

chrome.exe --disable-web-security --allow-file-access-from-files

On Mac:

open /Applications/Google\ Chrome.app --args --disable-web-security --allow-file-access-from-files

With this in place you can open the index.html from the file system and start the app via Chrome Menu File -> Open File.

 

In a next post I will explain how to modify the template and extend it. And which basic features are provided.

SAPUI5 and hidden gems of the SDK

0
0

One of the great benefits of downloading the SDK locally is the vast amount of sample code in the SDK that is available.


Some of these samples are hidden away, starting here test-resources\sap\m will uncover some of these.


The first one is the demokit folder, which contains the apps you can see running when you look at the SDK and Demp Apps tab.


These include the below samples

  • cart
  • poa
  • mvc
  • splitapp

 

At the root level of test-resources\sap\m, we have sample pages for each control, and numerous script examples of different configurations and usages.  "A picture is worth a thousand words" I think the same goes for code

 

And under the folder called quint we have examples of QUnit for each control as well, so a great way to see how you can use QUnit for testing your new applications and web pages.

 

Hope this helps with the learning.

Using openUI5 on Force.com

0
0

openUI5 is a nice library with a lot of predefined controls and elements. It is a Javascript library, so it can run on a multitude of platforms, including Force.com. One VisualForce page as an index which kicks off everything and then the UI5 library takes over. And this is exactly what we did in a three-hour hackathon in teams of three.

Our challenge was to use:

 

  • openUI5
  • Force.com
  • The SAP Flight demo data and services in NW Gateway

 

We needed to mix these to make a mobile (-first and multi-device) application that would give its user an overview of the flights available, preferably searchable, and add a detail page for each flight with on the page an option to book the flight and exchange instant messages with other users that booked the flight.

This makes for an interesting combination of platforms, systems, and techniques to combine into a working app. Not an easy task, even if each team had a SAP developer, a Force.com developer, and a UX designer on hand.

 

Proceedings:

We were set-up with a working NW Gateway implementation for the flight data, so we had a oData service from which to get the information, and the outline of a connector to read the NW Gateway data into Force.com.

 

The oData service was well made, but the Force.com connector still needed some implementation as it needed to be hooked up to the models we had to create ourselves. Rather than stall our development while one of us implemented the connector, we created a dump of the data and inserted this into the models; when to build the real link, we didn’t yet know.

 

This made it possible to iterate quickly and speeded up the development on the front-end a lot, as we now already knew (roughly) what format the data would have. Then we split up development into the front-end (openUI5), integration of the front-end into Force.com, and tidying up the back-end and the SAP link. My task was the front-end.

 

Screen Shot 2014-01-24 at 8.41.20 .png

Front end: openUI5 development

To make it possible to use openUI5 on Force.com, you will need the libraries. These are publicly available in complete and mobile packages. But even when they’re zipped, they’re too big to upload to a developer environment as there is a restriction on resources of 10MB there. The zips supplied are 15M and 30MB respectively. The way to resolve this is to remove the *-dbg.js files as they are the debug versions of the controls and are very useful for development, but are not needed in production.

 

Then there is the necessity to get the data from the models of Force.com into the javascript models of UI5. For this you can use VisualForce remoting, which makes it possible to call functions in the controller and get the returned values in javascript.

From there on it is just the same as any UI5 app, and we can use a SplitApp with master-detail setup to show a list of flights on ObjectListItems, fill the details with all the data we want from the selected flight, and add a button that calls the oData service to make a booking.

Unfortunately three hours was not enough (for us) to also add the ability to chat with other users, but maybe the chatter functionality of Force.com can be used for this. We need to leave some stuff to improve on.

How to add beautiful notifications to UI5


Simple node.js chat server example using UI5 WebSocket

0
0

Today i will show you an example using node.js to provide a simple chat server to play around with the WebSocket API sap.ui.core.ws.WebSocket that is part of UI5.

 

To offer some useful tricks and features i blowed up the example with the following features:

 

  • 5-Way-Model-Binding
  • WebSocket based data connectivity
  • adding 3rd party notification library (noty)
  • JSON stringify and parsing

 

The article can be found at

 

OpenUI5 Developer: Simple chat server example using UI5 WebSocket

 

Cheers Holger

OpenUI5 Meets Google App Engine

0
0

I have been playing around with Google App Engine recently and, because the SAPUI5 library has recently gone open source, I have decided to introduce the two together and share my experiences. In order to do so, I have created a basic CRUD application that will manage Employee information. You can view the entire example on GitHub, this blog will just showcase the most relevant pieces to shorten the blog.

 

Backend

Google App Engine is a cloud computing platform as a service for developing and hosting web applications through Google's infrastructure.

 

Employee JDO Class

I am making use of Java and JDO to access the datastore (there are many other ways of achieving this on Google App Engine).

 

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class Employee {
// ...
// Private persistent class attributes
// ...  public Employee(String firstName, String lastName, String phoneNumber, String cellNumber, String email, String idNumber, String country, String city) {       this.key = KeyFactory.createKey(Employee.class.getSimpleName(), email);       this.firstName = firstName;       this.lastName = lastName;       this.phoneNumber = phoneNumber;       this.cellNumber = cellNumber;       this.email = email;       this.idNumber = idNumber;       this.country = country;       this.city = city;  }
// ...
// Public Getters and Setters


// ...

Employee Endpoint Class

I have made use of the Google Cloud Endpoint Service to easily generate my web backend API and JavaScript client libraries. I have decided to only post the method and API access names here for readability and to shorten the blog, you can view the complete demo on Github.

 

@ApiMethod(name = "listEmployee")
public CollectionResponse<Employee> listEmployee(@Named("cursor") String cursorString, @Named("limit") Integer limit)
@ApiMethod(name = "getEmployee")public Employee getEmployee(@Named("email") String email)
@ApiMethod(name = "insertEmployee") // Update employee is the same
public Employee insertEmployee(
@Named("firstName") String firstName,  @Named("lastName") String lastName,  @Named("phoneNumber") String phoneNumber,  @Named("cellNumber") String cellNumber,  @Named("email") String email,  @Named("idNumber") String idNumber,  @Named("country") String country,  @Named("city") String city )
@ApiMethod(name = "removeEmployee")
public void removeEmployee(@Named("email") String email)

By making use of the Google Plugin for Eclipse, I am easily capable of generating client libraries for iOS, Android and specifically JavaScript.

Connecting to the backend

In order to access my endpoints and use them in my OpenUI5 JavaScript application, I need to call the Google APIs JavaScript Client Library and load the endpoint.

Endpoint Loader - Custom Class

I have created a custom OpenUI5 class that will take care of loading the client library and allow easy access throughout the application:

jQuery.sap.declare("Endpoint");
jQuery.sap.require("sap.ui.base.ManagedObject");
sap.ui.base.ManagedObject.extend("Endpoint", {       metadata: {            properties: {                 "root": {type: "string", defaultValue: "http://127.0.0.1:8888/_ah/api"},                 "gapiObject": "object",                  "library": "object",             }         },         init: function() {              // Gets called right after instantiation         },         successDialog: new sap.m.Dialog({                 title: "Endpoint Client Libraries Successfully Loaded.",                 leftButton:  new sap.m.Button({                 text: "Close",                 state: sap.ui.core.ValueState.Success,                 press: function(oControlEvent) {                         if( oControlEvent.getSource().getParent().isOpen() ) {                                oControlEvent.getSource().getParent().close();                           }                 }            }),  }),    createLibrary: function() {          var gapis = this.getGapiObject();          var successDialog = this.successDialog;          var simpleForm    = new sap.ui.layout.form.SimpleForm({                   content: [ new sap.ui.core.Title({ text: "Success!" }) ]         });         successDialog.addContent(simpleForm);          var response = function(endpointName) {              console.log(endpointName + " has been loaded.");              simpleForm.addContent( new sap.m.Label({ text: "Successfully Loaded" }) );              simpleForm.addContent( new sap.m.Text({ text: endpointName }) );               if( !successDialog.isOpen() ) {                   successDialog.open();              }         };          for( k = 0; k < gapis.length; k++ ) {              // Load the client libraries through googles api object (Connects to the endpoint classes)              gapi.client.load(gapis[k].name, gapis[k].version, response(gapis[k].name), this.getRoot());              // Set the google api client to the library object for easy access               this.setLibrary(gapi.client);          }          return this.getLibrary();    },    getEndpoint: function(name) {          return this.getLibrary()[name];    }

Custom Application Class

The project follows the MVC principles as discussed in the developers guide. I have made a few changes to the application class and the way it is loaded in the index.html file.

jQuery.sap.declare("Application");
jQuery.sap.require("sap.ui.app.Application");
jQuery.sap.require("Endpoint");
sap.ui.app.Application.extend("Application", {       init: function() {            // Set global models...       },       main: function() {            // Create app view and set to the root html element            var root = this.getRoot();            var application = sap.ui.jsview("application", "js.Application");            application.placeAt(root);            // Connect to Google App Engine Endpoints            var library = new Endpoint({                 id: "clientEndpoint",                 gapiObject: [                     { name: "employeeendpoint", version: "v1" }  // Allows for multiple endpoints to be loaded                 ]            }).createLibrary();  // Create and return the loaded client library            // Attach the client library to the application for easy access            application.setClientEndpointLibrary( library );       }
});

Index.html

<!DOCTYPE html><html lang="en"><head>      <meta charset="UTF-8">      <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">      <meta http-equiv="X-UA-Compatible" content="IE=edge">       <title>OpenUI5 On Google App Engine</title>      <script id="sap-ui-bootstrap"            src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"            data-sap-ui-theme="sap_bluecrystal"            data-sap-ui-libs="sap.ui.commons, sap.m"></script>      <script>            // Application Content...            sap.ui.localResources("js");            // Register Module Paths            jQuery.sap.registerModulePath("Application", "Application");            jQuery.sap.registerModulePath("Endpoint", "Endpoint");      </script>      <script type="text/javascript">            var initializeGapi = function() {                 // Launch Application...                 jQuery.sap.require("Application");                 var oApp = new Application({ root: "content" });            }      </script>      <script type="text/javascript" src="https://apis.google.com/js/client.js?onload=initializeGapi"></script></head><body class="sapUiBody">      <div id="content"></div></body></html>

I have incorporated the loading of the endpoint classes within the OpenUI5 application in order to reduce the time the user has to wait for the client library to load. If I had not done so, the user interface would load first and, depending on where the Google API load function is called, the data would not be available to query, leaving the user with an application that contains empty data.

 

Frontend - The Application

When the application is started and the main function is called, the endpoint classes are loaded and a message is displayed to the user. In this case I have just called a dialog to show that the endpoints have been loaded, but I have not included a check to see whether or not it was actually successful, despite calling it the "successDialog".

05.png

Creating an Employee

In the CreateEmployee.controller.js I am assigning the loaded employeeEndpoint to the controller and calling a method that will use the InsertEmployee method of the endpoint class:

onBeforeRendering: function() {     // Assign the employee endpoint to this controller to use      this.employeeEndpoint = this.getView().getParent().getParent().getParent().library.employeeendpoint;
},

I have called the endpoint library from the application by navigating through the relationships. There might be a better method of achieving this and I hope to improve on this solution in time, I welcome any suggestions and feedback.

save: function() {       var resultDialog = this.resultDialog;       // Inputs       var firstName   = this.getView().firstName;       var lastName    = this.getView().lastName;       var city        = this.getView().city;       var country     = this.getView().country;       var phoneNumber = this.getView().phoneNumber;       var cellNumber  = this.getView().cellNumber;       var email       = this.getView().email;       var clearInputs = function() {            firstName.setValue("");            lastName.setValue("");            city.setValue("");            country.setValue("");            phoneNumber.setValue("");            cellNumber.setValue("");            email.setValue("");       }       var employee = {            firstName: firstName.getValue(),            lastName: lastName.getValue(),            city: city.getValue(),            country: country.getValue(),            phoneNumber: phoneNumber.getValue(),            cellNumber: cellNumber.getValue(),            email: email.getValue()       };       this.employeeEndpoint.insertEmployee(employee).execute(function(response) {            var resultMessage = new sap.m.Text({});            if( !response.code ) { // No Error                 if( !resultDialog.isOpen() ) {                      resultDialog.setTitle("Success");                      resultDialog.setState(sap.ui.core.ValueState.Success);                      resultMessage.setText("Successfully saved the employee to the datastore.");                      resultDialog.addContent(resultMessage);                      resultDialog.open();                      clearInputs();                 }            } else  { // Error                 if( !resultDialog.isOpen() ) {                      resultDialog.setTitle("Error");                      resultDialog.setState(sap.ui.core.ValueState.Error);                      resultMessage.setText(response.message);                      resultDialog.addContent(resultMessage);                      resultDialog.open();                         clearInputs();                 }            }       });  }

03.png

04.png

Reading Employees

 

listEmployees: function() {       var controller = this;       var listEmployee = this.getView().listEmployee;       this.employeeEndpoint.listEmployee().execute(function(response) {            var jsonModel = new sap.ui.model.json.JSONModel(response);            listEmployee.setModel(jsonModel);            var template = new sap.m.ObjectListItem({                 title: "{key/name}",                 type: sap.m.ListType.Navigation,                 press: [controller.toGetEmployee, controller]            });            listEmployee.bindItems("/result/items", template);       });  }

06.png

The data is stored in Google App Engines Datastore as shown below:

07.png

Deleting Employee

  deleteEmployee: function() {       var employee = {            email: this.getView().email.getText()       };       this.employeeEndpoint.removeEmployee(employee).execute(function(response) {            // Response...       });  }

 

The OpenUI5 Library is still so young and yet so feature-rich, it excites me to see what other people are, and have already, come up with. This is my first blog post on SCN and as I have mentioned before, I welcome all feedback and suggestions and even if it is just a discussion on other technologies, I would love to engage in them with you.

 

Complete solution can be found on GitHub.

 

Kind regards

Miki von Ketelhodt

Getting more out of your JavaScript unit tests with strict mode

0
0

JavaScript has many pitfalls. With strict mode you can detect some bugs earlier. Strict mode makes several changes to normal JavaScript semantics:

  • First, strict mode eliminates some JavaScript silent errors by changing them to throw errors. E.g. makes it impossible to accidentally create global variables. In normal JavaScript mistyping a variable in an assignment creates a new property on the global object and continues to "work".
  • Second, strict mode prohibits the use of keywords which are likely to be defined in future versions of ECMAScript, such as ‘implements’, ‘private’, and ‘protected’.


If you start a new application, you can switch on strict mode from the beginning.

For more information about strict mode see:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode

 

But applications with an existing codebase you should be careful. Since some of the errors are only detected during runtime, switching to strict mode for your existing productive code without testing everything can be risky. If you cannot ensure all code is touched by automated tests, you might not want to enable strict mode for your productive code. Instead you can use the strict mode for executing your unit tests to detect some bugs earlier without putting strict mode in the productive implementation.


How to? You can use a self-executing anonymous function to put your test in strict mode. Douglas Crockford describes the reasons for using the self-executing function in his blog post: http://www.yuiblog.com/blog/2010/12/14/strict-mode-is-coming-to-town/


(function() {

  "use strict";

  module( "Assign List", {

   setup : function() {

   },

  });

  test(){

  }

  ..

}());


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

XML Views and Resource Bundle Declarations

0
0

Just a quick post on the train on the way down to London this morning.

 

The other day, Andreas Kunz pointed to an overview of the MVC options which contains very detailed information - an interesting and recommended read. One of the things that piqued my interest was the ability, in XML views, to specify a resource bundle (for internationalisation) declaratively, using a couple of attributes of the root View element. This I thought was rather neat.

 

So further to my recent explorations and posts on XML views ...

 

Mobile Dev Course W3U3 Rewrite - XML Views - An Intro

Mobile Dev Course W3U3 Rewrite - XML Views - An Analysis

UI5 XML Views - Another Example

 

... I thought I'd put together a little runnable app and make it available on sapui5bin, to demonstrate it. The result is XMLResourceBundleDeclaration, which is an index file, instantiating an XML view that has the resourceBundle declaration in it; this points to the resourceBundle.properties file in the i18n folder where you might expect to find it in other apps too.

 

The runnable is here: https://github.com/qmacro/sapui5bin/tree/master/XMLResourceBundleDeclaration

 

Screen Shot 2014-01-28 at 08.08.17.png

 

Share and enjoy!

How I made highlight of specific values in Table

0
0

The challenge was as follows: it was necessary to select from the database information to compare the values ​​in the columns (sent and confirmed) in the case where the values ​​were different from each other - it was necessary to simplify to highlight search problematic records.

 

 

For a start create controller and define model (messagesSearchResult) to save result of query. Also define url to query (searchMessages).

 

sap.ui.controller("controller_name.page", {    models: {        messagesSearchResult: null    },    urls: {        searchMessages: "/XMII/Illuminator?QueryTemplate=PATH/TO/query&Content-Type=text/xml"    },

 

Then create init-function, where create XML-object (new sap.ui.model.xml.XMLModel()) and connect functions for started, completed and failed request.

 

 

    onInit: function() {      this.models.messagesSearchResult = new sap.ui.model.xml.XMLModel();      this.models.messagesSearchResult.attachRequestCompleted(this._dataLoadingFinished)      this.models.messagesSearchResult.attachRequestFailed(this._dataLoadingFinished)      this.models.messagesSearchResult.attachRequestSent(thisLoadingStarted);    },

 

Next step, create main function where binding request data with Table control and connect function to manipulation with request data.

 

    searchMessages: function() {      var t = this.byId('searchResultTbl'); // get Table element from page      t.setModel(this.models.messagesSearchResult); // connect XML-model to Table element at page
// aggregation binding data from XML-path (/Rowset/Row) to Table rows
// and manipulation with data by function _addTableRows(this.models.messagesSearchResult).
// At the end, loading data from query by url. (this.models.messagesSearchResult.loadData())      t.bindAggregation("rows",      {          path: "/Rowset/Row",          factory: $.proxy(this._addTableRows(this.models.messagesSearchResult), this)      });      this.models.messagesSearchResult.loadData(this.urls.searchMessages, {}}, false, "POST");    },

 

Finally, function to manipulate data and highlight 'problem" cells.

 

    _addTableRows: function (oContext) {      var _this = this; // save handler _this to controller      var backgroundColor = '#fcdd82'; // define background-color for "problem" cells      var ConfRecColumn, SentRecColumn;      var TMP_REC;      // Compare this field with next.      // Bind property (CONF_REC) from XML-model to new TextField value and save it to temporary variable (TMP_REC).      // By jQuery set attribute readonly to true ($('#' +  this.getId()).attr("readonly", true)).      // Set this TextField not editable (this.setEditable(false)).      var ConfRecColor = new sap.ui.commons.TextField().bindProperty("value", "CONF_REC", function(cellValue){             $('#' +  this.getId()).attr("readonly", true);        this.setEditable(false);        _this.TMP_REC = cellValue;        return cellValue;      });      // Compare this field with previous and highlight if doesn't match.      // Bind property (SENT_REC) from XML-model to new TextField value and compare it with temporary variable (TMP_REC).      // By jQuery set background-color if doesn't match ($('#' +  this.getId()).parent().parent().css("background-color", backgroundColor)).      // Or remove by jQuery attribute style if previous and this values is match ($('#' +  this.getId()).parent().parent().removeAttr('style')).      // By jQuery set attribute readonly to true ($('#' +  this.getId()).attr("readonly", true)).      // Set this TextField not editable (this.setEditable(false)).      var SentRecColor = new sap.ui.commons.TextField().bindProperty("value", "SENT_REC", function(cellValue){        if(cellValue != _this.TMP_REC)        {          $('#' +  this.getId()).parent().parent().css("background-color", backgroundColor);        }        else        {          $('#' +  this.getId()).parent().parent().removeAttr('style');        }        $('#' +  this.getId()).attr("readonly", true);        this.setEditable(false);        return cellValue;      });      this.byId('searchResultTbl').getColumns()[11].setTemplate(ConfRecColor); // set template, which we prepare above ConfRecColor      this.byId('searchResultTbl').getColumns()[12].setTemplate(SentRecColor); // set template, which we prepare above SentRecColor    },

 

At the end define functions for started, completed and failed request.

 

    _dataLoadingStarted: function() {      sap.ui.core.BusyIndicator.show();    },    _dataLoadingFinished: function(oEvent) {      sap.ui.core.BusyIndicator.hide();      if (oEvent.getId() == "requestFailed") {        sap.ui.commons.MessageBox.alert(oEvent.getParameter('message'));      }    }
}); // close controller body

Demonstration how it works (blue and red blocks combined cells, wich must be identical).

 

1.JPG

 

Full controller script: at JSFiddle

Viewing all 789 articles
Browse latest View live




Latest Images