If you read my recent blog, HCM Processes & Forms: Making "User Events" Useful Again!, you might have caught on to the very cool fact that in FPM forms, making use of FPM interface classes, we can tell exactly what "event" is triggered and which form field triggered it. Of course, this got my head spinning with all kinds of use cases, but the first that came to mind was the good old, tried-and-true "make selections in a drop-down list control the options in another drop-down list" (also known as "dependent drop-down lists"). But that's "low hanging fruit". I thought "how can I make it even cooler?".
What if I showed you a way to make a secondary "dependent" drop-down list whose options we populate in response to the "change" event on our primary drop-down (ie. "master")? But now because I can tell which form field triggers the event, what if I can also populate (add additional entries?) depending on which form field triggered the event (for example a "regular" drop-down list vs. a "super user" drop-down list). So then, we have TWO "master" controls that will fire an event and our "dependent" control will populate accordingly (ie. because it knows WHICH master threw the event).
First, I built a basic process and form scenario.
Make a simple process
Form Scenario based on FPM form
I defined 3 Form Fields and assigned all the data element "SUBTY" ( a char 4 field). Notice that their "input help" (ie. F4) values are configured to come from my backend service "Y_GS_SOLO".
User Event
This is a key step. I defined a simple user event, made a field group with our 3 form fields and assigned the field group to the user event.
FPM Form definition/development
I developed my very basic FPM "form" type configuration.
The FPM attributes for the "Food Category" and "Food Category (Super Shopper)" drop down list set the user event "UE_CAT_CHANGE" (from our HCM P&F configuration above) on the "change" event of the drop down list. This is another key step in making it all work.
Backend Generic Service
You might have noticed in the Form Scenario configuration above, I developed one, simple custom backend service "Y_GS_SOLO". It has one operation "CHECK_CATEGORY" with my 3 fields mapped to it. However, the main "magic" happen in my methods to populate the help values (F4 values).
Before showing off the code, let's see how this all looks in run time, so you really get the idea of what is going on....
First off, you will notice that I have hidden the "admin/super user" field for now. I just want to show how this "dependent" population works from one "master"drop-down. The user first sees....
Now we make selection from the "master" drop-down list...
We can see in the ABAP debugger that the "event" is caught...
And now we can see the new options in the "dependent" drop-down list...
If we change the "master" again, we can see how it changes the "dependent" options again..
But now, I want to really get you excited about this. Again, because we know WHICH field triggered the event we are "listening" for, we can now react differently.
Here, I add the "admin/super user" field to the form. It will also trigger the same event that the previous, existing "master" drop-down did.
Again, we pick from our regular "master" field and get the options populated in the "dependent" drop-down...
But now, I pick from the “admin field”…..it fires the same event BUT we know where the even came from. In the debugger, we can see the fieldname of the form field that triggered out event...
So, now, we can make a different selection list based on which field fired the event…here, you can see I added option that ONLY the "admin" can pick from...
…and again, we make another round of changes and compare….
So you can imagine that you might have other logic that looks at the form scenario step or the initiator and determines which "master" drop-down to display. But then nothing more is needed for the "dependent" drop-down. It will merely have more or less options available that we handle already in our custom service. Therefore, the same form field can be used regardless. Very cool!
So how is this done? TO THE CODE!!!!! (haha)
First off, in our standard generic service interface method GET_HELP_VALUES, I have a loop that checks which "help" field we are talking about and calls a private method to populate these.
[code]
method IF_HRASR00GEN_SERVICE~GET_HELP_VALUES.
FIELD-SYMBOLS <help_dataset_wa> TYPE hrasr00gs_help_dataset.
LOOP AT help_datasets ASSIGNING <help_dataset_wa>.
CASE <help_dataset_wa>-fieldname.
WHEN C_FLD_FOOD_CATEGORY OR C_FLD_FOOD_CATEGORY_SUPER. "Category
CALL METHOD me->GET_F4_CATEGORIES
EXPORTING
SPECIAL_FIELDS = special_fields
SERVICE_FIELD_VALUES = service_field_values
CHANGING
HELP_DATASETS = <help_dataset_wa>.
WHEN C_FLD_FOOD_ITEM. "Items
CALL METHOD me->GET_F4_ITEMS
EXPORTING
SPECIAL_FIELDS = special_fields
SERVICE_FIELD_VALUES = service_field_values
CHANGING
HELP_DATASETS = <help_dataset_wa>.
ENDCASE.
ENDLOOP.
endmethod.
[/code]
Here is the code to load up the help values for the “food category”….
[code]
method GET_F4_CATEGORIES.
TYPES : BEGIN OF ty_cat,
code_val TYPE subty,
code_text TYPE TEXT40,
END OF ty_cat.
TYPES : tty_cats TYPE TABLE OF ty_cat.
DATA: service_field_value TYPE hrasr00gensrv_dataset.
DATA: field_catalog TYPE hrpad_f4help_table_column_tab.
DATA: field_catalog_wa TYPE hrpad_f4help_table_column.
DATA: lt_cats TYPE tty_cats,
ls_cat TYPE ty_cat.
FIELD-SYMBOLS <help_values> TYPE table.
" create field catalog
CLEAR field_catalog.
CLEAR field_catalog_wa.
field_catalog_wa-fieldname = 'CATEGORY'.
field_catalog_wa-headertitle = 'Category'.
APPEND field_catalog_wa TO field_catalog.
field_catalog_wa-fieldname = 'CATEGORY_TEXT'.
field_catalog_wa-headertitle = 'Category Text'.
APPEND field_catalog_wa TO field_catalog.
help_datasets-field_catalog = field_catalog.
help_datasets-keycolumnname = 'CODE_VAL'.
help_datasets-valuecolumnname = 'CODE_TEXT'.
CREATE DATA help_datasets-data TYPE STANDARD TABLE OF ty_cat.
ASSIGN help_datasets-data->* TO <help_values>.
ls_cat-code_val = '0'.
ls_cat-code_text = 'Fruits'.
APPEND ls_cat TO lt_cats.
ls_cat-code_val = '1'.
ls_cat-code_text = 'Vegetables'.
APPEND ls_cat TO lt_cats.
ls_cat-code_val = '2'.
ls_cat-code_text = 'Meat'.
APPEND ls_cat TO lt_cats.
<help_values> = lt_cats.
endmethod.
[/code]
And here is the code to detect the user event and which field fired it to then build our help values accordingly.
[code]
method GET_F4_ITEMS.
TYPES : BEGIN OF ty_item,
code_val TYPE subty,
code_text TYPE TEXT40,
END OF ty_item.
TYPES : tty_items TYPE TABLE OF ty_item.
DATA: service_field_value TYPE hrasr00gensrv_dataset.
DATA: field_catalog TYPE hrpad_f4help_table_column_tab.
DATA: field_catalog_wa TYPE hrpad_f4help_table_column.
DATA: lt_items TYPE tty_items,
ls_item TYPE ty_item,
lv_cat TYPE subty.
FIELD-SYMBOLS <help_values> TYPE table.
* determine event and field it came from
DATA: lt_event_queue TYPE cl_fpm=>if_fpm~ty_t_event_queue,
ls_first_event TYPE cl_fpm=>if_fpm~ty_s_event_queue_entry,
lo_fpm TYPE REF TO if_fpm,
lo_event TYPE REF TO cl_fpm_event,
lt_params TYPE apb_lpd_t_params,
ls_param LIKE LINE OF lt_params.
DATA: l_flg_was_admin TYPE bool.
CONSTANTS: c_usr_event_catchange TYPE string VALUE 'UE_CAT_CHANGE'.
lo_fpm = cl_fpm_factory=>get_instance( ).
CHECK lo_fpm IS BOUND.
lo_fpm->read_event_queue( IMPORTING et_event_queue = lt_event_queue ).
IF NOT lt_event_queue[] IS INITIAL.
READ TABLE lt_event_queue INTO ls_first_event INDEX 1.
IF ls_first_event-id = c_usr_event_catchange.
"do stuff for our user event!
ENDIF.
"determine which field fired event
lt_params[] = ls_first_event-parameter.
LOOP AT lt_params INTO ls_param WHERE key EQ 'FIELDNAME'.
IF ls_param-value = C_FLD_FOOD_CATEGORY_SUPER.
l_flg_was_admin = 'X'.
READ TABLE service_field_values INTO service_field_value WITH KEY fieldname = C_FLD_FOOD_CATEGORY_SUPER.
IF sy-subrc = 0.
lv_cat = service_field_value-fieldvalue.
ENDIF.
ELSE.
CLEAR l_flg_was_admin.
READ TABLE service_field_values INTO service_field_value WITH KEY fieldname = C_FLD_FOOD_CATEGORY.
IF sy-subrc = 0.
lv_cat = service_field_value-fieldvalue.
ENDIF.
ENDIf.
* l_event_fieldname = ls_param-value.
EXIT.
ENDLOOP.
ENDIF.
" create field catalog
CLEAR field_catalog.
CLEAR field_catalog_wa.
field_catalog_wa-fieldname = 'ITEM'.
field_catalog_wa-headertitle = 'Item'.
APPEND field_catalog_wa TO field_catalog.
field_catalog_wa-fieldname = 'ITEM_TEXT'.
field_catalog_wa-headertitle = 'Item Text'.
APPEND field_catalog_wa TO field_catalog.
help_datasets-field_catalog = field_catalog.
help_datasets-keycolumnname = 'CODE_VAL'.
help_datasets-valuecolumnname = 'CODE_TEXT'.
CREATE DATA help_datasets-data TYPE STANDARD TABLE OF ty_item.
ASSIGN help_datasets-data->* TO <help_values>.
CASE lv_cat. "build help based on category selected
WHEN '0'. "fruit
ls_item-code_val = '0010'.
ls_item-code_text = 'Apple'.
APPEND ls_item TO lt_items.
ls_item-code_val = '0011'.
ls_item-code_text = 'Banana'.
APPEND ls_item TO lt_items.
ls_item-code_val = '0012'.
ls_item-code_text = 'Orange'.
APPEND ls_item TO lt_items.
IF l_flg_was_admin = 'X'.
ls_item-code_val = '0014'.
ls_item-code_text = 'Pineapple (admin only)'.
APPEND ls_item TO lt_items.
ls_item-code_val = '0016'.
ls_item-code_text = 'Tomato (admin only)'.
APPEND ls_item TO lt_items.
ENDIF.
WHEN '1'. "vegetables
ls_item-code_val = '1010'.
ls_item-code_text = 'Celery'.
APPEND ls_item TO lt_items.
ls_item-code_val = '1011'.
ls_item-code_text = 'Lettuce'.
APPEND ls_item TO lt_items.
ls_item-code_val = '1012'.
ls_item-code_text = 'Spinach'.
APPEND ls_item TO lt_items.
IF l_flg_was_admin = 'X'.
ls_item-code_val = '0016'.
ls_item-code_text = 'Tomato (admin only)'.
APPEND ls_item TO lt_items.
ENDIF.
WHEN '2'. "meat
ls_item-code_val = '2010'.
ls_item-code_text = 'Beef'.
APPEND ls_item TO lt_items.
ls_item-code_val = '2011'.
ls_item-code_text = 'Fish'.
APPEND ls_item TO lt_items.
ls_item-code_val = '2012'.
ls_item-code_text = 'Pork'.
APPEND ls_item TO lt_items.
WHEN OTHERS.
ls_item-code_val = '0000'.
ls_item-code_text = 'Please select category.'.
APPEND ls_item TO lt_items.
ENDCASE.
<help_values> = lt_items.
endmethod.
[/code]
The “magic” is really this small piece of code which captures the event and the field that fired it. You can really use this anywhere (ie. in help value methods, your “do operations”, deep within your own private methods, etc.) , so it is quite nice for “event” detection.
[code]
* determine event and field it came from
DATA: lt_event_queue TYPE cl_fpm=>if_fpm~ty_t_event_queue,
ls_first_event TYPE cl_fpm=>if_fpm~ty_s_event_queue_entry,
lo_fpm TYPE REF TO if_fpm,
lo_event TYPE REF TO cl_fpm_event,
lt_params TYPE apb_lpd_t_params,
ls_param LIKE LINE OF lt_params.
DATA: l_flg_was_admin TYPE bool.
CONSTANTS: c_usr_event_catchange TYPE string VALUE 'UE_CAT_CHANGE'. “your event name
lo_fpm = cl_fpm_factory=>get_instance( ).
CHECK lo_fpm IS BOUND.
lo_fpm->read_event_queue( IMPORTING et_event_queue = lt_event_queue ).
IF NOT lt_event_queue[] IS INITIAL.
READ TABLE lt_event_queue INTO ls_first_event INDEX 1.
IF ls_first_event-id = c_usr_event_catchange. “here we can check for a particular event name
"do stuff for our user event!
"determine which field fired event
lt_params[] = ls_first_event-parameter.
LOOP AT lt_params INTO ls_param WHERE key EQ 'FIELDNAME'.
* l_event_fieldname = ls_param-value. <====this is the field that fired the event
EXIT.
ENDLOOP.
ENDIF.
ENDIF.
[/code]
Well, that is it. I hope you really see what is possible now with this, and it gets you as excited as I was when I "discovered" it. It's not even about drop-down list....it's about being able to respond (be it adding help values, show/hide fields, populate data, etc) by knowing not only WHAT event was triggered but also WHICH form field triggered it. It makes a lot of things easier and also adds a new level of form interaction. I know I will be using it! As always, I hope this helps others, and I will keep cranking these out as long as you keep reading. Till next time....
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
3 | |
3 | |
2 | |
2 | |
2 | |
2 | |
2 | |
2 | |
2 |