This is part 3 in a series about building SAP mobile apps with Sencha Touch. Part 1 provides an overview of the Sencha family of framework and tools. In part 2 we started coding and introduced Models, Stores and Views.
Controllers
Now let’s have a look at Controllers. Controllers respond to view events and handle the data through Models and Stores.
We are going to expand our sample app so that the user can select an agency from the Travel Agency list and its details are shown in a details view. The details view contains a back button to return to the list.
ST offers a UI component, called NavigationView which makes it very easy to build this type of user interface. Conceptually, the NavigationVIew manages a stack of pages and displays the one on top of the stack. You can simply push a new page on the stack. The NavigationView will automatically take care of the toolbar title, back buttons and page transitions.
Here’s the code for the TravelAgencyList view, which will be the first page in the stack:
// define a List to display Travel Agencies
Ext.define('App.view.TravelAgencyList', {
extend: 'Ext.dataview.List',
alias: 'widget.travelagencylist',
config: {
grouped: true,
title: 'Travel Agencies',
itemTpl: '{Name}'
}
});
Please note the alias. This offers a shorthand to include this list in other components, such as the NavigationView:
Ext.define('App.view.MainNavigationView', {
extend: 'Ext.navigation.View',
config: {
fullscreen: true,
// we would like a navigation bar on top for title and back button
navigationBar: {
docked: 'top'
},
// initially the navigation view will only contain the travel agency list
items: [{
xtype: 'travelagencylist'
}]
}
});
Now, let’s define a view to display the details of a Travel Agency. We’ll use a FormPanel which can contain Fieldsets and Fields.
// define a FormPanel to display Travel Agency details
Ext.define('App.view.TravelAgencyDetails', {
extend: 'Ext.form.Panel',
alias: 'widget.travelagencydetails',
config: {
title: 'Travel Agency',
items: [{
xtype: 'fieldset',
title: 'Address', // title of fieldset
// define defaults that apply to all items in this container
defaults: {
xtype: 'textfield',
readOnly: true,
labelWidth: '30%'
},
items: [
{
name: 'Name',
label: 'Name'
}, {
name: 'Street',
label: 'Street'
}, {
name: 'City',
label: 'City'
}, {
name: 'Country',
label: 'Country'
}
]
}]
}
});
The next step is to actually display the details view when a users selects (taps on) an Agency from the list. We could easily create an event listener in the list view to open a details view. But we want to maintain a clear separation and loose coupling, so we are going to handle the list item tap event in a controller:
Ext.define('App.controller.TravelAgencies', {
extend: 'Ext.app.Controller',
config: {
views: ['TravelAgencyDetails'],
// define a reference to the main nav view
refs: {
navView: 'mainnavigationview'
},
// define which event handlers should be invoked
control: {
'travelagencylist': {
itemtap: 'onTravelAgencyListItemTap'
}
}
},
onTravelAgencyListItemTap: function (list, index, target, record) {
var navView = this.getNavView(),
name = record.get('Name');
navView.push({
xtype: 'travelagencydetails',
title: name,
record: record // pass the selected record
});
}
});
Let’s point out some details:
Finally, you need to tell the Ext.application which controller to include:
Ext.application({
name: 'App',
// define required stores, views and controllers
stores: ['TravelAgencies'],
views: ['MainNavigationView', 'TravelAgencyList'],
controllers: ['TravelAgencies'],
launch: function () {
// create a store and navigation view
var store = Ext.create('App.store.TravelAgencies'),
view = Ext.create('App.view.MainNavigationView');
// push a travel agency list into the Navigation view
view.push({
xtype: 'travelagencylist',
store: store
});
}
});
Code organization
You are now familiar with all the key ingredients of a ST application: models, stores, proxies, views and controllers. It is best practices to organize your code into separate source files, one file for each class, in a folder structure that mirrors the namespace. The root folder is by default called ‘app’.
Here’s how your file structure should look like:
Instead of manually including all the different js files in your application html file, you can let Sencha Touch dynamically load all the source files. But you have to inform ST about the dependencies in your code through the ‘requires’ config of a class.
Let’s look at the TravelAgency Store class. It requires the TravelAgency Model class, because it is used in the model config:
// define a store to load a collection TravelAgency records
Ext.define('App.store.TravelAgencies', {
extend: 'Ext.data.Store',
// the class depends on the TravelAgency model
requires: 'App.model.TravelAgency',
config: {
// in the model config we use the TravelAgency model,
// that's why we need to define it as a dependency.
model: 'App.model.TravelAgency',
// automatically load the records when store is created
autoLoad: true,
// perform client-side sorting on field Name
sorters: 'Name',
// configure grouping
grouper: {
// group by the first letter of Name field
groupFn: function (record) {
return record.get('Name')[0];
}
}
}
});
The Sencha Command tools will use the same dependency information to include only the source files required from your app and the framework when building the production version of your app.
So far, we have used the SAP OData connector to fetch data from the SAP NW Gateway server. However, we can modify server data just as well!
As an example we will create, modify and delete a Booking for a Flight in the SAP NW Gateway Flight service.
We’ll first define a Booking model:
Ext.define('App.model.Booking', {
extend: 'Ext.data.Model',
config: {
fields: [{
name: 'AirLineID',
type: 'string'
}, {
name: 'FlightConnectionID',
type: 'string'
}, {
name: 'FlightDate',
type: 'string'
},
{
name: 'BookingID',
type: 'string',
defaultValue: ''
},
{
name: 'CustomerID',
type: 'string'
}, {
name: 'TravelAgencyID',
type: 'string'
}, {
name: 'PassengerName',
type: 'string'
}, {
name: 'CustomerType',
type: 'string'
}, {
name: 'Smoker',
type: 'boolean'
}, {
name: 'LuggageWeight',
type: 'float'
}, {
name: 'WeightUnit',
type: 'string'
}, {
name: 'Invoice',
type: 'boolean'
}, {
name: 'FlightClass',
type: 'string'
}, {
name: 'PriceInForeignCurrency',
type: 'float'
}, {
name: 'ForeignCurrencyCode',
type: 'string'
}, {
name: 'PriceInLocalCurrency',
type: 'float'
}, {
name: 'LocalCurrencyCode',
type: 'string'
}, {
name: 'SalesOfficeID',
type: 'string'
}, {
name: 'BookingDate',
type: 'string'
}, {
name: 'TravelAgencyID',
type: 'string'
}, {
name: 'Cancelled',
type: 'boolean'
}
, {
name: 'Reserved',
type: 'boolean'
}
, {
name: 'PassengerName',
type: 'string'
}, {
name: 'Title',
type: 'string'
}, {
name: 'PassengerDateOfBirth',
type: 'string'
}],
proxy: {
type: 'odata',
url: 'http://gw.esworkplace.sap.com/sap/opu/odata/IWBEP/RMTSAMPLEFLIGHT_2'+
'/BookingCollection',
withCredentials: true,
username: 'GW@ESW',
password: 'ESW4GW'
}
}
});
Now, let’s create a booking. Please note that there are some required fields that we need to provide:
var booking;
booking = Ext.create('App.model.Booking', {
AirLineID: flight.get('AirLineID'),
FlightConnectionID: flight.get('FlightConnectionID'),
FlightDate: flight.get('FlightDate'),
CustomerID: '00004274',
TravelAgencyID: '00000087',
PassengerName: 'Joe Picard'
});
booking.save(function (record, operation) {
if (operation.wasSuccessful()) {
console.log('Booking created. Id:' + record.get('BookingID'));
} else {
console.log('Create booking failed');
}
});
Once we a Booking, we may update its properties:
booking.set('PassengerName', 'Tom Picard');
booking.save(function (record) {
console.log('Booking updated')
});
Finally, to delete a booking:
booking.erase(function () {
console.log('Booking cancelled');
})
Please note that the server determines how to handle the delete request. In the Flights service, the Booking record is not removed from the database. Instead, the Booking is marked as ‘Cancelled’.
Have a look at Developer Tools in Chrome, while developing your app. Here you can sse the Create (POST), Update (PUT) and Delete (DELETE) requests on HTTP level:
Typically you’ll place code to fetch and modify data in the controller, in response to view events.
It's time to wrap up! In this post we have introduced controllers to handle UI events and access the Models. We organized our code in seperate source files and let Sencha Touch dynamically load the source files. Finally, we performed create, update and delete actions on the server data.
I hope this helps you get started with building SAP mobile apps with Sencha Touch.
Thanks for reading!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
6 | |
5 | |
5 | |
5 | |
5 | |
4 | |
4 | |
4 | |
3 | |
3 |