Following on from Part 1 (Build your first Mobile web app using jQuery Mobile and ABAP - Part 1), we now explore the coding required to develop a Mobile web app using jQuery Mobile and ABAP BSP, for DISPLAY scenarios.
The business scenario we will cover is HCM employee self-service on your Smartphone. This will enable you to see your organisation details, address details, and your absence bookings. Many more scenarios can be easily added using the same techniques described here.
Firstly, some pre-requisites required for this are as follows:
I also assume you know how to create classes, methods etc.
OK, now for the design. I have elected to build this using stateless Business Server Pages and with MVC. I know that MVC may not be as beneficial for stateless scenarios, nonetheless I felt that since my iPhone apps were built using MVC (in Objective-C) I should preserve the same approach. I have also elected not to use HTMLB in the scenario, because HTMLB is not fully supported for the breadth of browsers that we may be concerned with (at the time of writing, HTMLB is not even supported for Safari until NetWeaver 7.02) - refer OSS Note 598860.
For the code that follows, this is what I have assembled in as quick and succinct a format as possible so that it can be documented in the form of a blog. However I have intentionally left out error handling etc, that you would ordinarily include. Also the code could be improved through re-factoring to improve robustness and flexibility to add more scenarios. I am simply sharing my experience of building a simple application, and this code would need additional work to be deployable.
MODEL
Start by building the model. We will simplify things here just for the blog by building a single model class which encapsulates all the data and business logic pertaining to what is to be displayed by the entire app (in practice you could choose to break this out into separate classes to maintain a separation of concerns).
METHOD refresh_model_data.
* event handler for data retrieval
DATA: lv_uname TYPE syuname,
lv_error TYPE retco.
DATA: lt_org_data TYPE TABLE OF bapip0001b INITIAL SIZE 1,
lt_personal_data TYPE TABLE OF bapip0002b INITIAL SIZE 1.
*
* Get the user name .
*
lv_uname = cl_abap_syst=>get_user_name( ).
*
* Get the employee number.
*
CALL FUNCTION 'BAPI_USR01DOHR_GETEMPLOYEE'
EXPORTING
id = lv_uname
begindate = sy-datum
enddate = sy-datum
IMPORTING
employeenumber = m_emp_number.
*
* Get general employee data.
*
CALL FUNCTION 'BAPI_EMPLOYEE_GETDATA'
EXPORTING
employee_id = m_emp_number
TABLES
org_assignment = lt_org_data
personal_data = lt_personal_data.
* We should only have one record for each table returned
READ TABLE lt_org_data INDEX 1 INTO m_org_data.
READ TABLE lt_personal_data INDEX 1 INTO m_personal_data.
*
* Get the address data
*
DATA: lt_addressempkeytable TYPE TABLE OF bapipakey.
DATA: lv_addressempkey TYPE bapipakey.
CALL FUNCTION 'BAPI_ADDRESSEMP_GETLIST'
EXPORTING
employeenumber = m_emp_number
subtype = '1' "Only interested in permanent address
timeintervallow = sy-datum
timeintervalhigh = sy-datum
TABLES
addressempkey = lt_addressempkeytable.
* At today's date, we should only have a single permanent address
READ TABLE lt_addressempkeytable INDEX 1 INTO lv_addressempkey.
* Prepare the initial address details
MOVE m_emp_number TO m_address_data-pernr.
MOVE '0006' TO m_address_data-infty.
MOVE lv_addressempkey-subtype TO m_address_data-subty.
MOVE lv_addressempkey-objectid TO m_address_data-objps.
MOVE lv_addressempkey-lockindic TO m_address_data-sprps.
MOVE lv_addressempkey-validbegin TO m_address_data-begda.
MOVE lv_addressempkey-validend TO m_address_data-endda.
MOVE lv_addressempkey-recordnr TO m_address_data-seqnr.
*
* Get the address data details
*
CALL FUNCTION 'BAPI_ADDRESSEMP_GETDETAIL'
EXPORTING
employeenumber = m_address_data-pernr
subtype = m_address_data-subty
objectid = m_address_data-objps
lockindicator = m_address_data-sprps
validitybegin = m_address_data-begda
validityend = m_address_data-endda
recordnumber = m_address_data-seqnr
IMPORTING
streetandhouseno = m_address_data-stras
city = m_address_data-ort01
postalcodecity = m_address_data-pstlz
state = m_address_data-state.
*
* Get the absence history
*
CALL FUNCTION 'BAPI_ABSENCE_GETLIST'
EXPORTING
employeenumber = m_emp_number
TABLES
absenceempkey = m_absence_list.
ENDMETHOD.
CONTROLLER
Now we build the BSP application and controller.
METHOD do_init.
* Create and register model instance
m_mobile_model ?= create_model( class_name = 'zcl_mobile_demo_model'
model_id = 'demo_model' ).
m_mobile_model->refresh_model_data( ).
ENDMETHOD.
METHOD do_request.
* Data declaration
DATA: l_view TYPE REF TO if_bsp_page,
ls_field TYPE ihttpnvp,
lt_fields TYPE tihttpnvp.
request->get_form_fields( CHANGING fields = lt_fields ).
*
* Determine whether there are any input fields to process
*
IF lt_fields IS INITIAL.
* Default to display main page
* Instantiation
l_view = create_view( view_name = 'main.htm').
* Set the attributes
l_view->set_attribute( name = 'lv_pernr' value = m_mobile_model->m_emp_number ).
l_view->set_attribute( name = 'ls_org_data' value = m_mobile_model->m_org_data ).
l_view->set_attribute( name = 'ls_personal_data' value = m_mobile_model->m_personal_data ).
l_view->set_attribute( name = 'ls_address_data' value = m_mobile_model->m_address_data ).
l_view->set_attribute( name = 'lt_absencelist' value = m_mobile_model->m_absence_list ).
* Call the view
call_view( l_view ).
ELSE.
* Process input fields
* Ignore this at this stage for Part 2 of the blog
ENDIF.
ENDMETHOD.
VIEW
Here we create a view and finally we are able to leverage the jQuery Mobile framework.
<!DOCTYPE html>
<html>
<head>
<title>Mobile Demo</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.css"></link>
<script src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
<script src="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.js"></script>
</head>
<body>
<!-- Home Page -->
<div data-role="page" data-theme="b" id="home">
<div id="homeheader">
<div style="text-align:center">
<h1 id="jqm-logo"><div style="color:#ffffff; background-color:#aec5db">Mobile Demo</div></h1>
<p>
Welcome <%= ls_personal_data-firstname %> <%= ls_personal_data-last_name %> <br>
Personnel No. <%= lv_pernr %>
</p>
</div>
</div>
<div data-role="content">
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="a">
<li><a href="#emp_svc">Employee Services</a></li>
</ul>
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="a">
<li><a href="#about" data-transition="flip" data-rel="dialog">About</a></li>
<li><a href="#help" data-transition="flip">Help</a></li>
</ul>
</div>
<div data-role="footer">
</div>
</div><!-- /Home Page -->
<!-- Employee Services -->
<div data-role="page" data-theme="b" id="emp_svc">
<div data-role="header" data-theme="a">
<a href="#home" data-icon="arrow-l">Back</a>
<h1>Emp Services</h1>
</div>
<div data-role="content">
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="a">
<li data-role="list-divider">Employee Information</li>
<li><a href="#emp_org">Org Assignment</a></li>
<li><a href="#emp_address">Address</a></li>
</ul>
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="a">
<li data-role="list-divider">Time and Leave Information</li>
<li><a href="#emp_lvebook">Leave Bookings</a></li>
</ul>
</div>
<div data-role="footer">
</div>
</div><!-- /Employee Services -->
<!-- Org Assignment -->
<div data-role="page" id="emp_org">
<div data-role="header">
<a href="#emp_svc" data-icon="arrow-l">Back</a>
<h1>Org Assignment</h1>
</div>
<div data-role="content">
<ul data-role="listview" data-inset="true" data-theme="d" data-dividertheme="c">
<li data-role="list-divider">Organisational Unit</li>
<li><%= ls_org_data-org_unit %> <%= ls_org_data-orgtxt %> </li>
</ul>
<ul data-role="listview" data-inset="true" data-theme="d" data-dividertheme="c">
<li data-role="list-divider">Position</li>
<li><%= ls_org_data-position %> <%= ls_org_data-postxt %> </li>
</ul>
<ul data-role="listview" data-inset="true" data-theme="d" data-dividertheme="c">
<li data-role="list-divider">Cost Center</li>
<li><%= ls_org_data-costcenter %> </li>
</ul>
</div>
<div data-role="footer">
</div>
</div><!-- /Org Assignment -->
<!-- Address -->
<div data-role="page" id="emp_address">
<div data-role="header">
<a href="#emp_svc" data-icon="arrow-l">Back</a>
<h1>Address</h1>
<a href="#emp_address_update" data-transition="slideup" class="ui-btn-right">Edit</a>
</div>
<div data-role="content">
<ul data-role="listview" data-inset="true" data-theme="d" data-dividertheme="c">
<li data-role="list-divider">Main Address</li>
<li>
<span id="display_street"><%= ls_address_data-stras %></span><br>
<span id="display_city"><%= ls_address_data-ort01 %></span><br>
<span id="display_state"><%= ls_address_data-state %></span><br>
<span id="display_postcode"><%= ls_address_data-pstlz %></span>
</li>
</ul>
</div>
<div data-role="footer">
</div>
</div><!-- /Address -->
<!-- Update Address -->
<div data-role="page" id="emp_address_update">
<div data-role="header">
<a href="#emp_address" data-icon="delete">Cancel</a>
<h1>Update Address</h1>
</div>
<div data-role="content">
<form id="address_update" action="" method="">
<fieldset>
<label>Street</label><br>
<input id="street" name="street" type="text" value="<%= ls_address_data-stras %>"><br>
<label>City</label><br>
<input id="city" name="city" type="text" value="<%= ls_address_data-ort01 %>"><br>
<label>State</label><br>
<input id="state" name="state" type="text" value="<%= ls_address_data-state %>"><br>
<label>Post Code</label><br>
<input id="postcode" name="postcode" type="text" value="<%= ls_address_data-pstlz %>"><br>
<input type="submit" name="submit" class="button" id="save_address" value="Save"></input>
</fieldset>
</form>
</div>
<div data-role="footer">
</div>
<!-- </form> -->
</div><!-- /Update Address -->
<!-- Leave Bookings -->
<div data-role="page" id="emp_lvebook">
<div data-role="header">
<a href="#emp_svc" data-icon="arrow-l">Back</a>
<h1>Leave Bookings</h1>
</div>
<div data-role="content">
<ul data-role="listview" data-inset="true" data-filter="true" data-theme="d" data-dividertheme="c">
<li data-role="list-divider">Leave History</li>
<% field-symbols: <ls_abs_line> like line of lt_absencelist.
data: lv_absencetext type atext.
data: lv_countrycode type molga.
select single molga from T001P into lv_countrycode
where werks = ls_org_data-pers_area
and btrtl = ls_org_data-p_subarea.
loop at lt_absencelist assigning <ls_abs_line>.
select single atext from T554t into lv_absencetext
where SPRSL = sy-langu
and MOABW = lv_countrycode
and AWART = <ls_abs_line>-subtype. %>
<li><%= lv_absencetext %>
<small>
<div class="ui-li-aside">From: <%= <ls_abs_line>-validbegin+6(2) %>.
<%= <ls_abs_line>-validbegin+4(2) %>.
<%= <ls_abs_line>-validbegin(4) %>
<br>
To: <%= <ls_abs_line>-validend+6(2) %>.
<%= <ls_abs_line>-validend+4(2) %>.
<%= <ls_abs_line>-validend(4) %>
</div>
</small>
</li>
<% endloop. %>
</ul>
</div>
<div data-role="footer">
</div>
</div><!-- /Leave Bookings -->
<!-- About -->
<div data-role="page" data-theme="b" id="about">
<div data-role="header" data-theme="a">
<a href="#home" data-icon="arrow-l">Back</a>
<h1>About</h1>
</div>
<div data-role="content">
<p>John Moy, November 2010.</p>
<p>This is a demonstration of a basic web mobile application using jQuery Mobile (Alpha2 version) connected to an
SAP Web Application Server using Business Server Pages.</p>
</div>
</div><!-- /About -->
<!-- Help -->
<div data-role="page" data-theme="b" id="help">
<div data-role="header" data-theme="a">
<a href="#home" data-icon="arrow-l">Back</a>
<h1>Help</h1>
</div>
<div data-role="content">
<p>This Web application requires an internet connection to operate. The performance of the application may vary
depending upon connection speeds.</p>
</div>
</div><!-- /Help -->
<!-- Message -->
<div data-role="dialog" data-theme="b" id="message">
<div data-role="header" data-theme="a">
<h1> </h1>
</div>
<div data-role="content">
<p id="return_message" mce_keep="true" mce_keep="true"> </p>
<a href="" data-role="button" data-theme="c"><center>OK</center></a>
</div>
</div><!-- /Message -->
</body>
</html>
Now for the explanation of the layout definition ...
Home Page | This is the initial default page. It simply serves as a menu page because it incorporates links to other pages. Notice that at the top of the page we incorporate some BSP page attributes to display the employee number. Notice also that the links to the pages About and Help have an explicitly defined animation transition of 'flip'. This is how we get the effect shown in the Flash video from Part 1 of this blog, when clicking on these links. Simply declare data-transition="flip", and the framework handles the rest. |
Employee Services Page | This serves as a menu page, linking to the 3 detail pages Org Assignment and Address, and Leave Bookings which are all declared in the same file. |
Org Assignment Page | This page provides details of the employee's organisational assignment. Notice the BSP page attributes referenced here. |
Address Page | This page provides details of the employee's main address. Notice the BSP page attributes referenced here. This page also links to the Update Address Page via a declaration in the header region to display an 'Edit' button, with an explicit animation transition of 'slideup'. |
Update Address Page | This page provides details of the employee's main address in form edit mode. At this point in time, pressing the Save button will have no effect. Enabling this is the focus of Part 3 of this blog. |
Leave Bookings Page | This page lists the absence bookings for you. Note that the attribute data-filter="true" has the effect of the framework automatically inserting a filter bar at the top of the list. This filter provides client-side filtering and is fully functional. |
About Page | This page provides basic information in the content area. |
Help Page | This page provides basic information in the content area |
If you are wondering about what you would do if you were to link to pages in separate files, that is OK also. jQuery Mobile automatically highjacks the response and inserts it into the DOM for the existing HTML, and executes the page transition so that navigating to the new page appears seamless. The decision as to whether to declare pages in other HTML files and link to them, or incorporate them into one, is up to you. Performance is a consideration though. If you include everything in one page then when performing view-only tasks, you no longer need to have a network connection after the initial download. However, this is at the cost of incurring processor and network time to retrieve everything upfront. It would typically make sense to weigh up the user experience benefit once the application is loaded versus the server processing and upfront download cost. Of course you can choose to mix and match (ie. some pages downloaded in the initial HTML, others linked to as external pages). When linking to pages in separate files, jQuery Mobile automatically displays a Loading... indicator which is pretty slick.
Spending the time to review this HTML, you will notice the power of the jQuery Mobile framework. Simply declare a flip transition using data-transition="flip" and when the link is pressed, the app provides an animated flip to the new page. There are other animations available such as slide, slideup etc. You can even use jQuery Mobile to detect touch events such as tap, swipe, swipeleft etc. This example only scratches the surface.
Review the documentation on jQuery Mobile at the website.
RESULT
Execute the BSP application using a decent browser such as Safari (please note that Internet Explorer will not render this all too well). You may need to resize the browser to the dimensions of a Smartphone or Tablet to see how it would look in terms of size.
After you authenticate, you should see something like the YouTube video from Part 1, which I have replicated below (note however that at this stage the Address Update function will NOT work as depicted in the video)...
Note: With this Alpha release of the framework, I have noticed that sometimes it gets confused and executes transitions incorrectly. Also sometimes when executing it on a smartphone (in my case iPhone) the full-screen mode disables itself upon a transition. These are simply bugs (noticed in the forums for the jQuery Mobile website) that I would expect would be fixed for the version 1.0 release.
Like it so far? In Part 3 of this blog series we will extend the code to process the Address Update scenario.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
8 | |
7 | |
6 | |
5 | |
5 | |
4 | |
4 | |
4 | |
3 | |
3 |