Wiring heterogeneous feeder models together – or the fairytale of Hänsel and Gretel

Introduction

Recently I was asked as a reply to one of my previous blog posts, wether it is possible to wire multiple UI building blocks to one target. The target could be a transaction component in order to save all of these models when the users presses the “Save” button in the UI.
The exact question was:

“If we have a GAF with several steps and at the last step we want to save all the GUIBB’s. Some GUIBB’s have there own model.We have created a WD component with interface IF_FPM_TRANSACTION. We want to transfer all the GUIBB models to the transaction component.Wiring would be nice, but we have more sources to the transaction component. That is the reason why I would like to have more sources to a target. What would you do? We can always use a factory singleton, but if we do that then the wiring would not be needed anymore.”

Unfortunately, SAP says clearly, that every UIBB may serve as a source for wiring multiple times, while it may serve as a target only once.
To answer the question, having a FPM transaction component as a target for multiple UIBBs is not possible. But there is a workaround, which could help solving this issue.

The fairytale of Hänsel and Gretel

The basic question is, how heterogeneous feeder models can be connected to a single UIBB feeder model which eventually performs a Save-operation on all the received feeder models.
This is where a commonly known fairytale comes into the scene.
Imagine, we had a Guided Activity FPM application. The first steps shows the “Hänsel” UI-Building Block which has its own feeder model to read the data and save it afterwards. The second step is the “Gretel” UI Building Block which has a totally different feeder model with its own data. The main question is: What happens to these feeder models and what are the further operations invoked?
Everyone who knows the fairytale already has the answer: Both are held captive by a witch. But what does that have to do with our FPM application?

Technically spoken, we need to transfer both feeder models to a composite feeder model. This is, where the composite pattern is used.
Both feeder models need to implement the same interface ZIF_FPM_SAVEABLE_FEEDER_MODEL which forces its implementers to implement a SAVE( )-Method.
ZIF_FPM_COMPOSITE_MODEL inherits from ZIF_FPM_SAVEABLE_FEEDER_MODEL and describes what a composite feeder model should be able to do. Despite the SAVE( )-method, it requires another method +ADD_MODEL(IO_MODEL : ZIF_FPM_SAVEABLE_FEEDER_MODEL). The composite feeder model is implemented by ZCL_WITCH_MODEL.
This means, the witch model may include other feeder models and subjects them to a common treatment, which is, in this very example, the Save-operation.
Wiring Model
The wiring behaviour of the UIBBs now needs to work like this:
1. If the input which comes from a wire, is not initial, the UIBB checks if the input is a composite feeder model of type ZIF_FPM_COMPOSITE_MODEL (to do so, simply down-cast the connector’s output to an instance of type ZIF_FPM_COMPOSITE_MODEL and catch the CX_SY_MOVE_CAST_ERROR exception, if raised)
2. If no input was provided, or the input had the wrong data type, a new composite model of type ZIF_FPM_COMPOSITE_MODEL is created by the UIBB
3. A new outport of the UIBB is created which outputs a composite model of type ZIF_FPM_COMPOSITE_MODEL
4. The UIBB’s feeder model is added to the output using the ADD_MODEL-Method of the composite model

This approach allows you to wire multiple UIBBs, who have a completely different feeder model, together. The output of every feeder model will be an instance of type ZIF_FPM_COMPOSITE_MODEL. Now we’ve got several wiring possibilities to wire the UIBBs and the FPM transaction component together.

Option 1 – The FPM transaction component pushes ZIF_FPM_COMPOSITE_MODEL to the feeders

Both feeders receive the witch, which is an instance of type ZIF_FPM_COMPOSITE_MODEL. Every UIBB adds its own model by calling the ADD_MODEL method. In the end, the witch holds captive all the feeder models.
Option 1 - FPM transaction component pushes ZIF_FPM_COMPOSITE_MODEL to the feeders

Option 2 – The FPM transaction component receives ZIF_FPM_COMPOSITE_MODEL

The UIBBs are connected in series. The last chain link is the transaction component. In contrast to the fairytale, the witch is created by Hänsel and is handed over to Gretel. The transaction component receives the witch in the end. Again, the witch holds captive the two other feeder models.
Option 2- The FPM ransaction component is the last chain link

Conclusion

If feeder models need to be transferred using wires, a common interface and the use of the composite pattern may be an option, if the implementations of the feeder models differ. This is often the case, if the UIBBs in the application were initially build up for another use case, but now need to be combined for a new application.
However, the interface which needs to be implemented yby the feeder models depends on the type of common treatment you want to apply to the feeder models. So there is still the effort of applying the above shown changes to existing feeders.

Perform Save-operations using a wire transaction handler in the FPM – Refactoring

Introduction

In the previous blog post, I introduced the Save-operation by using a wire transaction handler. The main statement below this blog post was, that an event can be enriched by an OVP-exit before it is getting processed by a wire transaction handler. The event receives a new parameter value, which is an instance of a saveable business object, that is, an ABAP Object implementing ZIF_FPM_DEMO_SAVEABLE.
There is a small issue with the way the wire transaction handler handles the event. In this short blog post I would like you to show what the problem is and how it can be fixed.

The existing wire transaction handler implementation

Right now, the wire transaction handler implementation reads the business object by a hard coded key-value:

METHOD if_fpm_wire_model_transaction~after_process_event.
DATA: lo_savable TYPE REF TO zif_fpm_demo_saveable.
CASE io_event->mv_event_id.
WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
TRY.
io_event->mo_event_data->get_value(
EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id
IMPORTING ev_value = lo_savable ).
CHECK lo_savable IS NOT INITIAL.
lo_savable->save( ).
CATCH cx_sy_move_cast_error.
ENDTRY.
ENDCASE.
ENDMETHOD.

The value represented by constant zif_fpm_demo_saveable=>gc_event_parameter_id serves as a key to retrieve the business object that has been hooked previously to the event by the OVP-exit.

This coding violates the Open-Closed-Principle which says “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification” (Meyer, Bertrand (1988). Object-Oriented Software Construction. Prentice Hall. ISBN 0-13-629049-3)
Why is this transaction handler affected by this principle?
The answer is quite easy: Every time we reuse the wire transaction handler in another application, we would also need to read business objects which have been hooked into the event by other exits, possibly with other key values. If this was the case, every reuse would cause the coding shown above to be changed in order to support other key values:

METHOD if_fpm_wire_model_transaction~after_process_event.
DATA: lo_savable TYPE REF TO zif_fpm_demo_saveable.
CASE io_event->mv_event_id.
WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
TRY.
io_event->mo_event_data->get_value(
EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id
IMPORTING ev_value = lo_savable ).
CHECK lo_savable IS NOT INITIAL.
lo_savable->save( ).
CATCH cx_sy_move_cast_error.
ENDTRY.

TRY.
io_event->mo_event_data->get_value(
EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id2
IMPORTING ev_value = lo_savable ).
CHECK lo_savable IS NOT INITIAL.
lo_savable->save( ).
CATCH cx_sy_move_cast_error.
ENDTRY.
ENDCASE.
ENDMETHOD.

You recognized the changed coding already: another business object is read from the event with the key zif_fpm_demo_saveable=>gc_event_parameter_id2.
The way we deal with changes here is not a good practice.

Changes to the existing logic

Instead of supporting every thinkable FPM parameter key in the wire transaction handler, we improve the logic a little bit and make it more smart: The event parameters are now iteratively read and the handler tries to down-cast it to an instance of type ZIF_FPM_DEMO_SAVEABLE. If the down-cast fails, nothing happens (no SAVE-method is invoked) and the logic continues with the next event parameter until no more parameters are left.

METHOD if_fpm_wire_model_transaction~after_process_event.
DATA: lo_saveable TYPE REF TO zif_fpm_demo_saveable.
DATA lt_keys TYPE if_fpm_parameter=>t_keys.
FIELD-SYMBOLS LIKE LINE OF lt_keys.

CASE io_event->mv_event_id.
WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
lt_keys = io_event->mo_event_data->get_keys( ).
LOOP AT lt_keys ASSIGNING .
TRY.
io_event->mo_event_data->get_value(
EXPORTING iv_key =
IMPORTING ev_value = lo_saveable ).
CHECK lo_saveable IS NOT INITIAL.
lo_saveable->save( ).
CATCH cx_sy_move_cast_error.
CONTINUE.
ENDTRY.
ENDLOOP.
ENDCASE.
ENDMETHOD.

Now the wire transaction handler does not need any further update for any new use case any longer. It works, no matter if you pass zero, one or multiple different instances of type ZIF_FPM_DEMO_SAVEABLE to it.
In order to avoid parameter key collisions, the OVP exit is also updated a little bit. Instead of setting the business object with the hard coded key value represented by ZIF_FPM_DEMO_SAVEABLE=>GC_EVENT_PARAMETER_ID, let’s create a guid as a key value by using the CL_UUID_FACTORY.
Since the wire transaction handler does not rely on any specific key values any longer, it is going to work even after this change:

METHOD override_event_ovp.
wd_this->mo_ovp = io_ovp.
DATA lo_event TYPE REF TO cl_fpm_event.
DATA: lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel,
lo_savable TYPE REF TO zif_fpm_demo_saveable.
DATA lv_port_identifier TYPE fpm_model_port_identifier.
DATA lv_key TYPE string.
DATA lo_generator TYPE REF TO if_system_uuid.

CALL METHOD cl_uuid_factory=>create_system_uuid
RECEIVING
generator = lo_generator.
lv_key = lo_generator->create_uuid_c22( ).

lv_port_identifier = zif_fpm_demo_wire_flight_sel=>gc_name.

lo_event = io_ovp->get_event( ).
CASE lo_event->mv_event_id.
WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
TRY.
lo_datacontainer ?= wd_this->mo_feeder_model->get_outport_data( iv_port_type = 'LS' iv_port_identifier = lv_port_identifier ).
CHECK lo_datacontainer IS BOUND.
lo_savable ?= lo_datacontainer->get_lead_selection( ).
lo_event->mo_event_data->set_value( iv_key = lv_key iv_value = lo_savable ).
CATCH cx_sy_move_cast_error.
ENDTRY.
ENDCASE.
ENDMETHOD.

Benefits

The wire transaction handler is now much more robust against changes in any exit which enriches the save-event with a saveable business object. This increases reusability without cutting functionality.
The usage of the handler has been made easier dramatically since it tries to extract instances of type ZIF_FPM_DEMO_SAVEABLE from any event parameter instead of event parameters with a specific key. Any application that reuses this handler does not need to be aware of the parameter key value of that object, it just needs to make sure, that an instance of a saveable business object has been hooked into the event data with a freely defineable key value that before the handler is getting executed – and that’s it.

Perform Save-operations using a wire transaction handler in the FPM

Introduction

In the previous blog post I figured out how wiring based on business objects might work in the Floorplan manager. The example application provides a Search component to select records based on the table SFLIGHT. Once a result entry has been selected, the flight data record is shown in a detail view below the result list. While the result list is implemented by a List-UIBB, the detail view is implemented by a Form-UIBB.

Any follow-up steps have not been implemented then, this includes also the possibility to save the edited record back to the database.

The subject of this blog post is how a Save-operation could be implemented based on a wire transaction handler.

I decided to not use the interface IF_FPM_TRANSACTION which can be implemented by any UIBB in the application, rather, I decided to use a Transaction Handler for wire models.  The drawback of this approach is that a wire transaction handler cannot serve as a target for wires, hence, has usually no access to Business objects.

One opportunity to pass the business object to the handler might be to share it as a singleton instance. This requires the existence of a registry or any other singleton mechanism. Another possibility is the data exchange based on events.

In this very blog post, the business object is going to be shared as a parameter of the FPM_SAVE- event.

For now, let’s again recap the wires which are in use by the demo application. However besides this introduction it is strongly recommended to read the previous blog post if you did not yet read it.

The existing application

The selection criteria and the selection of the flight list is implemented by the list selection wire (ZCL_FPM_DEMO_WIRE_FLIGHT_SEL which implements ZIF_FPM_DEMOWIRE_SFLIGHT_SEL).
Wires of the application I

The selected flight data record is represented by an instance of type ZIF_FPM_DEMO_WIRE_SFLIGHT.
04 - Wires of the application II

Both wires have specific operations offered to the UIBBs or OVP Exit which use these wires.
ZIF_FPM_DEMO_WIRE_SFLIGHT

ZIF_FPM_DEMO_WIRE_FLIGHT_SEL

In this blog post, the first wire is going to be extended to hold a lead selection instance of the second wire. The second wire which represents a single flight data record will be enhanced with a Save-operation. In combination with some other changes, this allows the user to edit the flight data and save it back to the database.

Sharing the business object

A new Save-Button will be added to the application’s OVP toolbar.
In order to allow the wire transaction handler to access the business object, the OVP exit will catch the Save-Event which is triggered by the save- button and will enrich the event data with a new Name-Value Pair which represents an instance of a saveable business object, that is, the wire which represents a single entry of the flight data list (It would be a good idea to work with an abstraction to the specific flight data instance as well, so we can possibly reuse the wire transaction handler later on in another application)
Since the OVP exit has access to the list selection instance only we need to provide access to the selected business object through the list selection wire.

Enhancements to the current application

First of all, we include an OVP-toolbar button to allow the user to trigger the Save-operation.
Save button
The list selection wire will need to store the selected flight record to allow the OVP Exit to get access to the currently selected record. Therefore the methods GET_LEAD_SELECTION and SET_LEAD_SELECTION are added to the interface ZIF_FPM_DEMO_WIRE_FLIGHT_SEL which describes, what operations have to be supported by this wire. In the implementing class, a new attribute MO_LEAD_SELECTION is included and the corresponding Getter- and Setter method are implemented.
ZIF_FPM_DEMO_WIRE_FLIGHT_SEL+new

Now we need to call SET_LEAD_SELECTION in ZCL_FPM_DEMO_FLIGHT_LIST, method IF_FPM_GUIBB_LIST~PROCESS_EVENT, which handles the lead selection event in the list.

METHOD if_fpm_guibb_list~process_event.
DATA lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel

lo_datacontainer ?= mo_connector->get_output( ).
CASE io_event->mv_event_id.

WHEN 'FPM_GUIBB_LIST_ON_LEAD_SELECTI'.
CHECK lo_datacontainer IS NOT INITIAL.
READ TABLE mt_sflight INTO ls_sflight INDEX iv_lead_index.
IF sy-subrc = 0.
CREATE OBJECT mo_sflight TYPE zcl_fpm_demo_wire_sflight.
mo_sflight->set_sflight( ls_sflight ).
lo_datacontainer->set_lead_selection( mo_sflight ).
ENDIF.
ENDCASE.
ENDMETHOD.

The selected flight record is show in the detail section of the screen. Its feeder does not yet have a FLUSH-implementation which needs to be implemented. Its purpose would be to transfer the entered values back to the flight record instance.
Therefore the form feeder class ZCL_FPM_DEMO_FORM_SFLIGHT_DET receives a FLUSH-method implementation:

METHOD if_fpm_guibb_form~flush.
CHECK mo_connector IS NOT INITIAL.
mo_sflight ?= mo_connector->get_output( ).
CHECK mo_sflight IS NOT INITIAL.

FIELD-SYMBOLS TYPE any.
ASSIGN is_data->* TO .
DATA ls_sflight_key LIKE ms_sflight.
MOVE-CORRESPONDING TO ms_sflight.
MOVE-CORRESPONDING TO ls_sflight_key.
ms_sflight-carrid = ls_sflight_key-carrid.
ms_sflight-connid = ls_sflight_key-connid.
ms_sflight-fldate = ls_sflight_key-fldate.
mo_sflight->set_sflight( ms_sflight ).
ENDMETHOD.

Now, the OVP exit has also access to the currently selected flight record instance through its configured list selection wire. The OVP exit is implemented by the webdynpro component ZWDYN_FPM_FLIGHT_OVP_EXIT. The implementation of method OVERRIDE_EVENT_OVP now needs to try to read the flight record instance from the wire model and enrich the Save-Event with the flight record data object. Please note that we pass an instance of type ZIF_FPM_DEMO_SAVEABLE rather than ZIF_FPM_DEMO_WIRE_SFLIGHT.

METHOD override_event_ovp.
wd_this->mo_ovp = io_ovp.
DATA lo_event TYPE REF TO cl_fpm_event.
DATA: lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel,
lo_saveable TYPE REF TO zif_fpm_demo_saveable.
DATA lv_port_identifier TYPE fpm_model_port_identifier.
lv_port_identifier = zif_fpm_demo_wire_flight_sel=>gc_name.
lo_event = io_ovp->get_event( ).
CASE lo_event->mv_event_id.
WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
TRY.
lo_datacontainer ?= wd_this->mo_feeder_model->get_outport_data( iv_port_type = 'LS' iv_port_identifier = lv_port_identifier ).
CHECK lo_datacontainer IS BOUND.
lo_saveable ?= lo_datacontainer->get_lead_selection( ).
lo_event->mo_event_data->set_value( iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id iv_value = lo_saveable ).
CATCH cx_sy_move_cast_error.
ENDTRY.
ENDCASE.
ENDMETHOD.

ZIF_FPM_DEMO_SAVEABLE serves as a generic interface for a save operation. It is implemented by the wire which represents the flight record (ZCL_FPM_DEMO_WIRE_SFLIGHT)
Interface ZIF_FPM_DEMO_SAVEABLE

The class ZCL_FPM_DEMO_WIRE_MODEL_TRANS serves as the application wire transaction handler. It implements the standard FPM interface IF_FPM_WIRE_MODEL_TRANSACTION and therefore takes part in the event loop. The method AFTER_PROCESS_EVENT is implemented and handles the SAVE-Event which will later on be triggered once the Save-Button has been pressed. The method implementation tries to read an instance of type ZIF_FPM_DEMO_SAVEABLE from the event data and eventually call the SAVE-Method.

METHOD if_fpm_wire_model_transaction~after_process_event.
DATA: lo_savable TYPE REF TO zif_fpm_demo_saveable.
CASE io_event->mv_event_id.
WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
TRY.
io_event->mo_event_data->get_value(
EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id
IMPORTING ev_value = lo_savable ).
CHECK lo_savable IS NOT INITIAL.
lo_savable->save( ).
CATCH cx_sy_move_cast_error.
ENDTRY.
ENDCASE.
ENDMETHOD.

Once the transaction handler class has been implemented, we register the transaction handler in the OVP-Component FPM_OVP_COMPONENT, Configuration ZFPM_DEMO_OVP_FLIGHT_SEARCH
Register wire transaction model

Why should we not simply enhance the OVP-Exit?

The reason why I didn’t just implement the Save-operation directly in the OVP-Exit is the Separation of concerns design principle.
In the case of having an exit only and no transaction handler, the OVP Exit would have to evaluate the save event and trigger the SAVE-Method, besides its task of updating the OVP floorplan title with the number of results. Also, an OVP Exit is not reusable in OIF or GAF floorplans, and this would also apply to its transaction logic, if coupled too strongly.
Right now, with the current implementation, it only does, what an exit is intended to do: It catches certain events like the Save-event and enriches it with some data, if necessary. The actual SAVE-Method call is triggered by another component which actually does not care about how or by whom the event has been enriched – it just expects the business object along with the event data. This decouples the execution of the SAVE-Command from other aspects of the application and eventually leads to a better design and maintainability. There is no real benefit yet, but in case a more complex transaction logic would be needed, we could easily provide the implementation of these aspects, like commits or rollbacks, in a specific transaction handler, not within the OVP Exit.

The nugget file can be found here.

Wiring based on Business Objects in Floorplan Manager

Introduction

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.

Demo

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

Overview of the application

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.
02 - OVP-Floorplan, UIBBs and title of the application

Data sharing

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.

Interface ZIF_FPM_DEMO_SAVEABLE
ZIF_FPM_DEMO_WIRE_FLIGHT_SEL new

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.


METHOD if_fpm_guibb_search~process_event.
CLEAR: et_messages, ev_result.
DATA:
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.
FIELD-SYMBOLS:
<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.
CASE io_event->mv_event_id.
WHEN if_fpm_guibb_search=>fpm_execute_search.
TRY.
LOOP AT it_fpm_search_criteria ASSIGNING <ls_selcrit>.
CLEAR ls_selopt.
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.
ENDLOOP.
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.
ENDTRY.
mo_container->set_selection_criteria( lt_selopt ).
ENDCASE.
ENDMETHOD.

If the instance does not yet exist, it is created at runtime during the wiring setup.


METHOD if_fpm_feeder_model~get_outport_data.
try_get_container( ).
CASE iv_port_identifier.
WHEN zif_fpm_demo_wire_flight_sel=>gc_name.
ro_data = mo_container.
ENDCASE.
ENDMETHOD.

METHOD try_get_container.
CHECK mo_container IS INITIAL.
IF mo_connector IS NOT INITIAL.
mo_container ?= mo_connector->get_output( ).
ELSE.
CREATE OBJECT mo_container TYPE zcl_fpm_demo_wire_flight_sel.
ENDIF.
ENDMETHOD.

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.
CASE io_event->mv_event_id.
WHEN if_fpm_guibb_search=>fpm_execute_search.
lo_datacontainer ?= mo_connector->get_output( ).
lo_datacontainer->apply_selection( ).
mt_sflight = lo_datacontainer->get_list( ).
...
ENDCASE.

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 ).
ENDMETHOD.

Wires of the application I

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.
05 - Wiring 02
Wiring 02

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.

04 - Wires of the application II
The business object currently only provides a Setter and a getter method to read the data structure or write a modified data structure back to the instance.

ZIF_FPM_DEMO_WIRE_SFLIGHT
The wiring setup reuses the connector class ZCL_FPM_DEMO_FL_SEARCH_CONN as there is no special treatment needed for the exchange of the current business object.

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.
Wiring 03

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.