Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
10,311

Introduction

For creating value helps based on domain fixed values in ABAP Cloud the recommendation so far was to create custom CDS views based on the released CDS views DDCDS_CUSTOMER_DOMAIN_VALUE  and DDCDS_CUSTOMER_DOMAIN_VALUE_T. 

The drawback of this solution is 

  1. that you have to create dedicated CDS views for each domain and
  2. that the aforementioned CDS views only work for domains that reside in software components owned by customers. 

Based on a question in a forum of the German SAP user group DSAG where the question was raised whether there is no alternative solution I thought again about this requirement. 

Instead of using CDS views it is possible just use one single custom entity which uses RTTI to retrieve the domain fixed values.

Result

When adding the new value help to the projection view as follows:

 

 

 

 

      @Consumption.valueHelpDefinition: 
      [{ entity: { name : 'ZI_DOMAIN_FIX_VAL' , element: 'low' } ,
         additionalBinding: [{ element: 'domain_name',
                               localConstant: 'BEL_API_RETCODE', usage: #FILTER }]
                               , distinctValues: true
                               }]
      Low,

 

 

 

 

We get the following result:

domain_fix_value_help_result.png

Implementation

The challenge to get this to work with just one custom entity is, to provide the name of the domain that is used as a value help via additional binding. 

This can be achieved by using localConstant key word  and in addition it needs the usage parameter set to #FILTER. This way we can specify the name of the domain whose domain fixed values shall be used within the coding.

Be careful !

It doesn't work if the element ("domain_name" in this case) is hidden in the Value Help CDS View (e.g. through "UI.hidden: true").

Custom Entity

The DDL source code of the custom entity looks like this:

 

 

 

 

@EndUserText.label: 'Get domain fix values'
@ObjectModel.resultSet.sizeCategory: #XS
@ObjectModel.query.implementedBy: 'ABAP:ZCL_GET_DOMAIN_FIX_VALUES'
define custom entity ZI_DOMAIN_FIX_VAL
{
      @EndUserText.label     : 'domain name'
      @UI.hidden : true 
  key domain_name : sxco_ad_object_name;
      @UI.hidden  : true
  key pos         : abap.numc( 4 );
      @EndUserText.label     : 'lower_limit'
      low         : abap.char( 10 );
      @EndUserText.label     : 'upper_limit'
      high        : abap.char(10);
      @EndUserText.label     : 'Description'
      description : abap.char(60);


}

 

 

 

 

Query implementation class

And the code of the query implementation class is as follows:

The domain name is retrieved from the filter that is filled via the additional binding.

Once the domain name has been retrieved the domain fixed values are being retrieved using RTTI as described in the following blog post Fetching texts for domain fixed values - SAP Community by Michał Badura.

Another option would be to use the XCO library. 

 

 

 

 

 

CLASS zcl_get_domain_fix_values DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    INTERFACES if_rap_query_provider.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_get_domain_fix_values IMPLEMENTATION.


  METHOD if_rap_query_provider~select.
    DATA business_data TYPE TABLE OF zi_domain_fix_val .
    DATA business_data_line TYPE zi_domain_fix_val .
    DATA(top)     = io_request->get_paging( )->get_page_size( ).
    DATA(skip)    = io_request->get_paging( )->get_offset( ).
    DATA(requested_fields)  = io_request->get_requested_elements( ).
    DATA(sort_order)    = io_request->get_sort_elements( ).

    DATA domain_name  TYPE sxco_ad_object_name  .
    DATA pos TYPE i.

    TRY.
        DATA(filter_condition_string) = io_request->get_filter( )->get_as_sql_string( ).
        DATA(filter_condition_ranges) = io_request->get_filter( )->get_as_ranges(  ).

        READ TABLE filter_condition_ranges WITH KEY name = 'DOMAIN_NAME'
               INTO DATA(filter_condition_domain_name).

        IF filter_condition_domain_name IS NOT INITIAL.
          domain_name = filter_condition_domain_name-range[ 1 ]-low.
        ELSE.
          "do some exception handling
          io_response->set_total_number_of_records( lines( business_data ) ).
          io_response->set_data( business_data ).
          EXIT.

        ENDIF.

        business_data_line-domain_name = domain_name .

        CAST cl_abap_elemdescr( cl_abap_typedescr=>describe_by_name( domain_name ) )->get_ddic_fixed_values(
          EXPORTING
            p_langu        = sy-langu
          RECEIVING
            p_fixed_values = DATA(fixed_values)
          EXCEPTIONS
            not_found      = 1
            no_ddic_type   = 2
            OTHERS         = 3 ).

        IF sy-subrc > 0.
          "do some exception handling
          io_response->set_total_number_of_records( lines( business_data ) ).
          io_response->set_data( business_data ).
          EXIT.
        ENDIF.

        LOOP AT fixed_values INTO DATA(fixed_value).
          pos += 1.
          business_data_line-pos = pos.
          business_data_line-low = fixed_value-low .
          business_data_line-high = fixed_value-high.
          business_data_line-description = fixed_value-ddtext.
          APPEND business_data_line TO business_data.
        ENDLOOP.

        IF top IS NOT INITIAL.
          DATA(max_index) = top + skip.
        ELSE.
          max_index = 0.
        ENDIF.

        SELECT * FROM business_data AS data_source_fields
           WHERE (filter_condition_string)
           INTO TABLE business_data
           UP TO max_index ROWS.

        IF skip IS NOT INITIAL.
          DELETE business_data TO skip.
        ENDIF.

        io_response->set_total_number_of_records( lines( business_data ) ).
        io_response->set_data( business_data ).

      CATCH cx_root INTO DATA(exception).
        DATA(exception_message) = cl_message_helper=>get_latest_t100_exception( exception )->if_message~get_longtext( ).

        DATA(exception_t100_key) = cl_message_helper=>get_latest_t100_exception( exception )->t100key.

        "do some exception handling

    ENDTRY.
  ENDMETHOD.
ENDCLASS.

 

 

 

 

 

 

 

 

 

 

 

20 Comments
antelman82
Newcomer
0 Kudos

Question: Is the domain an actual table or is it just an instance of a table object? It looks like it's a domain object created on the fly that is then populated with fixed values that are passed into it at run time ( dynamic ) and not being populated in a table that is then referenced. 

We are trying to do something similar, except we are trying to pass in a filtered service from a custom entity and passing the results of that query into the domain so we can use those as a valuehelper for a dropdown selection. 

jayme_alonso
Participant

Hi, great blog, this CDS View should be delivered as the standard solution!!!

Just two corrections, fix the select to:

        SELECT * FROM @business_data AS data_source_fields
           WHERE (filter_condition_string)
           INTO TABLE @business_data
           UP TO @Max_index ROWS

 and remove the domain name from the dropdown with @UI.hidden : true

      @EndUserText.label     : 'domain name'
      @UI.hidden  : true
      key domain_name : sxco_ad_object_name;

 

Andre_Fischer
Product and Topic Expert
Product and Topic Expert

Hi Jayme,

thanks for the hint. I thought that I already fixed the coding but this was obviously not the case. Also thanks for the hint that the field for the domain name can be hidden.
I took the information that @UI.hidden cannot be used from the other blog post as granted, but it this technical restriction seem to have gone in Steampunk.

Kind regards, 
Andre

Pallava
Explorer
0 Kudos

Hi Andre,

I tried the above the feature but it does not retrieve the values. I would like to know in which specific placeholder in the code I need to replace the domain name which I created.

I have replaced the domain_name with "Z_GENDER" which is the domain I created earlier,as shown below but would like to know whether this is fine because I observed that this "domain_name" repeats in multiple place in the query implementation class and I am not sure should I replace the domain name everywhere in the code?

 

READ TABLE filter_condition_ranges WITH KEY name = 'Z_GENDER'

INTO DATA(filter_condition_domain_name).

 

 

GENDER.jpg

Kind regards,

Pallava

Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hi Pallava,

you don't have to and you must not replace anything in the coding.

The name of the domain is specified in the defintion of the value help.

localConstant: 'BEL_API_RETCODE', usage: #FILTER

This value is then transferred to the implementation in the query implementation class. 

It is in the defintion of the value help where you would have to specify the name of the domain

for you 

localConstant: 'Z_GENDER' , usage :  #FILTER

Kind regards,   

Andre

Kind regards,

Andre

 

 

 

Pallava
Explorer
0 Kudos

Hi Andre,

Thanks for your timely reply.

I did follow those steps but not able to arrive at the result yet by creating the DDL and its query class.

What I am trying to achieve

I want to display this drop down value list in another entity's CDS View (ZC_PROJECT)as shown on the diagram below.

Pallava_0-1710230339712.png

 

 

Below is the annotation I used in the consumption view of my view entity ZC_PROJECT

define root view entity ZC_PROJECT

provider contract transactional_query

as projection on ZI_PROJECT

{

 

@Consumption.valueHelpDefinition:

[{ entity: { name : 'ZI_DOMAIN_FIX_VAL' , element: 'low' } ,

additionalBinding: [{ element: 'domain_name',

localConstant: 'Z_PROJTYPE', usage: #FILTER }]

, distinctValues: true

}] ProjectType,

}

Do let me know if I am doing something wrong over here.

 

 

 

Pallava
Explorer
0 Kudos

Hi Andre,

I was able to resolve the issue but I have two clarifications where you could help .

1) While I am able to see the domain value list in my dropdown I am could either display the code or its description as of now with the below annotation.

 

For displaying code value

@EndUserText.label: 'Bill Type'

//@UI.textArrangement: #TEXT_ONLY

@Search.defaultSearchElement: true

@UI.fieldGroup: [{ qualifier: 'ProjectGeneralInfo', position: 60 }]

@UI.selectionField: [{ position: 60 }]

@UI.identification: [{ position: 60 }]

@UI.lineItem: [{ position: 60 }]

@Consumption.valueHelpDefinition:

[{ entity: { name : 'ZI_DOMAIN_FIX_VAL' , element: 'low' } ,

additionalBinding: [{ element: 'domain_name',

localConstant: 'Z_BILLTYPE_DATAELEMENT', usage: #FILTER }]

, distinctValues: true

}]

BillType;

 

For displaying description

 

 

@Consumption.valueHelpDefinition:

[{ entity: { name : 'ZI_DOMAIN_FIX_VAL' , element: 'description' } ,

additionalBinding: [{ element: 'domain_name',

localConstant: 'Z_PROJTYPE_DATAELEMENT', usage: #FILTER }]

, distinctValues: true

}]

ProjectType;

 

I would desire to display both code and description on the dropdown(as shown on your screenshot) can you let me know what additional code has to be added to the annotation in this case and in such I would like to know which value gets stored in the back end.
CodeValue.pngDescription.png

2) While leveraging your suggested code functionality in this blog I am not able to add use draft functionality in combination. Is there a limitation here ?

 

 

lauferf
Discoverer
0 Kudos

Hello together,

I'm facing the same Problem as Pallava. 

@Pallava Do you have a solution to the issue in the meantime?

Kind regards,

Florian

Pallava
Explorer
0 Kudos

Hi Florian,

I could not solve the issue yet.

@Andre_Fischer, could you help us over here ?

Thanks,

Pallava

hendrikp
Participant
0 Kudos

Hello @Andre_Fischerit seems that one needs to pass a data element which then takes the underlying domain fixed values defined in the data element and not the domain name itself.

In your example there is also an dataelement BEL_API_RETCODE. When I call cl_abap_typedescr=>describe_by_name( domain_name )in se24 testmode with a domain name which has no identical data element it throws an exception "type_not_found".

Also: How did you achieve to display the text of the domainfixed values? I only see the values in the drowdown as @Pallava .

BR

TylerFincham
Explorer
0 Kudos

Hi Andre,

Is it possible to pass a dynamic value from the current entity to the implementation class using the additional binding annotation?
Also what is the reason you are passing the domain name in the additional binding? Why not just hardcode it in the implementation class? Unless you want to implement multiple value helps from domain on the same entity I suppose.

 

 

Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos

The reason why I am passing the domain name in the additional binding via a local constant is that I want to have just two generic repository objects, the custom entitiy and the class that implements the query to have value helps for all domains with fixed values.

As I wrote the approach was so war to build two custom CDS views for each domain.

So after having created the two Z-objects you can add value helps for each domain with fixed values.

jheisler
Explorer
0 Kudos

Hi @Andre_Fischer,

can this generic functionality also be somehow used as a text provider to display texts for keys of domain fixed values in a table? As the domain is currently only passed as part of the additional binding it would not be possible to create e.g. an association.

KPröll
Explorer
0 Kudos

Hi Andre,

thanks for a helpful blog!

Is it possible to add custom fixed values to the SAP standard domain in ABAP Cloud? (like we can do it via "Fixed Value Append" in OnPrem).

Kind regards,

Kristi

sridhar_meesala
Active Contributor
0 Kudos

Value and Description works fine with OData V4. Those who are facing the issue might be using V2.

Andre_Fischer
Product and Topic Expert
Product and Topic Expert
0 Kudos

I don't understand your comment. 

The implementation shown does not depend on the type of service binding and will work for both V4 and V2.

Regards, 

Andre 

sridhar_meesala
Active Contributor
0 Kudos

Well, I am confused now 😕. I see a difference in value help dropdown (key + description) with V2 and V4.

With V4 binding:

sridhar_meesala_0-1726758533897.png

With V2 binding:

sridhar_meesala_1-1726758661620.png

priyotheone
Explorer
0 Kudos

Hi @Pallava,

Could you please explain, how did you got the values on the dropdown. I am still not getting any values like your last post.

How did you resolved it.

Pallava
Explorer
0 Kudos

Hi @priyotheone ,

All you have to do is the below.

1) Create data definition and copy the same content as mentioned by @Andre_Fischer.

2) Create a class and copy the same content as mentioned by @Andre_Fischer .

3) Create a domain 1st which contains the list of values however you want.

    Next use the same domain in your data element. Use the same data element for one of the fields in your business object (database table).

  In your metadata definition use the below syntax where localConstant's value is the only dynamic value you will have to change based on the field's data element you used. In my example  Z_BILLTYPE_DATAELEMENT is the data element and BillType is the field name.

@Consumption.valueHelpDefinition:
[{ entity: { name : 'ZI_DOMAIN_FIX_VAL' , element: 'description' } ,
additionalBinding: [{ element: 'domain_name',
localConstant: 'Z_BILLTYPE_DATAELEMENT', usage: #FILTER }]
, distinctValues: true
}]
BillType;

Also make sure you have the below annotations mentioned in your interface view so that you get a dropdown menu with the listed values.

@AbapCatalog.viewEnhancementCategory: [ #NONE ]

@Metadata.ignorePropagatedAnnotations: true

@Metadata.allowExtensions: true

@ObjectModel.resultSet.sizeCategory: #XS

This should work for sure  !!

 

 

crus_ch
Explorer
0 Kudos

For everyone wondering why it doesn't work:

You have to enter the name of your Data Element for "loclaConstant" NOT the Domain Name here:

      @Consumption.valueHelpDefinition: 
      [{ entity: { name : 'ZI_DOMAIN_FIX_VAL' , element: 'low' } ,
         additionalBinding: [{ element: 'domain_name',
                               localConstant: 'DATA_ELEMENT_NAME', usage: #FILTER }]
                               , distinctValues: true
                               }]

 

This is really confusing, I don't know why the coding examples are not fixed by using the right terms.

(Everyone having the same name for the domain and the data element is not affected obvsly)