Skip to Content
Technical Articles
Author's profile photo George Manousaridis

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.

Common.FieldControl Term

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.

CDS View

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.

MPC_EXT Class

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"/>

Conclusion

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.

Useful references

Add annotations to an odata service through MPC_EXT

OData Vocabulary Annotations APIs

The attribute sap:field-control

Assigned Tags

      15 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Denis-Constantin Popescu
      Denis-Constantin Popescu

      Great post, Georgios! Thanks for sharing!

      Author's profile photo George Manousaridis
      George Manousaridis
      Blog Post Author

      Thank you Denis!!

      Author's profile photo Panagiotis Theodorogiannakis
      Panagiotis Theodorogiannakis

      Very useful post! Thank you for sharing your experience in this task!

      Author's profile photo George Manousaridis
      George Manousaridis
      Blog Post Author

      Thanks Panagiotis, both for your comment and for your help & cooperation!

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Georgios Manousaridis , thank you for your very helpful post.

      Do you or anybody else here knows how to do this is in an umanaged RAP Publishing on Steampunk? Thanks!

      Author's profile photo George Manousaridis
      George Manousaridis
      Blog Post Author

      Hi Wolfgang,

      Thanks for your kind words and sorry for the late response.

      No, unfortunately I don't know how this could be performed in the cloud.

      George

      Author's profile photo Daniel Ojados Conesa
      Daniel Ojados Conesa

      Hi guys,

      I think you can use virtual elements for this.

      By using in the target attribute the UI annotation:

      @UI.identification: [{hidden:#('virtualElement') }]
      

      and then declare a new virtual element type boolean:

      @ObjectModel.virtualElementCalculatedBy: 'ABAP:Z_ShowField'
       virtual testField :abap_boolean,
      

      Class 'Z_Showfield' should include interface IF_SADL_EXIT_CALC_ELEMENT_READ

      Further info in page 721 of RAP manual 🙂

       

      Hope it helps.

       

      Author's profile photo Joemil Aganan
      Joemil Aganan

      Nice. Can this be a solution to make a field read only (on Create/Edit) when it is already saved to the Z table (hasactiveentity)?  Sort of locking the field from changes when this record is already saved in the Z table.

      Author's profile photo George Manousaridis
      George Manousaridis
      Blog Post Author

      Hello Daniel Ojados Conesa,

      Thanks a lot for taking the time to propose this solution. Indeed virtual elements are a neat solution, but unfortunately didn't work on this case, as the cds view in hand was not a new, custom view, but one that extends a standard SAP view, providing limited options for further customizations.

      Please find the corresponding part of the post which explains the situation and I have updated accordingly, to make it clearer to future readers:

      External Calculation through BOPF: Using the BOPF framework can possibly be a neat workaround, by providing annotation values through external calculation. 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.

      Please, also check the update and tell me if it is clear enough.

      Thanks again for your contribution,

      George

      Author's profile photo Mithun Kumar
      Mithun Kumar

      Great suggestion Daniel, but unfortunately as George mentioned this doesn't seem to work for extend views.

      I get an error there "Syntax "VIRTUAL" is supported in projection views only" 🙁

      Author's profile photo Daniel Ojados Conesa
      Daniel Ojados Conesa

      Hi Joemil Aganan

      For that purpose, RAP gives you the possibility to use what they call features and control dinamically fields, standard operations and actions. You set up the display and availability of those depending on your requirement conditions.

      Further information here: https://help.sap.com/viewer/923180ddb98240829d935862025004d6/Cloud/en-US/dfc68f6275454c41b53510dacaec51d6.html

       

      Hope it helps 🙂

      Regards,

      Daniel

      Author's profile photo Joemil Aganan
      Joemil Aganan

      Thanks Daniel Ojados , I will keep that in mind.

      Author's profile photo Paul McFarling
      Paul McFarling

      Just a comment. You referenced my question about direct assignment. I have found that @UI.Hidden doesn't work with the #field_name. This technique only seems to work with the hidden property of a FieldGroup (and maybe some others, I tried doing the local annotation of @UI.hidden using the path property and that doesn't work either. )  

      Author's profile photo George Manousaridis
      George Manousaridis
      Blog Post Author

      Hi Paul,

      Thanks for your comment!
      Indeed, when I found your content I was excited that I have finally found a solution.
      Unfortunately the solution was not applicable to my case.
      Thanks again for taking the time to contribute!

      Cheers,
      George

      Author's profile photo Mithun Kumar
      Mithun Kumar

      Great article and tips, George. You mentioned

      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

      However, I'm struggling with the hidden property here. I'm trying to dynamically hide or show an additional field in the MyInbox app. So extended the CDS view accordingly.

      I tried this way:

      in CDS:

      I_PurReqnAccountAssignment.MasterFixedAsset,
      (case when _Purchaserequisitionitem.AccountAssignmentCategory = 'A' then cast( ('false') as abap_boolean ) else 'true' end) as HideorViewAsset

      then in the MPC_EXT -> define method:

          lo_ann_target = vocab_anno_model->create_annotations_target( lv_obje_service_name && '.' && 'C_PurReqnAccountAssignmentType/MasterFixedAsset' ).
          lo_annotation = lo_ann_target->create_annotation( iv_term = 'UI.hidden' ) ##NO_TEXT.
          lo_annotation->create_simple_value( )->set_path( 'HideorViewAsset' ).  ##NO_TEXT.
      

      But nothing happens. The Common.FieldControl term seems to only hide the value of the field, but not the field (label still visible) itself.

      Still a n00b at CDS view. Can you see if I'm doing something completely wrong here?