Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member193140
Active Participant

In this blog, I would like to share my experience on how to develop a simple SAP mobile app using Sencha Touch with view, update, create and delete operations.

The objective of this app is to be able to view, update, create and delete a record in TravelagencyCollection-RMTSAMPLEFLIGHT that is published by OData service SAP Netweaver Gateway Service Consumption System.

Required Components

We would need the following components in order to build the app:

  1. Sencha Touch framework from http://www.sencha.com
    Download this framework. We will write the code using this framework.
  2. Sencha Touch OData Connector for SAP
    Download the connector from https://market.sencha.com/extensions/sencha-touch-odata-connector-for-sap to connect  to SAP OData available services. File we need is OData.js.
  3. Apache
    We will host the code on Apache web server and we will configure the reverse proxy to resolve the "same-origin policy" issue.
    Download Apache with SSL enabled  from http://httpd.apache.org/download.cgi
  4. Get an account from SAP Netweaver Gateway Demo system
    Register an account from https://supsignformssapicl.hana.ondemand.com/SUPSignForms

        We need this account to be able to use the sample service. In this case is RMTSAMPLEFLIGHT.

Configure Reverse Proxy

The first step that you need to do after installing  Apache web-server is to configure reverse proxy. I am not going into detail on how to install the Apache web server. In my configuration, I am using the Apache with SSL enabled.


Open your httpd.conf under Apache conf folder and uncomment the following lines:


LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule headers_module modules/mod_headers.so
LoadModule ssl_module modules/mod_ssl.so






































































Also add the following lines:


Header set Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "Content-Type"
Header add Access-Control-Allow-Headers "X-Requested-With"
SSLProxyEngine "On"
RequestHeader set Front-End-Https "On"
ProxyPass /sapgw/ https://sapes1.sapdevcenter.com/
ProxyPassReverse /sapgw/ https://sapes1.sapdevcenter.com







































































In the URL connection string, we need to add the /sapgw/ to enable the reverse proxy, for example:

http://ASPSGPLR81BC0G/sapgw/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/TravelagencyCollection

Replace ASPSGPLR81BC0G with your computer name.

Any request to http://ASPSGPLR81BC0G/sapgw, will be directed by Apache to https://sapes1.sapdevcenter.com/

Let's open this link from Chrome browser and you will get the result:


Page Structure


We will build three simple pages as follow:

  • Travel Agency List.
    On this page, user can view the list of travel agency with a button link to the Detail page.

  • Detail.
    On the Detail page, user can update or delete the record. They cannot update the Agency No field.


  • Add Travel Agency.
    On this page, user can add the new record. The Agency No is a required field.

Code Structure

As you can see in the below structure, we will build the MVC structure. You need to copy the OData.js (from Sencha Touch OData Connector from SAP) , sencha-touch-all.js and resources (from Sencha Touch) to the root folder, TravelAgency.

Let's walk through the important files,

    • View: app.js

      We register the controller, model, store and views in app.js.


      controllers: ["TravelAgency"],
      models: ["TravelAgency"],
      stores: ["TravelAgency"],
      views: ["Viewport",
      "MainPanel",
      "TravelAgencyListContainer",
      "TravelAgencyEditor"
      "AddTravelAgentContainer",
      "AddTravelAgentMenu"
      ],






































      PageCorresponding View
      Travel Agency ListTravelAgencyList
      Detail PageTravelAgencyEditor
      Add Travel AgencyAddTravelAgentMenu

    • Model  and Store: TravelAgency.js
      In the model and store TravelAgency.js, we define all the required fields and URL connection in the proxy.

      fields: [
      { name: "agencynum" },
      { name: "NAME" },
      { name: "STREET" },
      { name: "POSTBOX" },
      { name: "POSTCODE" },
      { name: "CITY" },
      { name: "COUNTRY" },
      { name: "REGION" },
      { name: "TELEPHONE" },
      { name: "URL" },
      { name: "LANGU" },
      { name: "CURRENCY" },
      { name: "mimeType" }
      ],
      proxy: {
      type: 'odata',
      enablePagingParams: false,
      withCredentials: true,
      username: 'P1940517116',
      password: 'Password',
      url: "//ASPSGPLR81BC0G/sapgw/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/TravelagencyCollection",
      }

















    • View: TravelAgencyList.js
      To display out the Agency No (agencynum) and Agency Name (NAME) based on the data obtained from store TravelAgency and put a button link to the Detail page, onItemDisclosure


      config: {
      store: "TravelAgency",
      itemTpl: [
      '<div>',
      '<div>Agency No: {agencynum}</div>',
      '<div>{NAME}</div>',
      '</div>',
      ],
      onItemDisclosure: function(record,btn,index) {
      this.fireEvent("editTravelAgencyCommand", record);
      }
      },

















    • View: TravelAgencyEditor.js
      It will display Name, Street, Post Box, Post Code, City, Country, Region, Telephone, URL, Language and Currency based on the given agencynum.  We also put the Save (onSaveTap) and Delete (onDeleteTap) functions on this view.


      items: [
      { name: "NAME", label: "Name" },
      { name: "STREET", label: "Street" },
      { name: "POSTBOX", label: "Post Box" },
      { name: "POSTCODE", label: "Post Code" },
      { name: "CITY", label: "City" },
      { name: "COUNTRY", label: "Country" },
      { name: "REGION", label: "Region" },
      { name: "TELEPHONE", label: "Telephone" },
      { name: "URL", label: "URL" },
      { name: "LANGU", label: "Language" },
      { name: "CURRENCY", label: "Currency" }
      ],
      onSaveTap: function(button,e,options) {
      this.fireEvent("saveCommand", this.getRecord(), button);
      },
      onDeleteTap: function(button,e,options) {
      this.fireEvent("deleteCommand", this.getRecord(), button);
      ),



















    • View: AddTravelAgentMenu.js

      We populate the text fields for user to enter the Agency No, Name, Street, Post Box, Post Code, City, Country, Region, Telephone, Language, Currency, URL and add the Add (onFormAdd) function.


      items: [
      {
      xtype: 'textfield',
      label: 'Agency No',
      labelWrap: true,
      labelWidth: '30%',
      name: 'agencynum',
      placeHolder: 'Enter Agency No',
      },
      {
      xtype: 'textfield',
      label: 'Name',
      labelWrap: true,
      labelWidth: '30%',
      name: 'NAME',
      placeHolder: 'Enter Name'
      },
      {
      xtype: 'textfield',
      label: 'Street',
      labelWidth: '30%',
      name: 'STREET',
      placeHolder: 'Enter Street'
      },
      {
      xtype: 'textfield',
      label: 'Post Box',
      labelWidth: '30%',
      name: 'POSTBOX',
      placeHolder: 'Enter Post Box'
      },
      {
      xtype: 'textfield',
      label: 'Post Code',
      labelWidth: '30%',
      name: 'POSTCODE',
      placeHolder: 'Enter Post Code'
      },
      {
      xtype: 'textfield',
      label: 'City',
      labelWidth: '30%',
      name: 'CITY',
      placeHolder: 'Enter City'
      },
      {
      xtype: 'textfield',
      label: 'Country',
      labelWidth: '30%',
      name: 'COUNTRY',
      placeHolder: 'Enter Country'
      },
      {
      xtype: 'textfield',
      label: 'Region',
      labelWidth: '30%',
      name: 'REGION',
      placeHolder: 'Enter Region'
      },
      {
      xtype: 'textfield',
      label: 'Telephone',
      labelWidth: '30%',
      name: 'TELEPHONE',
      placeHolder: 'Enter Telephone'
      },
      {
      xtype: 'textfield',
      label: 'Language',
      labelWidth: '30%',
      name: 'LANGU',
      placeHolder: 'Enter Language'
      },
      {
      xtype: 'textfield',
      label: 'Currency',
      labelWidth: '30%',
      name: 'CURRENCY',
      placeHolder: 'Enter Currency'
      },
      {
      xtype: 'textfield',
      label: 'URL',
      labelWidth: '30%',
      name: 'URL',
      placeHolder: 'Enter URL'
      },
      ],
      onFormAdd: function(button,e,options){
      var formObj = button.up('addtravelagentmenu');
      var formData = formObj.getValues();
      this.fireEvent("addCommand", this.getRecord(), button);
      }



















    • Controller: TravelAgency.js

      On controller, we define the method for save, delete and update record.

      Update Method
      We need to specify an id field in order to update the record based on agencynum.

      onSaveButton: function(record, button) {
      travel = Ext.create('TravelAgency.model.TravelAgency', {
      id : "//ASPSGPLR81BC0G/sapgw/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/TravelagencyCollection('" +
      agencynum: record.get('agencynum'),
      });
      var formObj = button.up('travelagencyeditor');
      var formData = formObj.getValues();
      travel.set('agencynum', formData.agencynum);
      travel.set('NAME', formData.NAME);
      travel.set('STREET', formData.STREET);
      travel.set('POSTBOX', formData.POSTBOX);
      travel.set('POSTCODE', formData.POSTCODE);
      travel.set('CITY', formData.CITY);
      travel.set('COUNTRY', formData.COUNTRY);
      travel.set('REGION', formData.REGION);
      travel.set('TELEPHONE', formData.TELEPHONE);
      travel.set('URL', formData.URL);
      travel.set('LANGU', formData.LANGU);
      travel.set('CURRENCY', formData.CURRENCY);
      travel.save(function (record, operation) {
      console.log(record);
      console.log(operation);
      if (operation.getError()==null) {
      console.log('record udated. Id:' + record.get('agencynum'));
      Ext.Msg.alert('Status', "Record updated");
      } else {
      console.log('Create record failed');
      Ext.Msg.alert('Status', operation.getError());
      }
      var store = Ext.getStore("TravelAgency");
      store.loadPage(1);
      });
      },


















      Delete Method
      Similar to Update method, we need to specify an ID (agencynum) to delete the record.

      onDeleteButton: function(record, button) {
      mp = this.getMainPanel();
      travel = Ext.create('TravelAgency.model.TravelAgency', {
      id : "//ASPSGPLR81BC0G/sapgw/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/TravelagencyCollection('"
      + record.get('agencynum') + "')",
      agencynum: record.get('agencynum'),
      });
      var formObj = button.up('travelagencyeditor');
      var formData = formObj.getValues();
      travel.erase(function (record, operation) {
      console.log(record);
      console.log(operation);
      if (operation.getError()==null) {
      console.log('record deleted');
      Ext.Msg.alert('Status', 'Record deleted successfully.');
      } else {
      console.log('Delete record failed');
      Ext.Msg.alert('Status', 'Delete record failed.');
      }
      var store = Ext.getStore("TravelAgency");
      store.loadPage(1);
      Ext.Viewport.animateActiveItem(mp, { type: "slide", direction: "left" });
      });
      }



















      Create Method
      For Create method, we need to ensure the agencynum is not empty.

      onAddButton: function(record, button) {
      var formObj = button.up('addtravelagentmenu');
      var formData = formObj.getValues();
      travel = Ext.create('TravelAgency.model.TravelAgency', {
      agencynum: formData.agencynum,
      NAME: formData.NAME,
      STREET:  formData.STREET,
      POSTBOX:  formData.POSTBOX,
      POSTCODE:  formData.POSTCODE,
      CITY: formData.CITY,
      COUNTRY: formData.COUNTRY,
      REGION: formData.REGION,
      TELEPHONE: formData.TELEPHONE,
      URL: formData.URL,
      LANGU:  formData.LANGU,
      CURRENCY:  formData.CURRENCY
      });
      if (formData.agencynum == "") {
      Ext.Msg.alert('ERROR', 'Agency No could not be empty');
      } else {
      travel.set('agencynum', formData.agencynum);
      travel.set('NAME', formData.NAME);
      travel.set('STREET', formData.STREET);
      travel.set('POSTBOX', formData.POSTBOX);
      travel.set('POSTCODE', formData.POSTCODE);
      travel.set('CITY', formData.CITY);
      travel.set('COUNTRY', formData.COUNTRY);
      travel.set('REGION', formData.REGION);
      travel.set('TELEPHONE', formData.TELEPHONE);
      travel.set('URL', formData.URL);
      travel.set('LANGU', formData.LANGU);
      travel.set('CURRENCY', formData.CURRENCY);
      travel.save(function (record, operation) {
      console.log(record);
      console.log(operation);
      if (operation.wasSuccessful()) {
      Ext.Msg.alert('Status', 'Record added successfully. Record ID: ' + record.get('agencynum'));
      console.log('record created. Id:' + record.get('agencynum'));
      formObj.setValues({
      agencynum: '',
      NAME: '',
      STREET: '',
      POSTBOX: '',
      POSTCODE: '',
      CITY: '',
      COUNTRY: '',
      REGION: '',
      TELEPHONE: '',
      URL: '',
      LANGU: '',
      CURRENCY: ''
      });
      } else {
      console.log('Create record failed');
      }
      var store = Ext.getStore("TravelAgency");
      store.loadPage(1);
      });
      }
      },


















After you complete writing all the codes, you can launch the app from Chrome browse and it will give you the Travel Agency List page:

http://<your-apache-web-server>/TravelAgency/index.html

Some screenshots of edit, update, delete operations in action:


The complete source code can be found in GitHub: https://github.com/ferrygun/TravelAgency

Thanks for reading my blog, feel free to drop any comment/question.

6 Comments
Labels in this area