Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
MarcelloUrbani
Active Contributor
0 Kudos

<body>Introduction

Trying to understand how abap webdynpro works, i stumbled upon

this tutorial

by Thomas Jung  (/people/thomas.jung/blog)

about recreating transaction SE16 in a webdynpro application.I found it

very good, but as many step-by-step tutorials is too concerned with how

you do something than to explain why you are doing it.

So I tried to

get the same job done in a slightly different way,  both about the

coding and the comments on what's going on.The main reason for which

I'm writing this is to see if my understanding of this technology is

good enough to explain it, but I hope it'll be useful to other users

too.

The full

source code

of the application described below is available as saplink  (http://code.google.com/p/saplink/) packages (see the readme file before importing, there are a couple of minor issues with saplink) on

my google code page

, so here you'll only find a few snippets.

Also

note that to keep things simple this application doesn' t have user

rights management and only uses very basic error handling.

Create the application

I

start creating a new webdynpro component ZSE16_URB with a window named

MAIN and a webdynpro application with the same name. Of course the

applicartion will use the component I just created:

Not much to understand here, basically I only set a few names and descriptions.

If you have trouble importing the application slinkee, just copy whatr you see above and save it, it's a pretty simple task.

Set up navigation path

The application has a single window with three views, with an interface that mimics the original SE16.

I

create the three views and leave them empty, with only the inbound and

outbound plugs required to manage a forward flow and backward one:

    table selection view (TABSEL) <-> main selection screen (SELSCR) <-> output list (LIST)

Then I set up the navigation path in the window:

The default view is TABSEL.

The actual navigation will be activated in the view event handlers using methods like wd_this->

Fire_To_Selection_Plg(

), where the bold part is the name of the plug. Those methods could

have parameters too, but I don't need any in this case because most of

the data is handled globally in the componentcontroller.

Every inbound plug has a handler method such as

HANDLEBACK, in which you could eventually add parameters like what you defined in the matching outbound plug(s).

At

this point I have a valid application, accessible through the URL shown

in the webdynpro application.Of course it still doesn't do anything but

display an empty screen.

Helper class

Before I start coding, in order to define language dependent texts I

have to define an helper class. To do so I simply enter the name of my

helper class ZCL_SE16_URB_ASSIST in the component header and let the

system authomatically create it when saving the modified

component.

The

helper class is very handy because an instance of this class is created

during the application startup and is accessible almost everywhere in

the component, so it acts a little like a global data and code

repository.Abuse of this feature may be very dangerous, an most

programming shortcuts, so I decided to only use it for messaging.

I

then added a public message manager attribute (MESSMAN), a method to

set it (DO_INIT), and another to show error messages using their

IDs.The latter is handy because the 'some text'(T01) idiom doesn't work

in a webdynpro environment, except in the helper class methods.

A little warning on the MESSMAN trick: I tested this on my system, but

I don't really know what I'm doing: if the message manager can change

during a session it may be a BAD idea to store it in the helper class.

Doing

the job of DO_INIT in the constructor would be nicer, but I don't think

it's possible to get it there, let alone how to do it.

Component controller attributes

I defined three attributes:

    • PARMSEL:

a reference of the context element. Used as a shortcut to move data

from SE16_PARMS to the context and back.Wouldn't be needed if I could

figure out bind them together properly.

CORE 

of type ZCL_SE16_CORE, which does most of the work. I described

this class in a later blog  (A class for dynamic queries) to keep this entry focused on webdynpro

technology.Later I'll add a few methods to this controller to expose to the views the functionality provided by the attributes above.

Another

way to get the same result is to put the attributes above into the

helper class: this way I could use them directly from the views and

spare a few lines of code.While this approach wouldn't hurt too much in

a toy application like this, it would be a terrible idea in the real

world, so I'll try to behave like a nice guy and code as I'm expected

to.

Global context

In a webdynpro component the data

is linked to the screen elements through the context.Every controller

has its context, and data shared between more than one view should be

put in the componentcontroller context.

In the global context I define a node PARMS with an attribute for every field in SE16_PARMS.

The next step is to use method WDDOINIT to bind attribute SE16_PARMS to the context:

method wddoinit .

  data:parms type ref to if_wd_context_node.

  try.

*set up the message manager

      wd_assist->do_init( wd_this ).

*Initialize defaults

      wd_this->se16_parms-tabname = 'SFLIGHT'.

      wd_this->se16_parms-maxrec = 500.

      wd_this->se16_parms-keysonly = abap_false.

      wd_this->se16_parms-tabdesc = ''.

*Bind the context

      parms = wd_context->get_child_node( name = wd_this->wdctx_parms ).

      parms->bind_element( new_item = wd_this->se16_parms set_initial_elements = abap_false ).

      wd_this->parmsel = parms->get_element(  ).

    catch cx_root.

      "This will dump if the error occurred creating the message manager

      wd_assist->report_error( 'E01' ).

  endtry.

endmethod.

As you can see from the code, I also initialize the message

manager I added to the helper class and then use it to report an error.

Table selector view<br />First

of all we copy and bind the context of the view with the global one. To

do so I simply open the context tab, drag node PARMS from the global

context to the local one and answer yes to the box below:

Now

it's time to actually draw the view, so I move to the layout tab and I

start dragging visual components in the screen area.Not much to say

here, the only interesting stuff is the tabname inputfield, with its

value property bound to TABNAME context element, and the execute

button's action code, the other stuff is only there to improve the view

layout.Here you can see the layout tree and the properties of the

tabname inputfield:

Here you can see the action method of the execute button, which is mostly a wrapper around a componentcontroller's method:

method onactionexecute .

  try.

      wd_comp_controller->createcore(  ).

      wd_this->fire_to_selection_plg( ).

    catch cx_root.

      wd_assist->report_error( 'E02' ).

  endtry.

endmethod.

Anyway it shows how to fire an outbound plug and how I use the helper class to display error messages.

Using webdynpro components

For the next view (SELSCR) I need to use another webdynpro component, WDR_SELECT_OPTIONS.

Doing

so requires several tasks in different part of the application, the

first being to add it to the 'Used components' tab of the ZSE16_URB

component. I also add the SALV_WD_TABLE,needed in the last view.

Selection screen view

First of all, I go to the properties tab and use the create button to add references to the component I just added:

I'm

not done with this guy yet: as we'll see it also needs some coding, a

VIEW_CONTAINER_UIELEMENT in this view and a setting in the window

structure to specify which component has to be embedded there.

Now

I move to the view layout and design the screen.Some caption texts are

bound to context elements to display table name and description. The

last item we need is a VIEW_CONTAINER_UIELEMENT to put our select

options in.The layout looks like this:

later I'll move to the main window to embed the actual selection screen in this view container.

I

define two attributes for this view: SEL_HANDLER, a reference to

IF_WD_SELECT_OPTIONS used to create and manipulating the dynamic select

options, and OLDTABNAME, of type TABNAME, used to check wether we

should rebuild our select-options or not.

In the initialization method I'll take care of initializing the component usage SEL_HANDLER:

method wddoinit .

  data: l_ref_cmp_usage type ref to if_wd_component_usage,

        seoptif type ref to iwci_wdr_select_options.

*initialize component usage

  l_ref_cmp_usage =   wd_this->wd_cpuse_select_options( ).

  if l_ref_cmp_usage->has_active_component( ) is initial.

    l_ref_cmp_usage->create_component( ).

  endif.

*initialize a handler for select-options

  seoptif = wd_this->wd_cpifc_select_options( ).

  wd_this->sel_handler = seoptif->init_selection_screen( ).

* init the select screen (hide a few buttons)

  wd_this->sel_handler->set_global_options( i_display_btn_cancel  = abap_false

                                            i_display_btn_check   = abap_false

                                            i_display_btn_reset   = abap_false

                                            i_display_btn_execute = abap_false ).

endmethod.

I should  have added a little error handling here, but I was too lazy.

The only other significative methods are

HANDLEFROM_TABSEL and ONACTIONRUNQUERY, but they are only tiny wrappers for a few component controller methods described below.

Component controller methods for select-options handling

In

the method below the only code of some interest on a webdynpro

perspective is the calls to the handler (a reference to

IF_WD_SELECT_OPTIONS) to clean and create the select options

dynamically.If the table hasn't changed since the last time I ran this

method, no action is taken.

method create_selopts .

  data: field type dfies,

        rangetab type ref to data,

        ftype type string,

        fname type string..

* If the last generated select options was for the same table there's nothing to do

  if wd_this->core->tabname = oldtab.

    return.

  endif.

*cleanup

  handler->remove_all_sel_screen_items( ).

*loop to generate the select options

  loop at wd_this->core->userfields into field.

    ftype = field-rollname.

    fname = field-fieldname.

    rangetab = handler->create_range_table( i_typename = ftype ).

    handler->add_selection_field( i_id = fname it_result = rangetab ).

  endloop.

  oldtab = wd_this->core->tabname.

endmethod.

The other interesting method handles the select options to the

core class to generate a dynamic query and store its result for the

last view.

method run_query .

  data: sel_fields type if_wd_select_options=>tt_selection_screen_item,

        sel_field  type if_wd_select_options=>t_selection_screen_item.

  wd_this->parmsel->get_static_attributes( importing static_attributes = wd_this->se16_parms ).

  handler->get_selection_fields( importing et_fields = sel_fields ).

  wd_this->core->clear_sel( ).

  loop at sel_fields into sel_field.

    wd_this->core->add_sel( id = sel_field-m_id rangetab = sel_field-mt_range_table ).

  endloop.

  wd_this->core->exec_query( wd_this->se16_parms-maxrec ).

endmethod.

List view<br />This view looks very much like the

selection screen, but has SALV_WD_TABLE as a component usage in place

of WDR_SELECT_OPTIONS and a component ALV_IF which is a reference to

IWCI_SALV_WD_TABLE.<br /><div id="fk2j" style="padding: 1em 0pt; text-align: left">!https://weblogs.sdn.sap.com/weblogs/images/251709722/File_002.png|alt=image|src=https://weblogs.sdn....!<br /><br />To

complete the work I'll have to edit the main window to embed the actual

ALV table screen in this view container, as shown below.<br /><br />Most of

the work for getting the ALV list on screen is handled by the following

method (I think the code has enough comments to tell you what's going

on).For once doesn't simply rely on the componentcontroller because it

has more to do with screen output that base logic, so I felt this was

the right place for this job.<br /><br />

method handlein .<br />

  data: root     type ref to if_wd_context_node_info,<br />

        dyn_node type ref to if_wd_context_node,<br />

        tab type ref to data,<br />

        nodename type string.<br />

  field-symbols:<tab> type table.<br />

  try.<br />

      root = wd_context->get_node_info( ).<br />

      nodename = wd_comp_controller->get_tabname( ).<br />

*create dynamic context node<br />

      cl_wd_dynamic_tool=>create_nodeinfo_from_struct(<br />

        parent_info = root<br />

        node_name = nodename<br />

        structure_name = nodename<br />

        is_multiple = abap_true ).<br />

      dyn_node = wd_context->get_child_node( name = nodename ).<br />

* Retrieve data and put it into the dynamic context<br />

      tab = wd_comp_controller->get_table(  ).<br />

      assign tab->* to <tab>.<br />

      dyn_node->bind_table( <tab> ).<br />

<br />

*Link the dynamic node to the ALV component<br />

      wd_this->alv_if->set_data( r_node_data = dyn_node  ).<br />

    catch cx_root.<br />

      wd_assist->report_error( 'E05' ).<br />

  endtry.<br />

endmethod.<br /><br />Complete window structure

Last

but not least we have to embed the used components views into the view

containers. To do so I go to the main window structure and expand the

tree since I find the VIEW_CONTAINER_UIELEMENT items, then I select

'Embed view' from their context menu.In the last picture you can see

both the final window structure and the parameters I used to get it.

!https://weblogs.sdn.sap.com/weblogs/images/251709722/File_007.png|height=395|alt=image|width=600|src...!</body>

3 Comments