In my previous blog post I compared the main data exchange possibilities to share data between UI Building Blocks (UIBBs) in the Floorplan Manager (FPM). The way of sharing data supported by the FPM Framework is called “Wiring”. There are a lot of good guides out there which show how wiring can be done. They usually utilize an example with a list which sends the selected column to another UIBB to show some detail data.
The problem with these examples is usually not the wiring mechanisms of the FPM or the way data is exchanged in general – moreover the way it is implemented.
Usually, the examples include a wire which goes via Lead selection from a certain List UIBB to a Form UIBB which shows the details of the record. The Wire connector contains an attribute of a single record described purely by an ABAP structure. This is misleading and is not the way large enterprise applications are implemented.
The problem with this approach is the fact that you are heading a dead end in most of the cases: What can the Form UIBB, which shows the detail data of the record do with the structure once there are some editable Input Fields showing the attributes of the structure?
Well, data can be written back to the structure and then what? How can the record be validated and saved on the database? If there is no function module to persist the data, you would need to use ABAP OO, and in the cases where we ourselves use the FPM in our own projects, there is always an ABAP OO Backend.
But you can’t call operations on structures. So either the Connector, to which the Form UIBB has access to, would have to persist the data afterwards or the Form UIBB would have to create its instance of the Business Object by itself in order to call something like a SAVE – Method.
Both solutions aren’t really a good idea in terms of maintainability and understandability. That’s why I wrote this blog post.
I set up a demo for this blog post in order to show the concepts which I used. The demo application comprises the following functionality:
- Search flights (implemented by a Search UIBB)
- Show number of results in the header area (OVP – Title)
- Show a list of the results (implemented by a List UIBB)
- Once the users selects an entry, a form UIBB shows the detail data
In order to make clear what the building blocks of the application are, I marked them for you. Of course, the OVP Floorplan title is not a Building block, rather a part of the header area of the OVP. For demo purposes, I also marked it since its information is also getting updated, hence, subject to the wiring mechanisms I will show you in detail.
Sharing selection criteria and the result list
There are two UIBBs involved in sharing the selection criteria and result list with each other. The header area of the OVP which shows the number of results is involved as well. All of them share one object with each other which has the responsibility of selecting all the data which fits to the selection criteria entered in the Search-Block.
The instance is typed as ZIF_FPM_DEMO_WIRE_FLIGHT_SEL. Please note that this is still an interface which describes only what an object needs to do – not how it is implemented.
The reason why this interface inherits ZIF_FPM_DEMO, which contains no methods and no attributes will be explained later on. There will be no further explanation here.
Wiring works based on this interface. Sharing the selection instance starts at the search UIBB. It fetches the selection criteria and pushes it to the data selection instance MO_CONTAINER of type ZIF_FPM_DEMO_WIRE.
CLEAR: et_messages, ev_result.
lt_selopt TYPE if_powl_easy_feeder=>typ_t_selection_parameter,
ls_selopt TYPE if_powl_easy_feeder=>typ_s_selection_parameter,
lt_sel TYPE rsdsselopt_t.
<ls_sel> TYPE rsdsselopt,
<ls_selcrit> TYPE fpmgb_s_search_criteria.
DATA lo_cx_fpmgb TYPE REF TO cx_fpmgb.
DATA ls_message TYPE fpmgb_search_s_t100_message.
LOOP AT it_fpm_search_criteria ASSIGNING <ls_selcrit>.
APPEND INITIAL LINE TO lt_sel ASSIGNING <ls_sel>.
<ls_sel> = cl_fpm_guibb_search_conversion=>to_abap_select_option( <ls_selcrit> ).
ls_selopt-attribute = <ls_selcrit>-search_attribute.
ls_selopt-sign = <ls_sel>-sign.
ls_selopt-option = <ls_sel>-option.
ls_selopt-low = <ls_sel>-low.
ls_selopt-high = <ls_sel>-high.
APPEND ls_selopt TO lt_selopt.
CATCH cx_fpmgb INTO lo_cx_fpmgb.
ls_message-plaintext = lo_cx_fpmgb->get_text( ).
ls_message-severity = if_fpm_message_manager=>gc_severity_error.
APPEND ls_message TO et_messages.
mo_container->set_selection_criteria( lt_selopt ).
If the instance does not yet exist, it is created at runtime during the wiring setup.
ro_data = mo_container.
CHECK mo_container IS INITIAL.
IF mo_connector IS NOT INITIAL.
mo_container ?= mo_connector->get_output( ).
CREATE OBJECT mo_container TYPE zcl_fpm_demo_wire_flight_sel.
Targets are both the List – UI Building Block as well as an OVP Exit (which implements IF_FPM_OVP_CONF_EXIT) which is also implementing the Application Controller Interface (IF_FPM_APP_CONTROLLER) and the UIBB feeder model interface (IF_FPM_UIBB_MODEL). This way it can also serve as a target for wiring. This OVP Exit updates the Application header title.
The list UIBB accesses the instance via the connector which has been passed by the FPM framework during wiring setup.
DATA lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel.
lo_datacontainer ?= mo_connector->get_output( ).
mt_sflight = lo_datacontainer->get_list( ).
The OVP Exit receives the instance via its feeder model and hooks into the AFTER_PROCESS_EVENT-Method:
METHOD after_process_event .
DATA lo_ovp TYPE REF TO if_fpm_cnr_ovp.
lo_ovp ?= wd_this->mo_fpm->get_service( cl_fpm_service_manager=>gc_key_cnr_ovp ).
DATA lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel.
DATA lv_port_identifier TYPE fpm_model_port_identifier.
DATA lv_title TYPE string.
DATA lv_num TYPE i.
DATA ls_content_area TYPE if_fpm_ovp=>ty_s_content_area.
CHECK wd_this->mo_feeder_model IS NOT INITIAL.
lv_port_identifier = zif_fpm_demo_wire_flight_sel=>gc_name.
lo_datacontainer ?= wd_this->mo_feeder_model->get_outport_data( iv_port_type = 'LS' iv_port_identifier = lv_port_identifier ).
ls_content_area = lo_ovp->get_current_content_area( ).
CHECK: lo_datacontainer IS NOT INITIAL, lo_ovp IS NOT INITIAL.
lv_num = lo_datacontainer->get_number_of_results( ).
lv_title = lv_num.
CONCATENATE 'Flight Search -' lv_title 'results' INTO lv_title SEPARATED BY space.
ls_content_area-title = lv_title.
lo_ovp->change_content_area_restricted( iv_content_area_id = ls_content_area-id iv_title = lv_title ).
The wiring setup finalizes the implementation and makes clear what the data exchange directions are. The connector class which is used for that is ZCL_FPM_DEMO_FL_SEARCH_CONN. There will be two wires as both the flights result list as well as the OVP title need to be updated based on the flight results.
It contains an instance of type ZIF_FPM_DEMO_WIRE, the parent interface of the current business object’s interface.
Sharing the selected entry as a business object
The List UIBB which shows the flight results needs to setup another business object which does not contain a list of flights, rather the exact record, which has been selected by the user. The flight record is represented by an instance of type ZIF_FPM_DEMO_WIRE_SFLIGHT. It inherits from ZIF_FPM_DEMO as well as the previously introduced business object of type ZIF_FPM_DEMO_WIRE_FLIGHT_SEL.
As mentioned earlier, it contains an instance of type ZIF_FPM_DEMO_WIRE, the parent interface of both business object’s interfaces.
Now it is getting clear, what ZIF_FPM_DEMO_WIRE is used for. It is just a marker interface, which gives a hint, which business objects are shared across the application. As the connector references an instance of that type, it can be referencing both the selection as well as the flight record. There is no technical or functional requirement for that – the reason why I didn’t use a more generic REF TO OBJECT for the instance is the fact that I just wanted to make clear for someone who reads and needs to understand the code, what the intended context of the current connector is.
I didn’t implement any update operations for this demo, but it would be yet another method for the existing business object besides the SET_SFLIGHT( )-Method, for example SAVE( ).
Since the feeder has already access to the wired business object, it has also access to its public methods. The SAVE( )-method would implement the update operations and could be called directly from the form feeder which provides the UI operations for the flight details building block.
In contrast to a possible solution, where only a flat data structure was exchanged, this is a much more convenient way of accessing and modifying the data provided by the backend.
The nugget can be found here.