Dynamic annotation values in ABAP CDS Extend View
The task was to hide the fields with null values, on Fiori My Inbox. It may seem pretty straight forward at first, but it can be quite difficult, depending on the complexity of the setup and the restrictions you may have. In our case the restrictions are no tweaking the front end and as less as possible modification of SAP code. Additionally, the attribute that adds up in the complexity of the setup is that we are working on an extend CDS view and not a normal one (definition).
The proposal here is not exhaustive and maybe not the best practice, but… it worked. If you can think of a way to improve this solution, or if you know of other solutions on the subject, please leave your comment.
So, let’s dive in:
The solutions that didn’t work
These solutions are proposed throughout the community and although they seem appealing, we didn’t manage to make them work. If you know anything please share.
Metadata Extension file: To even start considering this solution, one has to be able to create metadata extension file in the first place. To do so, the annotation @Metadata.allowExtensions: true must exist. In this particular case we cannot add it, because it is only possible to be added in regular views (definitions) and not in extend views. The original view does not have it by default, so there is not way to create a metadata extension file here. Out of curiosity we tried to apply the solution to a new custom CDS view on a test environment, but no luck.
Direct assignment: According to this answer, one can directly assign a dynamic value to any annotation, by providing the field in this format: @MY.Annotation:#(field_to_get_value) . We tried this way to make @UI.hidden dynamic, but unfortunately we weren’t able to make it work. Maybe @UI.hidden is not supposed to be dynamic.
External Calculation through BOPF: Using the BOPF framework can possibly be a neat workaround, by providing annotation values through external calculation.
- Dynamic Field and Entity Field Control: To achieve this, one has to, define the BO View, set annotation @ObjectModel.readOnly: ‘EXTERNAL_CALCULATION’, for the desired field and use the SET_ATTRIBUTE_ENABLED method to show/hide the field accordingly. This solution is proposed in various answers, has a great documentation article and was confirmed to have been implemented by a colleague on custom CDS views (thanks Ali). Unfortunately, it is possible only in custom CDS views, as one cannot enhance an extend view with BOPF.
- Virtual Elements (Update 14/03/2021): As mentioned and explained by Daniel Ojados Conesa, virtual elements are another option for external calculation, in order to dynamically provide values to annotations, but unfortunately it utilize the BOPF framework as well. If your CDS view is a new, custom one, then virtual elements, which are again very well documented, will probably work for you.
The solution that did work
Another colleague came up with this solution (thanks Andrei), which is based on the attribute sap:field-control. We made use of the equivalent vocabulary term Common.FieldControl and fortunately we managed to provide the annotation from the backend, through the relative MPC_EXT class.
This term accepts a range of integer values which are assigned to different UI utilities. These values can be provided dynamically, by pointing the path attribute to an edm.byte (integer) property.
Our extend view part, containing the field to be hidden, looks something like:
source.field as FieldYouWantDynamicallyHidden, (case when source.ControlField = 'ValueToHide' then 0 else 1 end) as FieldToGetHideOrShowValue
which evaluates to:
<Property Name="FieldYouWantDynamicallyHidden" Type="Edm.String" MaxLength="122" sap:display-format="UpperCase"/> <Property Name="FieldToGetHideOrShowValue" Type="Edm.Byte"/>
Note that, in our case, we used the values 0 (hidden) & 1(read-only), as the field we want to hide/show is just used for display reasons. In place of 1(read-only), you can add any of the other accepted values, explained above, to suit your needs.
After redefining and modifying the DEFINE method of the respective MPC_EXT class, of the relative gateway project (maybe not the best way to go, but still legitimate), we add the following piece of code:
lo_ann_target = vocab_anno_model->create_annotations_target( 'YourService.YourEntityType/FieldYouWantDynamicallyHidden' ). lo_annotation = lo_ann_target->create_annotation( iv_term = 'Common.FieldControl' ) ##NO_TEXT. lo_annotation->create_simple_value( )->set_path( 'FieldToGetHideOrShowValue' ). ##NO_TEXT.
Note that, for the above code in the define method to work, the following data types and aliases should be present. If not, then you should add them like so:
data lo_ann_target type ref to /iwbep/if_mgw_vocan_ann_target. data lo_annotation type ref to /iwbep/if_mgw_vocan_annotation. vocab_anno_model->create_vocabulary_reference( iv_vocab_id = 'vocabularies.Common.v1' iv_vocab_version = '0001' )->create_include( iv_namespace = 'com.sap.vocabularies.Common.v1' iv_alias = 'Common' ) ##NO_TEXT.
And the above code evaluates to:
<Annotations xmlns="http://docs.oasis-open.org/odata/ns/edm" Target="YourService.YourEntityType/FieldYouWantDynamicallyHidden"> <Annotation Term="Common.FieldControl" Path="FieldToGetHideOrShowValue"/> </Annotations>
As you can clearly see, the FieldControl annotation refers to the FieldToGetHideOrShowValue field, to retrieve the value , that we have provided in a dynamic manner, for controlling the UI of the FieldYouWantDynamicallyHidden field.
Another Approach for MPC_EXT
We could implement a code block like so in the DEFINE method of the MPC_EXT class and add the desired term directly as an attribute of the produced oData property:
The backend code would look something like:
DATA(lo_entity_type) = model->get_entity_type( 'YourEntityType' ). IF lo_entity_type IS BOUND. DATA(lo_entity_prop) = lo_entity_type->get_property( 'FieldYouWantDynamicallyHidden' ). IF lo_entity_prop IS BOUND. lo_entity_prop->set_field_control( 'FieldToGetHideOrShowValue' ). ENDIF. ENDIF.
Then the property would look like this:
<Property Name="FieldYouWantDynamicallyHidden" Type="Edm.String" MaxLength="122" sap:display-format="UpperCase" sap:field-control="FieldToGetHideOrShowValue"/> <Property Name="FieldToGetHideOrShowValue" Type="Edm.Byte"/>
Briefly, what we managed to do here is to implement a code based solution via model provider class (mpc_ext), so as to enable field control capability for the fields of a CDS extend view, in order to show/hide them conditionally.
So, now the system will “see” the field-control annotation for the FieldYouWantDynamicallyHidden field and refer through the path reference to the FieldToGetHideOrShowValue field to get a value telling it whether to hide it or not.
Another possible application of this solution is to dynamically change the field status (hidden, read-only, mandatory, etc) based on user interaction e.g with a checkbox, or another field’s value. But for static hiding, it is always advised, that one should use @UI.hidden or @Consumption.hidden like so.
At this final point, I would like to attribute a special thanks to my colleague Panagiotis for his overall guidance through CDS views.