Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member
0 Kudos

Part 1: Decision to use WD4A

Similar to Practical tips for developing with ABAP WebDynpro I had tinkered with WD4A in training environments for some twelve months going through the various tutorials on SDN during any spare downtime between projects. I had bought the SAP Press book by Ulli Hoffmann which all seemed quite straight forward when taking individual examples in isolation.

To my delight the next project was to be an ESS/MSS implementation on ECC6. Having gone through the blueprint it was obvious that a number of new applications needed to be written to meet the business requirement. This gave rise ‘to WD4A or not to WD4A' - that was the question.

After reading many articles on the subject including the one by Web Dynpro: ABAP or Java? I decided that the plan for any new applications would be WD4A and not Java WebDynpro. The main reasons for this decision were:

  • Content of the applications were based purely on ECC6 data i.e. all the application logic is based upon an ABAP system
  • No need to setup NWDI/NWDS (no plans to modify the existing delivered applications)
  • UI elements included ALV for some reporting requirements
  • Tools for development were more familiar
  • Support of the solution would be easier as the skill set available was primarily ABAP based

More importantly it gave me the perfect opportunity to put my skills into practice by having true productive developments to write!

Part 2: Design Principles

Now came the tricky bit. Developing productive applications is a far cry from prescriptively following examples on SDN. From the blueprint it was obvious that a sensible and consistent approach was needed if my applications were to have a look and feel of the standard SAP content for ESS/MSS.

Searching through SAP content and SDN I realised that ESS/MSS applications are built on a concept called the Floor Plan Manager (FPM).  However, this is for Java WebDynpro and not WD4A.  However, it did give an overall framework for how the applications look and feel. 

This is especially highlighted in the document 'ESS & WebDynpro' by Ralf Halbedel and Dr. Christian Wiele.  In particular this defined the general visual steps that each application has. The application views would be based upon a roadmap area, a message area and a context area.

Part 3: Development

With a basic understanding of the Java FPM I wanted to re-create a similar appearance to my applications. However, as with all custom development there needs to be a trade off between a completely flexible solution and one that offers a degree of flexibility whilst maintaining readability for support purposes.
With this in mind I decided to split the framework into several components that could be reused in each application. The following sections give an understanding as to what these components consist of. As with all custom development there are several ways to achieve the same result. Ultimately this may not be the best way - after all this is a leap of faith from the comfort of examples into the ‘real world' of WD4A development.....

Part 4: Roadmap UI Component

One of the fundamental UI elements in ESS/MSS is the Roadmap. This was an immediate candidate for component reuse. As each application would be different there needed to be a way to configure the roadmap and build it up dynamically. In addition this needed to interact with some navigation buttons to move the user through the application.

The first step was to define a table to hold the steps for each application. This consisted of a main table and a language dependant table to hold the descriptions.

Table for application steps

Table for application step descriptions

A corresponding maintenance view was created to allow the application developer to make entries.

Next a roadmap WD component was needed to visualise the steps in the table.  The component simply consisted of a single view with the UI element 'RoadMap'.

FPM RoadMap Component 

FPM RoadMap View 

FPM Roadmap Controller Context

Note that the context has been defined as as interface node and Input Element (Ext.) so that it can be controlled via the using component.  The fields themselves are based upon a dictionary structure of the corresponding transparent table that defines the steps.

Next the individual steps needed to be created dynamically.  This was done in the method WDDOMODIFYVIEW

METHOD wddomodifyview .
DATA:
  node_steps TYPE REF TO if_wd_context_node,
  table_steps TYPE if_v_rm_ui=>elements_steps,
  stru_steps TYPE if_v_rm_ui=>element_steps,
  lr_rm TYPE REF TO cl_wd_road_map,
  lr_rm_step TYPE REF TO cl_wd_road_map_step,
  ls_step TYPE string,
  ls_step_ltext TYPE string,
  ls_step_id TYPE string.
  CHECK first_time = abap_true.
* get roadmap element
  lr_rm ?= view->get_element( 'ROAD_MAP' ).
* get steps from context to dynamically create
  node_steps = wd_context->get_child_node( name = if_v_rm_ui=>wdctx_steps ).
  node_steps->get_static_attributes_table( IMPORTING table = table_steps ).
* add each step as defined in attribute gt_steps in the controller
  LOOP AT table_steps INTO stru_steps.
    ls_step = sy-tabix.
    ls_step_ltext = stru_steps-ltext.
    ls_step_id = stru_steps-stepid.
*   create step
     lr_rm_step = cl_wd_road_map_step=>new_road_map_step(
       description         = ls_step_ltext
       id                  = ls_step_id
       name                = ls_step ).
*   add step to roadmap
    lr_rm->add_step( lr_rm_step ).
  ENDLOOP.
ENDMETHOD.

Now that the steps have been created there needs to be a mechanism to move between steps.  In order to do this a new interface method has been defined in the component controller. This method has an input parameter of DIRECTION i.e. PREVIOUS or NEXT.

METHOD set_next_step .
  DATA lc_plug TYPE string VALUE 'TO_'.
  DATA li_index TYPE i.
  DATA lo_nd_steps TYPE REF TO if_wd_context_node.
  DATA lo_el_steps TYPE REF TO if_wd_context_element.
  DATA ls_steps TYPE wd_this->element_steps.
  wd_this->set_roadmap_step( i_direction ).
  lo_nd_steps = wd_context->get_child_node( name = wd_this->wdctx_steps ).
  li_index = lo_nd_steps->get_lead_selection_index( ).
  lo_el_steps = lo_nd_steps->get_element( li_index ).
* get all declared attributes
  lo_el_steps->get_static_attributes( IMPORTING static_attributes = ls_steps ).
  CONCATENATE lc_plug ls_steps-stepid INTO lc_plug.
  wd_this->fire_next_step_evt( lc_plug ).
ENDMETHOD.
METHOD set_roadmap_step .
  DATA:
    node_steps TYPE REF TO if_wd_context_node,
    li_step    TYPE i.
* read component
  node_steps = wd_context->get_child_node( name = if_componentcontroller=>wdctx_steps ).
  li_step = node_steps->get_lead_selection_index( ).
* set new step number
  CASE i_direction.
    WHEN zcl_ess_assist=>fpm_step_next.
      li_step = li_step + 1.
    WHEN zcl_ess_assist=>fpm_step_prev.
      li_step = li_step - 1.
  ENDCASE.
* reset lead selection to new step
  node_steps->set_lead_selection_index( li_step ).
ENDMETHOD.

The interface method will set the lead selection according to whether the user pressed the PREVIOUS or NEXT button.  In addition the method will fire an interface event that contains a parameter of the navigation plug to be fired based upon a simple concatenation of the step. 

BTW: the reference to ZCL_ESS_ASSIST contains some public static constants that are used throughout the application(s).

Part 5: Button Row UI Component

The second reusable component is the button row.  This needs to work in conjunction with the RoadMap component to provide the necessary events to move through the application steps.

FPM ButtonRow Component

FPM ButtonRow View

FPM ButtonRow Context

The STEPS_EXTERNAL context node is the same as defined in the RoadMap component so that the button descriptions can be picked up for the correct step.

The buttons are connected to ONACTION events that will fire an interface event:

METHOD onactionnext .
  wd_comp_controller->set_button_event( zcl_ess_assist=>fpm_step_next ).
ENDMETHOD.
METHOD onactionprev .
  wd_comp_controller->set_button_event( zcl_ess_assist=>fpm_step_prev ).
ENDMETHOD.
METHOD set_button_event .
  DATA lo_nd_steps_external TYPE REF TO if_wd_context_node.
  DATA lo_el_steps_external TYPE REF TO if_wd_context_element.
  DATA ls_steps_external TYPE wd_this->element_steps_external.
* get current step details
  lo_nd_steps_external = wd_context->get_child_node( name = wd_this->wdctx_steps_external ).
  lo_el_steps_external = lo_nd_steps_external->get_element( ).
  lo_el_steps_external->get_static_attributes( IMPORTING static_attributes = ls_steps_external ).
* trigger interface event
  wd_this->fire_action_br_evt( EXPORTING e_step = ls_steps_external
  e_direction = i_direction ).
ENDMETHOD.

In addition a number of interface methods have been defined in the component controller.  These methods allow the button row to be controlled via the calling component.

METHOD set_button_row_enabled .
  DATA lo_nd_button_row TYPE REF TO if_wd_context_node.
  DATA lo_el_button_row TYPE REF TO if_wd_context_element.
  DATA ls_button_row TYPE wd_this->element_button_row.
* navigate from to via lead selection
  lo_nd_button_row = wd_context->get_child_node( name = wd_this->wdctx_button_row ).
  lo_el_button_row = lo_nd_button_row->get_element( ).
* get context
  lo_el_button_row->get_static_attributes( IMPORTING static_attributes = ls_button_row ).
* change single attribute
  ls_button_row-enabled = i_enabled.
* reset context
  lo_el_button_row->set_static_attributes( EXPORTING static_attributes = ls_button_row ).
ENDMETHOD.
METHOD set_button_row_visible .
  DATA lo_nd_button_row TYPE REF TO if_wd_context_node.
  DATA lo_el_button_row TYPE REF TO if_wd_context_element.
  DATA ls_button_row TYPE wd_this->element_button_row.
* navigate from to via lead selection
  lo_nd_button_row = wd_context->get_child_node( name = wd_this->wdctx_button_row ).
  lo_el_button_row = lo_nd_button_row->get_element( ).
* get context
  lo_el_button_row->get_static_attributes( IMPORTING static_attributes = ls_button_row ).
* change single attribute
  ls_button_row-visible = i_visible.
* reset context
  lo_el_button_row->set_static_attributes( EXPORTING static_attributes = ls_button_row ).
ENDMETHOD.

These methods were necessary as in some applications a button row may need to be disabled or set invisible as determined by the calling application.

Part 6: Bringing it all together

Now that the main reusable components have been created they can be consumed by an application.  Following the FPM of standard SAP applications the WD4A application consists of the following main view:

The main view of the application is split into a roadmap area, message area, application area (as per the individual application needs) and a button row area.

In this example the application has three steps

Each step is embedded into the VCU_APPL container.  In addition each step has a corresponding inbound plug defined:

The main view then controls all the navigation between steps by defining corresponding outbound plugs.  These plugs are the ones that are fired by the RoadMap component.

The window then connects the plugs accordingly

To bind all this together the application needs to select the steps as defined in the table (see Part 4).  This can be done somewhere in the WDINIT method of the COMPONENT CONTROLLER using syntax such as

DATA lo_application TYPE REF TO if_wd_application.
DATA lo_application_det TYPE REF TO if_wd_rr_application.
DATA lo_api_componentcontroller TYPE REF TO if_wd_component.
DATA lo_cmp_usage TYPE REF TO if_wd_component_usage.
* get application name
  lo_api_componentcontroller = wd_this->wd_get_api( ).
  lo_application = lo_api_componentcontroller->get_application( ).
  lo_application_det = lo_application->get_application_info( ).

Capturing the ButtonRow Event

When the user presses the PREVIOUS or NEXT button then the event raised can be listened for in each individual view of the application.

The receiving event can then process the screen and determine if the next view should be displayed (e.g. by checking mandatory fields etc).

METHOD do_handle_br .
DATA lo_api_controller TYPE REF TO if_wd_view_controller.
  CHECK e_step-stepid = 'STEP1'.
* do your stuff
  CASE e_direction.
    WHEN zcl_ess_assist=>fpm_step_prev.
    WHEN zcl_ess_assist=>fpm_step_next.
*    e.g. check mandatory fields in this view
  ENDCASE.
  wd_comp_controller->set_next_step( e_direction ).
ENDMETHOD.

If the application is allowed to navigate to another view then the next step can be executed.

METHOD set_next_step.
DATA lo_interfacecontroller TYPE REF TO ziwci_ess_fpm_rm_ui . 
  lo_interfacecontroller = wd_this->wd_cpifc_usage_rm( ).
  lo_interfacecontroller->set_next_step( i_direction ).
ENDMETHOD.

This will call the interface method of the RoadMap component (see method METHOD set_next_step in Part 4).

Finally in the component controller of the application a listening event is defined.

This method now needs to call another method within the main view.  In order to do this another event is fired.

METHOD do_handle_next_step .
  wd_this->fire_do_navigation_evt( e_new_step ).
ENDMETHOD.

The receiving method as defined in the MAIN view will fire the necessary navigation plug.

METHOD do_handle_next_step .
  DATA lr_view_ctr TYPE REF TO if_wd_view_controller.
  lr_view_ctr = wd_this->wd_get_api( ).
  CALL METHOD lr_view_ctr->fire_plug( EXPORTING plug_name = e_new_step ).
ENDMETHOD.

Part 7: The Finished Application

The following screen shots show the finished article as seen by the ESS/MSS user.  Note the configuration of the buttons in terms of visibility and descriptions.

Configuration Steps

Step 1

Step 2

Step 3

Application Steps 

Note that 'Step 1, Step 2 and Step 3' are the application component views as defined in the VCU_APPL UI element of the main view.

Step 1

Step 2

Step 3

Part 8: Conclusion

At first glance this may seem rather complicated.  However, by designing the RoadMap and ButtonRow UI elements as reusable components saved considerable time in developing the individual applications.  This concept can be used for any WD4A application and not just specific to ESS/MSS. 

Personally it brought me a step further towards understanding the WebDynpro framework by having practical uses for concepts such as reverse context mapping, component usages, event handling and dynamic UI programming.  By bringing these together the end user of the system has a consistent look and feel to their applications regardless of them being standard content or bespoke.

As stated upfront there are several ways to approach custom development. I have learnt that the best way to develop WD4A is by designing small components that can interact with each other through simple reverse context mapping, interface methods and interface events.

8 Comments