Make a field Dynamically Mandatory – Custom Solution
Overview
Disclaimer: This blog post was originally created internally and i apologize for any sarcasm or criticism that i unintentionally leave in the blog. It is my hope that this basic guide will assist anyone searching with creating dynamic mandatory fields by providing a clear and detailed description how to do it.
If you have not read the section about creating dynamic mandatory fields first then go do that now, otherwise if you have just read that or read it previously…
Welcome Back! In this section I will describe a custom solution to complete the dynamic mandatory field development done previously so that some existing SAP errors no longer plague you. To solve our woes we will implement a custom class, I will provide a file for this, however, be aware that some standard enhancements were made to allow some functionality to work. I will attempt to describe the custom class here but be aware that some modifications may need to be made to make it function perfectly. I have attempted to remove references that use standard code enhancements but there is the potential that some still exist.
Please note that this tutorial assumes the developer has a general understanding of UI components, context nodes, and their various getter methods. Also the developer has read the previous section and knows how to make a field dynamically mandatory.
Introduction
As described the current SAP implementation(at least for the version we have) is flawed. Mandatory fields are checked in the DO_FINISH_INPUT and through the first configuration of the page… When you set the field as mandatory in the P Getter as previously shown this code will be called in the DO_PREPARE_OUTPUT method of your UI Component View Controller. This is going to be fine to load a page and set the field as mandatory first time round. Unfortunately, when another field changes, which forces the conditions of your mandatory field in the P getter the order of checking fails to identify that the mandatory states have changed. The reason is as follows(in a simplified roundabout description):
DO_PREPARE_OUTPUT (Calls P Getters setting up conditions) -> |User Input Changes| (Hopefully triggering your conditions) -> DO_FINISH_INPUT (Gets the current mandatory fields and applies the messages)
!– Page Refresh -!
DO_PREPARE_OUTPUT (Calls P Getters setting up conditions) -> |Page ready for user input again|…
The flaw here is that on first load the page checked the conditions and the fields are set as mandatory/not-mandatory, then the user makes some changes. Next the do_finish_input method is called which adds the messages but it uses the mandatory fields that it knows of since the page was loaded! There is no check to see if the conditions have changed, which may have modified the state of the mandatory fields. Now from here you can debug through and make necessary changes to get it to work for you. You can probably guess that your going to need to check the conditions again. Well this is going to be annoying to do every single time you want to configure a new view with some dynamic mandatory fields on it. To solve this issue i created a custom class.
NOTE: The following steps ONLY work for a totally custom view… IF you are enhancing a standard component you could copy the ZCL_BSP_VIEW_CONTROLLER class to a custom version and change IT’S super-class to be the same as the enhanced view controller.
Custom Controller Class
The custom controller class provides a bunch of functionality to reduce the need to re-create that functionality for each view for example the following features exist:
- Enhancement to determine if mandatory check is required.Can make use of the MV_GLOBAL_EVENT attribute.
- Enhancement to check ‘P Getters’ for registered context nodes prior to getting the empty mandatory fields which fixes the issue discussed below’
- Enhancement to remove messages for specific events e.g. EDIT
- Enhancement to create the view_group_context if it is initial.
- Enhancement to solve an issue where global messages don’t block save in overview pages
Click here for the custom class as a .nugg file.
Step 1: Add the ZCL_BSP_VIEW_CONTROLLER class as a super class
Go to your class definition and navigate up the hierarchy until you locate the standard CL_BSP_WD_VIEW_CONTROLLER class sitting as the super class to your Z* class and ‘Change Inheritance’ of the class so that ZCL_BSP_VIEW_CONTROLLER is now the super class.
Step 2: Register the Context Node(s) that have the Dynamic Mandatory Fields
This step is required as otherwise the conditions will not be rechecked. You could imagine that if we didn’t have dynamic mandatory fields and just always rechecked every single attribute for every single context node on finish then efficiency would suffer. However, SAP standard does do exactly that in many other instances so you could choose not to care and follow their example. In this case, if using this class you need to register the node.
Right click the DO_FINISH_INPUT method of you controller class and ‘Redefine’ it (my screenshot shows this has already occurred).
Add the code to as shown to register the applicable context node(in my case its some PERSON node)
Bonus 1: Don’t Check on Certain Events
Now that we have our custom class in place some bonus functionality is available. As shown if we have an event where we don’t need to see irritating messages in the header we can remove the messages. In this case the fields are mandatory(shown with a red *) but we are not spammed with a message. But sometimes we might not even want to check for messages. For example the standard implementation kindly checks if the fields are mandatory for us when the user clicks the cancel button… Most circumstances dictate that there really is no need to check on a cancel event so we can override the IS_MANDATORY_CHECK_REQUIRED method and provide our own custom results. By default the custom class checks if the view is in display or edit mode for example before even checking for mandatory fields. We can include or remove that functionality once redefined or add our own criteria. Just remember to return a true or false result no matter what you choose to do…
The below example shows overriding the above mentioned method, including the check for display mode etc, and choosing not to check when the event is ‘CANCEL’.
Bonus 2: Add a Custom Error Message(* Removed as requires standard enhancement)
I won’t describe this as it used a standard enhancement to the CL_BSP_MESSAGES class to allow modifying a message by absolute ID(known as ‘dummy’ in the standard code).
Hi Hugo,
There is an error in the method "POST_MANDATORY_FIELDS" of class "ZCL_BSP_VIEW_CONTROLLER".
We are trying to access the method "GET_CONFIGURED_FIELD" of class "CL_BSP_DLC_VIEW_DESCRIPTOR" line 21.
Can you please check and let me know if this method should be called or not?
Thanks,
Bernard
Hi Bernard,
Apparently i tried to slim the class down as much as possible to remove any specifically custom calls but sadly it seems that the method you mention is in fact a standard enhancement.
The reason its called is to get the fields that are configured on the page, i believe the issue was that some fields were returned that were not configured and that isnt desirable to check fields that are not configured. The enhanced method looks per the following:
method get_configured_fields .
data: lr_data type ref to data.
field-symbols: <fs_line> type any,
<fs_name> type any,
<fs_field> type any.
"... I assume the user of this method will pass a standard table of strings
create data lr_data like line of et_fieldnames.
assign lr_data->* to <fs_field>.
loop at dl_buffer assigning <fs_line>.
assign component 'FIELD_NAME' of structure <fs_line> to <fs_name>.
move <fs_name> to <fs_field>.
insert <fs_field> into table et_fieldnames.
endloop.
endmethod.
where et_fieldnames is an exporting parameter of type 'any table'. Ill investigate to see if i can change it otherwise i may adjust the blog to mention this, meanwhile if you still do want to use this and have any issues let me know.
Hi Hugo,
there is still another error in the class - at least in our environment.
In method POST_MANDATORY_FIELDS you call the P getter method:
"... Call P getter
REPLACE ALL OCCURRENCES OF 'STRUCT.' IN lv_fieldname WITH ''.
rv_result = lr_node->get_p_s_ext( component = lv_fieldname
iv_property = if_bsp_wd_model_setter_getter=>fp_input_mandatory ).
Here you remove 'STRUCT.' from the field name. This is ok for calling the P-Getter method, but not further on when appnding the fieldname to the table of mandatory fields.
Secondly, if you have extended the UI by custom fields, you also have to remove the string 'EXT.' before calling the P Getter.
My solution would be as follows:
"... Call P getter
lv_fieldname2 = lv_fieldname. "<<INS
REPLACE ALL OCCURRENCES OF 'STRUCT.' IN lv_fieldname2 WITH ''.
REPLACE ALL OCCURRENCES OF 'EXT.' IN lv_fieldname2 WITH ''. "<<INS
rv_result = lr_node->get_p_s_ext( component = lv_fieldname2
iv_property = if_bsp_wd_model_setter_getter=>fp_input_mandatory ).
Best regards
Marc
Hi Marc,
I can see what you mean and that seems to be a fine solution. Although there would be no requirement I suppose to add the variable lv_fieldname2 unless that was for something else.
Actually I believe some minor changes occurred to this class since I wrote this particular Blog, whilst the purpose of it remains a requirement last I checked.
I would like to make a change to incorporate your suggestion but obviously you will want to just change the code to fix your production issue. Not sure if it is worth changing the one I uploaded unless I pull it back down and change that one since other changes have occurred to the version i have and I personally did not make all of the changes plus i need to remove some functionality to upload it again.