Currently Being Moderated

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:

  • You need to develop this in a ERP system, which supports Business Server Pages
  • It is assumed you have employee records in your development system with appropriate data including org assignment (infotype 0001), personal data (infotype 0002), address (infotype 0006), and absence data (infotype 2001)
  • Your user ID needs to be associated with an employee (HCM) record in the system via infotype 105 subtype 0001, using transaction PA30.  If you are not sure how to accomplish this, speak to someone familiar with SAP HCM.
  • To view the result with the best interactivity from your desktop, you need to have a decent browser such as Safari installed.  Note that Internet Explorer (IE6, IE7, IE8) do NOT have good support for HTML5 and therefore the user experience is poor (this is not as much of an issue for smartphones and tablets, because the installed base of IE is much lower than for desktops). 
  • To view the result from your smartphone or tablet, you will need to have network access from that device to your SAP server.  This blog does not cover how to accomplish this and the security aspects, because that is another topic altogether.

 

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).

  • Create a class ZCL_MOBILE_DEMO_MODEL using SE24.  This class should have a superclass of CL_BSP_MODEL.  0.1. Add the following instance attributes to the class ...

image

  • Create one instance method REFRESH_MODEL_DATA for the class with PUBLIC visibility, with the following code ...

 

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.


  • Activate the model class

 

CONTROLLER

Now we build the BSP application and controller. 

  • Create a new BSP application ZMOBILE_DEMO using transaction SE80
  • Right click on the BSP application ZMOBILE_DEMO and select to create a controller.  Name the controller 'mobilemain.do' and enter a description.
  • For the controller, enter the class as ZCL_MOBILE_DEMO_CONTROLLER.  Using forward navigation by clicking into this class name, create the controller class.  When you do this, the controller class should automatically inherit from CL_BSP_CONTROLLER2 (if instead you create the class via SE24, you need to explicitly assign this superclass).
  • Add the following 2 instance attributes to the class ...

Controller class attributes

  • Reimplement the controller class method DO_INIT, and insert the following code ...

 

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.


  • Reimplement the controller class method DO_REQUEST, and insert the following code ...


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.

 

  • Activate the controller class and the BSP application

 

VIEW

Here we create a view and finally we are able to leverage the jQuery Mobile framework.

  • Right click on the BSP application ZMOBILE_DEMO and select to create a Page.  Enter the page name as 'main.htm', enter a description, and select the page type as View.
  • For the page, select the tab Page Attributes and enter the following attributes ...

Main page attributes

  • For the page, select the tab Layout and overwrite the default HTML with the following ...

 

<!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 ...

  • You will see at the top of the HTML that the jQuery framework libraries are referenced.  You can choose to use the hosted versions, or alternatively download these and serve them directly from your mimes directory.  Here we are using the hosted versions.
  • Note that we are referencing the Alpha 2 version of the jQuery Mobile library.  This may need to be updated to the latest release.  Check the website here.
  • A single page in jQuery Mobile is identified by the attribute data-role="page".  Within each such declaration, a page can declare a header region (data-role="header"), a content region (data-role="content"), and a footer region (data-role="footer").  If you inspect the HTML closely, you will see that the following pages have been marked-up into the entire HTML file ...

 

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

 

  • Additionally, you may have noticed the declaration of a Message 'dialog' using an attribute of data-role="dialog" (instead of "page").  Linking to this has the effect of the results being rendered in visual dialog box.  Currently we don't make use of this but I have included the declaration because it will be required for Part 3 of this blog series.

 

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.

 


Comments

Actions

Filter Blog

By author:
By date:
By tag: