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:
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:
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,
controllers: ["TravelAgency"],
models: ["TravelAgency"],
stores: ["TravelAgency"],
views: ["Viewport",
"MainPanel",
"TravelAgencyListContainer",
"TravelAgencyEditor"
"AddTravelAgentContainer",
"AddTravelAgentMenu"
],
Page | Corresponding View |
---|---|
Travel Agency List | TravelAgencyList |
Detail Page | TravelAgencyEditor |
Add Travel Agency | AddTravelAgentMenu |
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",
}
config: {
store: "TravelAgency",
itemTpl: [
'<div>',
'<div>Agency No: {agencynum}</div>',
'<div>{NAME}</div>',
'</div>',
],
onItemDisclosure: function(record,btn,index) {
this.fireEvent("editTravelAgencyCommand", record);
}
},
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);
}
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);
});
},
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" });
});
}
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
10 | |
9 | |
5 | |
4 | |
4 | |
3 | |
3 | |
3 | |
3 | |
3 |