Tuesday, September 10, 2013

Running a Report Program and Getting Its Result

There are situations when you need to run a report program and get its result list and display (or process if you need). This article helps you to achieve that.

At first, we are going to create a simple report with a selection screen. The selection screen will be having a single input end_val which gets an integer. Once executed the numbers are generated from 1 upto the end_val as the result.

ZTEST_REPORT

Now we are going to create a report which runs this report program and gets the result list in an internal table and writes it again on its output using SUBMIT command and LIST_FROM_MEMORY function module.

SUBMIT command has a vareity of options of which we are going to use the following structure

SUBMIT <report_name>
  WITH SELECTION-TABLE <rspar_tab>
  EXPORTING LIST TO MEMORY
  AND RETURN.

EXPORTING LIST TO MEMORY phrase makes sure once the report is executed the result is kept in a buffer which can be obtained by using the function module LIST_TO_MEMORY.

Now the selection screen data has to be passed to the SUBMIT command through a table of structure RSPARAMS.

Field Purpose
SELNAME Name of the selection screen parameter
KIND Pass value 'P' if parameter,'S' if select options
SIGN 'I' to include, 'E' to exclude (for select options only)
OPTION Select option values such as 'EQ', 'BT', etc.
LOW Selection Value (for parameter, pass the value here)
HIGH Selection Value high

RSPARAMS has 45 character length for LOW and HIGH values. If you need longer character length then use RSPARAMSL_255.

LIST_FROM_MEMORY function module returns the output list in a table of structure ABAPLIST. This can be passed to WRITE_LIST function module which will print the result report of the called program in the output of the current program.

ZSUBMIT_N_RET

Tuesday, January 15, 2013

Integrating Applications With Suspend, Resume & Exit Plugs

Integrating two different applications in Web Dynpro ABAP is quite a challenging task. Most of the time we choose to integrate applications by making component usage of one component in another. But sometimes this becomes a tedious task, especially when both component are dependent on each other. Here we are going to see an alternative option which is redirecting URL by using Suspend, Resume & Exit plugs.

Now we will see a small example where there are two applications with URLs A & B. Application A will contain a InputField and a Button captioned 'Open Application B' and application B will contain TextView element and a Button captioned 'Exit'. On clicking 'Open Application B' the text from the InputField goes to application B and get displayed in the TextView. On clicking exit button it jumps back exiting application B and resuming application A. Here the message is passed as a URL parameter and it is processed at the handler of the default plug of window of application B.

Create two components ZFAR_APP1 and ZFAR_APP2. Create the context as shown below for the both components in the component controller. Attribute MSG is of type STRING.


Create mapping for the context node NODE in both MAIN view and the window (ZFAR_APP1 and ZFAR_APP2) of both the components.

Now design the UI of application A as shown below in the MAIN view and bind the InputField to MSG attribute of NODE.


Design the UI of application B as shown below in the MAIN view and bind the TextView to MSG attribute of NODE.

Creating plugs for Application A


Create an outbound plug named TO_WINDOW in the MAIN view. Now create inbound plugs & outbound plugs in the window as shown below.



When you create a suspend or exit plug, you can create a non-optional parameter named URL and pass the URL to which the application should redirect to when the plug is fired. You can create any number of optional parameters which will be passed as the URL parameters to the URL which we are redirecting to. Here I have MSG parameter which will carry the message from App A to App B.

Create a navigation link between TO_WINDOW of MAIN view and FROM_VIEW of the ZFAR_APP1 window. Now in the handler method of FROM_VIEW we are going to fire the suspend plug so that we can navigate to Application B suspending our current application. Unlike an exit plug, a suspend plug does not close the current application, it just suspends it and jumps to another URL so that it can resume later. When the exit plug is called in the application B, it automatically resumes application A from the same point we left it. Suspend plug and resume plug always exists in pair. You cannot create either one alone, it will create a runtime exception. Following is the code in the handler method of FROM_VIEW.

method HANDLEFROM_VIEW .

  data msg type string.
  data app2_url type string.
  data node type ref to if_wd_context_node.

  node = wd_context->get_child_node( 'NODE' ).
  node->get_attribute( exporting name = 'MSG' importing value = msg ).

  cl_wd_utilities=>construct_wd_url(
    exporting
      application_name = 'ZFAR_APP2'
    importing
      out_absolute_url = app2_url
  ).

    wd_this->fire_to_app2_plg(
     msg = msg
      url = app2_url
    ).

endmethod.



Now on click of the button 'Open Application B' fire the TO_WINDOW plug.

method ONACTIONOPEN .

    wd_this->fire_to_window_plg(
    ).

endmethod.

That's all with application A.

Creating plugs for Application B


The message which is sent as the URL parameter should be processed in the handler of the DEFAULT inbound plug of the window ZFAR_APP2. Following is the code to process URL parameters and move the message to context node.

method HANDLEDEFAULT .

  data params type tihttpnvp.
  data params_stru type line of tihttpnvp.
  data node type ref to if_wd_context_node.

  wdevent->get_data(
    exporting
      name = if_wd_application=>all_url_parameters
    importing
      value = params
   ).

  node = wd_context->get_child_node( name = 'NODE' ).

  loop at params into params_stru.
    if params_stru-name = 'MSG'.
      node->set_attribute( exporting name = 'MSG' value = params_stru-value ).
    endif.
  endloop.
endmethod.


Create an outbound plug named EXIT in the MAIN view. Now create inbound plugs & outbound plugs in the window as shown below.




Create a navigation link between EXIT of MAIN view and FROM_VIEW of the ZFAR_APP2 window. Now in the handler method of FROM_VIEW we are going to fire the exit plug so that we can navigate back to application A exiting application B.


Following is the code in the handler method of FROM_VIEW.

method HANDLEFROM_VIEW .

    wd_this->fire_back_to_app1_plg(
    ).

endmethod.

Now on click of the button 'Exit' fire the EXIT plug.

method HANDLEFROM_VIEW .

    wd_this->fire_back_to_app1_plg(
    ).

endmethod.

Now activate both the components and create applications in the same name of the components and run application A.



Choose the data that has to be transferred through URL carefully as data are sent through GET and not POST. You can also do some additional security programming by creating tokens from application and transferring only the token number to application B. With the token number application B can request data through some BAPI. Though this URL redirection alternative is not the best solution always, it comes handy in situations when Application A and Application B are totally independent large applications.

Saturday, December 8, 2012

Google Maps in Web Dynpro ABAP

Google Maps integration with Web Dynpro ABAP is one of the most sought out thing. There are a lot of possible ways to this. Google is providing many ways to access map data and we are going to use static maps for this tutorial.

Google Static Maps API returns an image of the map when we call its URL with our own parameters which describe the location and properties of the map (For exact information see here). Recent NetWeaver versions have many ways to bring Google Maps but here we consider doing this in a NetWeaver 7.00 system with IFrame. Let’s jump to the tutorial.

Now we are going to create a simple Google Maps Client using Web Dynpro ABAP. Here we can search for any place and zoom in & out the map.

Create a new component and create the following context node attributes under the root node of the MAIN view.

Context Attribute NameType
LOCSTRING
ZOOMNUM
URLSTRING


Set the default value of ZOOM to 13 which is the optimal zoom for Google Maps.

Now design the layout with an Input Field for search text, a search button, two buttons for zoom in & out, an IFrame for the map, and a text view at the bottom to see the URL we pass to IFrame (this helps in debugging, we can make sure whether the URL we construct is valid) as shown below. MatrixLayout is used here. Create the elements with your own preferred sizes.


Bind the context attribute LOC to Search text input field and bind the URL to both SOURCE property of the IFRAME and TEXT property of the TEXTVIEW.

Now create a method named UPDATE_MAP with the following code.


method UPDATE_MAP .

  data:
        url type string,
        loc type string,
        zoom(2) type n,
        base_url type string
        value 'http://maps.googleapis.com/maps/api/staticmap?size=580x380&sensor=false&center='.

        wd_context->get_attribute( 
             exporting name = 'LOC' 
             importing value = loc ).
        wd_context->get_attribute( 
             exporting name = 'ZOOM'
             importing value = zoom ).

        concatenate base_url loc '&zoom=' zoom into url.

        wd_context->set_attribute( 
             exporting name = 'URL' value = url ).

endmethod.

The above code constructs the URL with the given location, zoom level and size and updates it to the URL context attribute which will reflect in refreshing the IFrame.

Create an action for the search button and add the following code.

method ONACTIONSEARCH_MAP .

  wd_this->update_map( ).

endmethod.

Now for zoom in and zoom out buttons add the corresponding code from the below code.

method ONACTIONZOOM_IN .

  data zoom type i.

  wd_context->get_attribute( 
         exporting name = 'ZOOM' 
         importing value = zoom ).
  add 1 to zoom.
  wd_context->set_attribute( 
         exporting name = 'ZOOM' 
                   value = zoom ).

  wd_this->update_map( ).

endmethod.

method ONACTIONZOOM_OUT .

  data zoom type i.

  wd_context->get_attribute( 
               exporting name = 'ZOOM' 
               importing value = zoom ).
  subtract 1 from zoom.
  wd_context->set_attribute( 
               exporting name = 'ZOOM' 
                         value = zoom ).
  wd_this->update_map( ).

endmethod.

And the output would look like this.


Wednesday, November 7, 2012

Text Symbols in Web Dynpro ABAP

Text symbols are one of the great and most used feature in ABAP. It is very helpful when it comes to internationalization of your application and also manages all your text at one place. ‘Is this feature accessible in Web Dynpro?’ Yes indeed, but it seems Web Dynpro has complicated accessing it. So this article is all about creating a simple application that gets the text from Text Symbols and places the text in a caption.

There is something called Assistance Class in Web Dynpro ABAP. Each Web Dynpro component can have one Assistance Class associated with it. An Assistance Class is any global class that inherits CL_WD_COMPONENT_ASSISTANCE. This class can contain any business logic and this class is automatically instantiated when the component is running. WD_ASSIST is the reference for the instance of this class which you can use from anywhere within the component. Though Assistance Class has other uses, Text Symbols is one that we are going to use now.

Create a class in SE24 and make the CL_WD_COMPONENT_ASSISTANCE as the super class to it.


Now create a new Web Dynpro Component and enter the name of the class you created in the previous step as the Assistance Class for this component (Here its 'ZCL_FAR_TXT_SYMB'). This assistance class is going to contain all the Text Symbols you need and you can retrieve it whenever you want using the method IF_WD_COMPONENT_ASSISTANCE~GET_TEXT( KEY = 'XXX' ) where XXX is the key of the Text Symbol you want.

Now you can go to the MAIN view and from menu select Goto -> Text Symbols. Here you can create text symbols as exactly as you do in ABAP Report programs. Now I create a single Text Element as “Hello World!”. Save and Activate the Text Symbols.

Making use of the Text Symbols is not similar to report programs. Create an attribute in the context of the MAIN view named HELLO of type String. Create a caption in the layout and bind the text property of the caption to HELLO attribute of the context. Now we are going to set the text we have in the Text Symbols to this caption using the following code.

method WDDOINIT .

  data txt type string.

  txt = wd_assist->if_wd_component_assistance~get_text( key = '001' ).
  wd_context->set_attribute( exporting name = 'TEXT' value = txt ).

endmethod.

And we get the following output.


Monday, October 22, 2012

How to Create a Context Menu in Web Dynpro ABAP

Creating customized menu for right click is always cool and creating such menu's in Web Dynpro ABAP is quite easy. In this article we are going to see creating context menu for a TextEdit UI element which has to be filled with the text 'Hello World' on clicking an action in the menu.


Begin by creating a new component and place a TextEdit UI element in the layout with the ID as TEXT_EDIT. Now you need a context attribute to bind TEXT_EDIT. Create an attribute named TEXT of type STRING under the context and bind it to value property of TEXT_EDIT.


Now we have to create the context menu. Right click CONTEXT_MENUS just above the ROOTUIELEMENTCONTAINER in the layout and create a new menu, then create a MenuActionItem under the menu. Use the naming as shown in the picture below. Create an action named SAY_HELLO for the MenuActionItem SAY_HELLO.


In the action, we have to write the code for filling the TextEdit UI element with 'Hello World' which in turn means we have to set 'Hello World' to attribute TEXT of context root node.

method ONACTIONSAY_HELLO .
  wd_context->set_attribute( exporting name = 'TEXT' value = 'Hello World' ).
endmethod.

Now we have to specify for which element the context menu should be shown. This is done through coding as I'm using NetWeaver 7.00. However, in recent versions you will have the property ContextMenuId on certain UI elements (UI elements that inherit CL_WD_CTXT_MENU_PROVIDER) and you don't have to write the following code if you have that property.

We have a method named WDDOONCONTEXTMENU by default with three parameters as shown below.


CONTEXT_MENU_EVENT contains the information about on which element right click took place. CONTEXT_MENU_MANAGER has all the menus associated with a view and the returning parameter MENU is the menu shown on the right click.

method WDDOONCONTEXTMENU .
  if context_menu_event->originator->id = 'TEXT_EDIT'.
    menu = context_menu_manager->
            get_context_menu( 'TEXT_MENU' ).
  endif.
endmethod.

Now the menu appears only if you click above the TEXT_EDIT.


Thursday, October 18, 2012

Anatomy of Table UI and Creating Customized Table UI Dynamically

Whenever we want to create a Table UI dynamically, we go to CREATE_TABLE_FROM_NODE method of CL_WD_DYNAMIC_TOOLS (see here). However in some cases we want the Table to be very much customized. For example, you may need to create a Table UI bounded to only three attributes of a node which already has four attributes and you may also want to represent each column with different UI element. ‘Yes, situations like this are there’. If you are making a service call, it will create a set of context nodes in the component controller. Now if you want to create a table dynamically and bind it to an already existing node, where you don’t want all the fields to be bound.

Anatomy of Table UI


Let’s see the anatomy of Table UI so that we can understand better.

The above diagram gives the anatomy of Table UI. Cell Editor can be any of the following UI elements. All these UI elements that inherit the interface IF_WD_TABLE_CELL_EDITOR (See UI Element Hierarchy).

UI ElementClass
ButtonCL_WD_BUTTON
CaptionCL_WD_CAPTION
CheckBoxCL_WD_CHECKBOX
DrobDownByIndexCL_WD_DROPDOWN_BY_IDX
DropDownByKeyCL_WD_DROPDOWN_BY_KEY
FileDownloadCL_WD_FILE_DOWNLOAD
FileUploadCL_WD_FILE_UPLOAD
ImageCL_WD_IMAGE
InputFieldCL_WD_INPUT_FIELD
LinkToActionCL_WD_LINK_TO_ACTION
LinkToURLCL_WD_LINK_TO_URL
ProgressIndicatorCL_WD_PROGRESS_INDICATOR
RadioButtonCL_WD_RADIO_BUTTON
TextViewCL_WD_TEXT_VIEW
ToggleButtonCL_WD_TOGGLE_BUTTON
TristateCheckBoxCL_WD_TRI_STATE_CHECKBOX

So creating a table dynamically involves creating all these parts dynamically and assembling them to form the desired Table UI. You have to do this all in WDDOMODIFYVIEW (If you are aware not of this, please check out ‘Creating UI Elements Dynamically’).

Now we will start an example. We are going to create a simple Table UI with just two columns, where one column will be input field and the other will be text view. We will be showing data from the database table KNA1 (Customer Master). If we would have created this example statically, the Table UI structure would be as follows.


Create a context node named CUSTOMER with dictionary structure KNA1 and select only the four fields which are shown on the following image as attributes.


This is the node which we are going to bind to the Table UI that we are going to create dynamically. This node has to be filled with data, so that our Table UI can have data on the output. The following code on WDDOINIT fills data to the node from KNA1 table.

method WDDOINIT .

  data:
        node type ref to if_wd_context_node,
        tab type wd_this->elements_customer.

  select * from kna1 into corresponding fields of table tab.

  node = wd_context->get_child_node( 'CUSTOMER' ).
  node->bind_table( tab ).

endmethod.

Finally we are going to write the code in WDDOMODIFYVIEW.

method WDDOMODIFYVIEW .

  data:
        node type ref to if_wd_context_node,
        my_table type ref to cl_wd_table,
        my_col1 type ref to cl_wd_table_column,
        my_col2 type ref to cl_wd_table_column,
        flow_data type ref to cl_wd_flow_data,
        root type ref to cl_wd_uielement_container,
        input_field type ref to cl_wd_input_field,
        text_view type ref to cl_wd_text_view,
        caption type ref to cl_wd_caption.

  cl_wd_table=>new_table(
        exporting
           id = 'TABLEID'
           bind_data_source = 'CUSTOMER'
           visible_row_count = 5
        receiving
           control = my_table
      ).

  my_col1 = cl_wd_table_column=>new_table_column( ).
  input_field = cl_wd_input_field=>new_input_field( 
                       bind_value = 'CUSTOMER.KUNNR' ).
  my_col1->set_table_cell_editor( input_field ).
  caption = cl_wd_caption=>new_caption( ).
  my_col1->set_header( caption ).

  my_col2 = cl_wd_table_column=>new_table_column( ).
  text_view = cl_wd_text_view=>new_text_view( 
                           bind_text = 'CUSTOMER.NAME1' ).
  my_col2->set_table_cell_editor( text_view ).
  caption = cl_wd_caption=>new_caption( ).
  my_col2->set_header( caption ).

  my_table->add_column( my_col1 ).
  my_table->add_column( my_col2 ).

  root ?= view->get_root_element( ).

  flow_data = cl_wd_flow_data=>new_flow_data( my_table ).
  my_table->set_layout_data( flow_data ).

  root->add_child( my_table ).

endmethod.

In the above code, we are creating a Table UI, attaching two columns to it where each configured with its own cell editor and caption. Usually CL_WD_CAPTION should be the column header and here I’m binding it without any caption text so that the system will automatically take the field labels of data elements KUNNR and NAME1. You can also override the default column header with your own by giving some text while creating the caption.

And you finally we get the output.


Monday, October 15, 2012

Creating Table UI Dynamically

Many a time you come across a situation of creating a Table UI dynamically. Especially, when you want to create Table UI based on certain conditions which will be known only during the run time. However, Table is not a simple element, it comprises of Columns, Cell Editors and Popins. To make our job simple, SAP has provided a built in method to create a simple table which can only display data. In this article we are going to use this method to create a Table UI dynamically.

SAP has provided the class CL_WD_DYNAMIC_TOOLS which contains some of the static methods which can be useful for programming dynamically. Now we are going to use the method CREATE_TABLE_FROM_NODE. This comes very handy whenever we want to create a simple table for displaying data. As the name suggests, this method creates a table from a given node making each attribute of the node as a column in the table. Column headers will be same as the field name of the corresponding data element.

Create a node under context as the following picture.

 
Here all the three attributes are of the type STRING in order to give you a simple example. And so you will not be seeing any column header as it is not a data element. Say if the type of CODE is KUNNR, then the column header would be "Customer Name".

method WDDOMODIFYVIEW .

  data:
        my_table type ref to cl_wd_table,
        root type ref to cl_wd_uielement_container,
        my_node type ref to if_wd_context_node.

  if first_time abap_true.

    root ?= view->get_root_element).

    my_node wd_context->get_child_node'DYNTAB' ).

    call method cl_wd_dynamic_tool=>create_table_from_node
      exporting
        ui_parent root
        table_id 'MYTAB'
        node my_node
      receiving
        table my_table.
  endif.

endmethod.

Write the above code in WDDOMODIFYVIEW of the view. In the above code, we just pass the reference of the container to which the table should be added, the ID of the table and reference to node according to which the table should be created and bound. The method automatically creates the required table and adds it to the container.


The above pictures shows the output. The table is empty as there is no data bound to the node (assuming that you know how to work with nodes and data).

This is the simplest way to create a simple table to just display the data. However, its not flexible so that you can create any type of table. In this way, you cannot create a table which contains of table cells made of drop down list or progress bar. You can ask the question 'Can't I create as rich as I do statically?'. 'Of course you can, but with a little longer code for little more enjoyment'. Check out that 'Anatomy of Table UI and Creating Customized Table UI Dynamically'.