Tuesday, February 23, 2010

WebDynpro: Example of dynamic programming

There is a lot of flexibility in terms of what can be done for user interaction if dynamic programming is done. No Web 2.0 here but still not bad.
I have a simple example to take you through the dynamic programming. This involves dynamic creation of Contexts and UI elements. In the example we will maintain a simple list, a list of American presidents. The list will be shown on a table UI element. We will be able add to the list any number of presidents we want.
image
We can create as many new input UI element as the user wants.
image
The addition process is not by creating new empty editable rows in the table but creating new text input UI elements that we create as required.
image
The layout for the screen is as below:
image
And the context:
image
The user enters the number of new presidents she wants to add and clicks the add button. The action add will do the followings:
  1. Read the value of number.
  2. Reset the number back to 1.
  3. Remove previous text input UI element if there are any
  4. Create new context to capture text input
  5. Add text input UI element
method ONACTIONADD .
* Refresh screen with new president additions
* Get number of new cohort addins to create
get_new_additions( ).
* Reset new additions
reset_new_addition( ).
* Remove group cohort from layout
remove_group_presidents( ).
* Manage dynamic contexts
manage_dynamic_contexts( ).
* Add new text input UI elements
wd_this->add_new_president_group( ).
endmethod.
1) & 2) should be easy. The new text input UI elements that are created are within a group container UI element. For 3) removing UI elements we just have to remove the group container.

method REMOVE_GROUP_PRESIDENTS .
DATA l_o_container TYPE REF TO cl_wd_uielement_container.
l_o_container ?= wd_this->o_view->get_element( 'ROOTUIELEMENTCONTAINER' ).
l_o_container->remove_child( id = 'GRP_PRESIDENT01' ).
l_o_container->remove_child( id = 'HG_PRESIDENT01' ). “Horizontal Gutter
endmethod.
The input fields need to be tied to a context attribute. We have the following context design that needs to be created dynamically.
image
For 4) we create the context dynamically and also store the context names in an internal table for management.

METHOD manage_dynamic_contexts .
DATA l_str_node_name TYPE string.
DATA l_parent_node_info TYPE REF TO if_wd_context_node_info.
DATA l_child_node_info TYPE REF TO if_wd_context_node_info.
DATA l_cohort_number(6).
DATA l_str_cohort_name TYPE string.
DATA l_attr_info TYPE wdr_context_attribute_info.
* Access the parent node
l_parent_node_info = wd_context->get_node_info( ).
delete_existing_nodes( l_parent_node_info ).
l_attr_info-name = 'NAME'.
l_attr_info-type_name = 'STRING'.
DO wd_this->new_additions TIMES.
WRITE sy-index TO l_cohort_number.
CONDENSE l_cohort_number.
CONCATENATE 'PRESIDENT' l_cohort_number INTO l_str_cohort_name.
* add new node for each president
CALL METHOD l_parent_node_info->add_new_child_node
EXPORTING
*    SUPPLY_METHOD                =
*    SUPPLY_OBJECT                =
*    DISPOSE_METHODS              =
*    DISPOSE_OBJECT               =
name                         = l_str_cohort_name
is_mandatory                 = abap_true
*    IS_MANDATORY_SELECTION       = ABAP_TRUE
is_multiple                  = abap_false
is_multiple_selection        = abap_false
is_singleton                 = abap_true
is_initialize_lead_selection = abap_true
is_static                    = abap_false
RECEIVING
child_node_info              = l_child_node_info.
CALL METHOD l_child_node_info->add_attribute
EXPORTING
attribute_info = l_attr_info.
APPEND l_str_cohort_name TO wd_this->t_dynamic_nodes.
ENDDO.
ENDMETHOD.
Some comments about parameters of the method ADD_NEW_CHILD_NODE of the interface IF_WD_CONTEXT_NODE_INFO. Option IS_MANDATORY is same as Selection property of context = 1..1. Option IS_STATIC needs to be false otherwise we won’t be able to delete it later.

The last thing 5) to do is to add the new text input UI elements. At this point we have the number of contexts created and the dynamic names assigned to it in an internal table. We will go through that table and create text input UI for each of these contexts. All these text input elements are inserted into a group UI element that is created first. For adding a UI element you need to assign layout data which should be same as the container layout. Each input is label UI + text input UI contained in transparent container UI element.

METHOD add_new_president_group .
DATA l_president_count TYPE i.
DATA l_president_number(6).
DATA l_str_president_number TYPE string.
DATA l_str_president_name TYPE string.
DATA l_str_bind_text TYPE string.
DATA l_str_input_text TYPE string.
DATA l_node_president TYPE REF TO if_wd_context_node.
DATA l_o_group     TYPE REF TO cl_wd_group.
DATA l_o_caption   TYPE REF TO cl_wd_caption.
DATA l_o_container TYPE REF TO cl_wd_uielement_container.
DATA l_o_t_cont   TYPE REF TO cl_wd_transparent_container.
DATA l_o_t_cont_bottom   TYPE REF TO cl_wd_transparent_container.
DATA l_o_input_field TYPE REF TO cl_wd_input_field.
DATA l_o_label TYPE REF TO cl_wd_label.
DATA l_cell_backgroup TYPE wdui_cell_bg_design VALUE '00'.
DATA l_str_path TYPE string.
DATA l_number_of_children TYPE i.
DATA l_o_hg TYPE REF TO cl_wd_horizontal_gutter.
* Get access to the root
l_o_container ?= wd_this->o_view->get_element( 'ROOTUIELEMENTCONTAINER' ).
DATA:
elem_president                         TYPE REF TO if_wd_context_element.
* Add presidents
LOOP AT wd_this->t_dynamic_nodes INTO l_str_president_name.
AT FIRST.
* Create new group
CALL METHOD cl_wd_group=>new_group
EXPORTING
id      = 'GRP_PRESIDENT01'
RECEIVING
control = l_o_group.
* Add layout data to group which should be same the
* container in which group is
CALL METHOD cl_wd_flow_data=>new_flow_data
EXPORTING
element = l_o_group.
* Add new layout to group
CALL METHOD cl_wd_flow_layout=>new_flow_layout
EXPORTING
container = l_o_group.
l_o_caption = cl_wd_caption=>new_caption( text = 'New presidents' ).
l_o_group->set_header( the_header = l_o_caption ).
ENDAT.
* Build the Node name using counter
l_president_count = l_president_count + 1.
WRITE l_president_count TO l_president_number.
CONDENSE l_president_number.
l_str_president_number = l_president_number.
* Get the node from name
l_node_president = wd_context->get_child_node( name = l_str_president_name ).
CALL METHOD l_node_president->get_path
EXPORTING
withoutcontroller = abap_true
RECEIVING
path              = l_str_path.
CONCATENATE l_str_path 'NAME' INTO l_str_path SEPARATED BY '.'.
* Create input field UI
CONCATENATE 'IF_PRESIDENT_DESC' l_president_number INTO l_str_input_text.
CALL METHOD cl_wd_input_field=>new_input_field
EXPORTING
id      = l_str_input_text
RECEIVING
control = l_o_input_field.
* Bind the value to the context
CALL METHOD l_o_input_field->bind_value
EXPORTING
path = l_str_path.
CALL METHOD cl_wd_flow_data=>new_flow_data
EXPORTING
element = l_o_input_field.
* transparent container to house the president
l_o_t_cont = cl_wd_transparent_container=>new_transparent_container( ).
* For each new centre toggle background colour
IF l_cell_backgroup = '00'.
l_cell_backgroup = '01'.                              " Fill1
ELSE.
l_cell_backgroup = '00'. " Transparent
ENDIF.
* Add layout data to container
CALL METHOD cl_wd_flow_data=>new_flow_data
EXPORTING
cell_design = l_cell_backgroup
element     = l_o_t_cont.
CALL METHOD cl_wd_flow_layout=>new_flow_layout
EXPORTING
container = l_o_t_cont.
* Create element label
l_o_label = cl_wd_label=>new_label( text = l_str_president_number label_for = l_str_input_text ).
CALL METHOD cl_wd_flow_data=>new_flow_data
EXPORTING
element = l_o_label.
l_o_t_cont->add_child( the_child = l_o_label ).
l_o_t_cont->add_child( the_child = l_o_input_field ).
l_o_group->add_child( the_child = l_o_t_cont ).
AT LAST.
CALL METHOD l_o_container->number_of_children
RECEIVING
number = l_number_of_children.
* Add a horizontal gutter for separation    
CALL METHOD cl_wd_horizontal_gutter=>new_horizontal_gutter
EXPORTING
id        = 'HG_PRESIDENT01'
rule_type = cl_wd_horizontal_gutter=>e_rule_type-none
RECEIVING
control   = l_o_hg.
CALL METHOD cl_wd_flow_data=>new_flow_data
EXPORTING
element = l_o_hg.
l_o_container->add_child( index = l_number_of_children the_child = l_o_group ).
l_number_of_children = l_number_of_children + 1.
l_o_container->add_child( index = l_number_of_children the_child = l_o_hg ).
ENDAT.
ENDLOOP.
ENDMETHOD.
Once the user has added the Presidents, she will click on the button save. That will read the entries from the dynamic context and add it to the table to be displayed. The action will also do the cleanup of removing the dynamic contexts and the UI elements. Finally a message is relayed to say that list has been updated.
METHOD onactionsave .
DATA l_president_node TYPE REF TO if_wd_context_node.
DATA lst_president  TYPE if_v_screen01=>element_presidents .
DATA lt_presidents TYPE TABLE OF if_v_screen01=>element_presidents.
DATA lst_data TYPE REF TO data.
DATA:
node_president_ref                       TYPE REF TO if_wd_context_node,
elem_president_ref                       TYPE REF TO if_wd_context_element.
* Get current table contents of cust ref
node_president_ref = wd_context->get_child_node( name = if_v_screen01=>wdctx_presidents ).
node_president_ref->get_static_attributes_table(
IMPORTING
table = lt_presidents ).
DATA l_parent_node_info TYPE REF TO if_wd_context_node_info.
DATA l_str_president_name TYPE string.
DATA l_ref(3) TYPE n.
FIELD-SYMBOLS <lst_president> TYPE if_v_screen01=>element_presidents.
* Access the parent node
l_parent_node_info = wd_context->get_node_info( ).
* Add the new presidents in the cust ref table
LOOP AT wd_this->t_dynamic_nodes INTO l_str_president_name.
l_president_node = wd_context->get_child_node( name = l_str_president_name ).
l_president_node->get_attribute(
EXPORTING
name              = 'NAME'
IMPORTING
value             = lst_president-name ).
CHECK lst_president-name IS NOT INITIAL.
APPEND lst_president TO lt_presidents.
ENDLOOP.
node_president_ref->bind_table(
EXPORTING
new_items = lt_presidents ).
* Delete added president nodes
delete_existing_nodes( l_parent_node_info ).
* Remove group president
remove_group_presidents( ).
* Reset new additions
reset_new_addition( ).
* get message manager
DATA: l_current_controller TYPE REF TO if_wd_controller,
l_message_manager    TYPE REF TO if_wd_message_manager.
l_current_controller ?= wd_this->wd_get_api( ).
CALL METHOD l_current_controller->get_message_manager
RECEIVING
message_manager = l_message_manager.
* report message
CALL METHOD l_message_manager->report_success
EXPORTING
message_text = 'Changes Saved'.
ENDMETHOD.
The removal of context is done as:
METHOD delete_existing_nodes .
DATA l_str_node_name TYPE string.
* Delete any existing nodes first
LOOP AT wd_this->t_dynamic_nodes INTO l_str_node_name.
CALL METHOD i_parent_node_info->remove_child_node
EXPORTING
name = l_str_node_name.
ENDLOOP.
CLEAR wd_this->t_dynamic_nodes[].
ENDMETHOD.