Application Development and Automation Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
Krishna_karale
Explorer
1,076

Hello, 
In this blog post we are going to Learn about Dynamic Feature Control in RAP.

With Feature Control, the various fields and properties can be adjusted again at runtime, for example to activate or deactivate actions. In this article you will learn more about how to use this function to get more flexibility in your data model.

This function is an optional feature provided by the RAP Framework, which complements existing functionalities. While it is not a mandatory component, it can be utilized to enhance certain aspects of the application. In the current example, this function has not yet been implemented, but we will explore its usage in this article. Specifically, feature control can be applied to fields to modify or influence their behavior, such as controlling their visibility or editability.

When implementing this feature, you need to decide how to apply it:

  1. Instance-based feature ("features:instance"): The data of individual instances is available, and the feature's behavior is determined for each instance individually.
  2. Global feature ("features:global"): There is no access to instance-specific data, and the decision is made once globally on whether the feature can be used.

Procedure

Here we are defining a Custom table (ZKK_DT_TRAVEL). 

The table zkk_dt_travel is a transparent table in SAP that stores travel-related information. It includes details such as travel ID, agency ID, customer ID, travel dates, booking fees, total price, currency, status and a description. It also tracks the creation and modification details like created by, created at, last changed by, and last changed at. 

 

 

@EndUserText.label : 'Travel table' 
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE 
@AbapCatalog.tableCategory : #TRANSPARENT 
@AbapCatalog.deliveryClass : #A 
@AbapCatalog.dataMaintenance : #RESTRICTED 
define table zkk_dt_travel { 
key client : abap.clnt not null; 
key travel_id : /dmo/travel_id not null; 
agency_id : /dmo/agency_id; 
customer_id : /dmo/customer_id; 
begin_date : /dmo/begin_date; 
end_date : /dmo/end_date; 
@Semantics.amount.currencyCode : '/dmo/travel_m.currency_code' 
booking_fee : abap.curr(16,2); 
@Semantics.amount.currencyCode : '/dmo/travel_m.currency_code' 
total_price : /dmo/total_price; 
currency_code : /dmo/currency_code; 
description : /dmo/description; 
status : abap_boolean; 
created_by : abp_creation_user; 
created_at : abp_creation_tstmpl; 
last_changed_by : abp_locinst_lastchange_user; 
last_changed_at : abp_locinst_lastchange_tstmpl; 
} 

 

The Above table holds the records which are given below 

Krishna_karale_0-1737718608633.png

For this database table(ZKK_DT_TRAVEL) we are defining the Basic view/Interface view. 

The ZKK_I_TRAVEL is a CDS view based on the zkk_dt_travel  table. It provides a structured way to access travel data, including fields like travel ID, agency ID, customer ID, travel dates, booking fees, total price, currency, and timestamps. It uses annotations to enhance usability, such as semantic tags for amounts, currencies, and metadata for created/changed users and timestamps. 

 

 

@AbapCatalog.viewEnhancementCategory: [#NONE] 
@AccessControl.authorizationCheck: #NOT_REQUIRED 
@EndUserText.label: 'Interface view for Travelling' 
@Metadata.ignorePropagatedAnnotations: true 
@Metadata.allowExtensions: true 
@ObjectModel.usageType:{ 
serviceQuality: #X, 
sizeCategory: #S, 
dataClass: #MIXED 
} 
define root view entity ZKK_I_TRAVEL 
as select from zkk_dt_travel 
{ 
key travel_id as TravelId, 
agency_id as AgencyId, 
customer_id as CustomerId, 
begin_date as BeginDate, 
end_date as EndDate, 
@Semantics.amount.currencyCode: 'CurrencyCode' 
booking_fee as bookingfee, 
@Semantics.amount.currencyCode: 'CurrencyCode' 
total_price as TotalPrice, 
currency_code as CurrencyCode, 
description as Description, 
status as status, 
@Semantics.user.createdBy: true 
created_by as CreatedBy, 
@Semantics.systemDateTime.createdAt: true 
created_at as CreatedAt, 
@Semantics.user.localInstanceLastChangedBy: true 
last_changed_by as LastChangedBy, 
@Semantics.systemDateTime.lastChangedAt: true 
last_changed_at as LastChangedAt 
} 

 

 Now I am defining the Projection view on top of interface view. 

 

@AbapCatalog.viewEnhancementCategory: [#NONE] 
@AccessControl.authorizationCheck: #NOT_REQUIRED 
@EndUserText.label: 'projection view for Travel' 
@Metadata.ignorePropagatedAnnotations: true 
@Metadata.allowExtensions: true 
@ObjectModel.usageType:{ 
serviceQuality: #X, 
sizeCategory: #S, 
dataClass: #MIXED 
} 
define root view entity ZKK_C_TRAVEL  
as projection on ZKK_I_TRAVEL as Travel 
{ 
key TravelId, 
AgencyId, 
CustomerId, 
BeginDate, 
EndDate, 
@Semantics.amount.currencyCode: 'CurrencyCode' 
bookingfee, 
@Semantics.amount.currencyCode: 'CurrencyCode' 
TotalPrice, 
CurrencyCode, 
Description, 
status, 
CreatedBy, 
CreatedAt, 
LastChangedBy, 
LastChangedAt 
} 

 

 

 Now I am defining the Metadata Extension for projection View. 

 

 

@Metadata.layer: #PARTNER 
annotate entity ZKK_C_TRAVEL with 
{ 
@UI.facet: [{ 
purpose: #STANDARD, 
position: 10, 
type : #IDENTIFICATION_REFERENCE, 
label: 'Travel Details' 
}] 
@UI.identification: [{ position: 10, label: 'Travel Id' }] 
@UI.lineItem: [{ label: 'Travel Id' }] 
TravelId; 
@UI.identification: [{ position: 20, label: 'Agency Id' }] 
@UI.lineItem: [{ label: 'Agency Id' }] 
AgencyId; 
@UI.identification: [{ position: 30, label: 'Customer Id' }] 
@UI.lineItem: [{ label: 'Customer Id' }]
CustomerId; 
@UI.identification: [{ position: 40, label: 'Begin Date' }] 
@UI.lineItem: [{ label: 'Begin Date' }] 
BeginDate; 
@UI.identification: [{ position: 50, label: 'End Date' }] 
@UI.lineItem: [{ label: 'End Date' }] 
EndDate; 
@UI.identification: [{ position: 60, label: 'Booking Fee' }] 
@UI.lineItem: [{ label: 'Booking Fee' }] 
bookingfee; 
@UI.identification: [{ position: 70, label: 'Total Price' }] 
@UI.lineItem: [{ label: 'Total Price' }] 
TotalPrice; 
@UI.identification: [{ position: 80, label: 'Status Confirm' }, 
{ type: #FOR_ACTION, dataAction: 'status', label: 'Status Confirm' } ] 
@UI.lineItem: [{ label: 'Status' }, 
{ type: #FOR_ACTION, dataAction: 'status', label: 'Status' }] 
status; 
@UI.identification: [{ position: 100, label: 'Currency' }] 
@UI.lineItem: [{ label: 'Currency' }] 
CurrencyCode; 
@UI.identification: [{ position: 90, label: 'Description' }] 
@UI.lineItem: [{ label: 'Description' }] 
Description; 
@UI.identification: [{ position: 110, label: 'Created By' }] 
@UI.lineItem: [{ label: 'Created By' }] 
CreatedBy; 
@UI.identification: [{ position: 120, label: 'Created At' }] 
@UI.lineItem: [{ label: 'Created At' }] 
CreatedAt; 
@UI.identification: [{ position: 130, label: 'Last Changed By' }] 
@UI.lineItem: [{ label: 'Last Changed By' }] 
LastChangedBy; 
@UI.identification: [{ position: 140, label: 'Last Changed At' }] 
@UI.lineItem: [{ label: 'Last Chnaged at' }] 
LastChangedAt; 
} 

 

 Now I am defining the Behavior Definition for Interface/Basic view. 

 

managed implementation in class zbp_kk_i_travel unique; 
strict ( 2 ); 
define behavior for ZKK_I_TRAVEL alias Travel //alias <alias_name> 
persistent table zkk_dt_travel 
lock master 
authorization master ( instance ) 
//etag master <field_name> 
{ 
create; 
update; 
delete; 
field ( readonly : update ) TravelId; 
field ( readonly ) LastChangedAt, LastChangedBy, CreatedAt, CreatedBy; 
field ( mandatory : create ) AgencyId, CustomerId, BeginDate, EndDate, bookingfee, CurrencyCode; 
action ( features : instance ) status result [1] $self; 
mapping for ZKK_DT_TRAVEL { 
TravelId = travel_id; 
AgencyId = agency_id; 
CustomerId = customer_id; 
BeginDate = begin_date; 
EndDate = end_date; 
bookingfee = booking_fee; 
TotalPrice = total_price; 
CurrencyCode = currency_code; 
Description = description; 
status = status; 
CreatedBy = created_by; 
CreatedAt = created_at; 
LastChangedBy = last_changed_by; 
LastChangedAt = last_changed_at; 
} 
} 

 

Create the Behavior Definition for Projection view. 

 

projection; 
//strict ( 2 ); 
define behavior for ZKK_C_TRAVEL alias Travel //alias <alias_name> 
{ 
use create; 
use update; 
use delete; 

use action status; 
} 

 

Instance feature

Here in behavior definition of interface view we have to use Feature control with Actions. 

Krishna_karale_0-1737958604162.png

 After this we have to implement 2 methods, Status and get_instance_feature method. 

Here is the class. 

 

CLASS lhc_ZKK_I_TRAVEL DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.

    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR travel RESULT result.

    METHODS status FOR MODIFY
      IMPORTING keys FOR ACTION travel~status RESULT result.

    METHODS get_instance_features FOR INSTANCE FEATURES
      IMPORTING keys REQUEST requested_features FOR travel RESULT result.

ENDCLASS.

CLASS lhc_ZKK_I_TRAVEL IMPLEMENTATION.

  METHOD get_instance_authorizations.
  ENDMETHOD.

  METHOD status.

  MODIFY ENTITIES OF zkk_i_travel IN LOCAL MODE
  ENTITY Travel UPDATE FIELDS ( status )
  WITH VALUE #( for key in keys ( %tky = key-%tky status = abap_true ) )
  FAILED failed
  REPORTED reported.


  read ENTITIES OF  zkk_i_travel IN LOCAL MODE
  ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys )
  RESULT data(travel_data).

  result = VALUE #( for travel_record in travel_data
                  ( %tky = travel_record-%tky %param = travel_record )
                  ).
  ENDMETHOD.

  METHOD get_instance_features.
  read ENTITIES OF zkk_i_travel IN LOCAL MODE
  ENTITY Travel
  FIELDS ( status ) WITH CORRESPONDING #( keys )
  RESULT data(confirmed_status)
  FAILED failed.

  result = VALUE #( for trav in confirmed_status
             let con_status = cond #( when trav-status = abap_true
                                      THEN if_abap_behv=>fc-o-disabled
                                      else if_abap_behv=>fc-o-enabled  )


                                      IN ( %tky = trav-%tky
                                      %action-status = con_status
                                      )
                                      ).

  ENDMETHOD.

ENDCLASS.

 

Implement the method Status with functionality triggered by the button. This method can include any business logic based on the requirements. In the current scenario, it sets the field value to True. 

Krishna_karale_1-1737958685131.png

The method GET_INSTANCE_FEATURES has been implemented to manage the control states (Enable or Disable in the current scenario). If the Status Confirmed is Yes then The Status button will be Disabled. 

Krishna_karale_2-1737958685133.png

Run Application. 

Scenario: If the Status Confirmed is ‘NO’ then we can change it to ‘Yes’, Using Confirm Status Button on list page. If Status Confirmed is ‘Yes’ then the button will be Disable. 

Krishna_karale_3-1737958728939.png

As we can see the status in above image is No, if we select the particular field the Confirm Status will be enabled. 

Krishna_karale_4-1737958728941.png

If we Confirm the status then the value will become ‘YES’ 

Krishna_karale_5-1737958728942.png

When we select the fields with Status ‘YES’ the button will be in disables State. 

Krishna_karale_6-1737958728944.png

Conclusion

Dynamic Feature Control

Dynamic feature control allows for runtime adjustments based on specific business logic or conditions. This is implemented using RAP methods like GET_INSTANCE_FEATURES. It enables controls to adapt based on context, such as user roles, permissions, or the status of the business object. For instance, the "Approve" button might be enabled only for records in the "Pending Approval" state, or a "Cancel" button could become inactive after the travel date has passed. 

3 Comments
AbhishekSharma
Active Contributor
0 Kudos

@Krishna_karale thanks for sharing...

Thanks-

Khan-Muskan
Explorer
0 Kudos

Thankyou for the information it is very helpful.

shivenganguly91
Explorer
0 Kudos

The created_by field is based on the semantic used, is there a way to get the proper syuname in managed implementation and with managed save ?

Labels in this area