
RAP - Optimize Search and Keys
In this article, we will optimize our search help in the custom pattern, use the additional binding, and make our RAP application fit for the end user.
Table of contents
In this article, we focus on the search help in our application and optimize input for the end user to provide the best possible experience.
Introduction
We are expanding our Custom Pattern with search help and optimizing input for the end user. In most cases, the actual business user no longer wants to have technical values. They are not interested in whether document type XY or indicator 1 is used. The technical identifiers then usually contain business information, such as the document type for credit memos or the payment indicator for domestic transactions. With this in mind, we want to optimize the application today and hide all technical values from the user.
Preparation
To do this, we want to provide two new search aids in our RAP application, one for Team and one for Application. To do this, we first need to create a foundation in the form of tables and then feed them with data.
Table
So let's first create the tables. We'll use data elements that we already used for the Custom Entity extension. To do this, we first create the Team.
@EndUserText.label : 'Teams'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zbs_dcp_team {
key client : abap.clnt not null;
key team : zbs_demo_dcp_team not null;
description : zbs_demo_dcp_text;
}
In the next step, we create the application, whereby an application is initially assigned to a team. Here, we have a relationship to the other data.
@EndUserText.label : 'Application'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zbs_dcp_appl {
key client : abap.clnt not null;
key application : zbs_demo_dcp_appl not null;
team : zbs_demo_dcp_team;
description : zbs_demo_dcp_text;
}
Data
In the next step, we fill the tables with data that we then want to retrieve via our search help. In this case, we write a small class that fills the tables with data. In the "real" world, you could provide a Business Configuration in which business users can maintain and transport the data, or a custom RAP application with more functions.
CLASS zcl_bs_dcp_data_init DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_bs_dcp_data_init IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA teams TYPE SORTED TABLE OF zbs_dcp_team WITH UNIQUE KEY team.
DATA applications TYPE SORTED TABLE OF zbs_dcp_appl WITH UNIQUE KEY application.
teams = VALUE #( ( team = 'BASE_DEV' description = 'Central Development' )
( team = 'FI_DEV' description = 'Development Financial' )
( team = 'HR_CUST' description = 'HR Customizing' ) ).
DELETE FROM zbs_dcp_team.
INSERT zbs_dcp_team FROM TABLE @teams.
applications = VALUE #( ( application = 'GENERAL' team = 'BASE_DEV' description = 'General Developments' )
( application = 'REVIEW' team = 'BASE_DEV' description = 'Review Tool' )
( application = 'CALC' team = 'FI_DEV' description = 'Tax Calculation' )
( application = 'ENGINE' team = 'FI_DEV' description = 'Calculation engine' )
( application = 'ACC_DEF' team = 'FI_DEV' description = 'Account definition' ) ).
DELETE FROM zbs_dcp_appl.
INSERT zbs_dcp_appl FROM TABLE @applications.
ENDMETHOD.
ENDCLASS.
Hint: In principle, you can use UUIDs when creating the system, but you cannot later include them in authorization objects to authorize the user. Therefore, we sometimes assign descriptive technical abbreviations as keys.
Search Help
In the second step, we create the two search help views directly and forgo a base view for normalizing the fields. To do this, we first create the view for the team, in which we also create a search field across both columns.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'VH for Team'
@Search.searchable: true
define view entity ZBS_I_DCPTeamVH
as select from zbs_dcp_team
{
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 1.0
key team as Team,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
description as Description
}
You can find more information about search fields in this article. We'll design the second view similarly, but here we'll also define a search help using the Team field.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'VH for Application'
@Search.searchable: true
define view entity ZBS_I_DCPApplicationVH
as select from zbs_dcp_appl
{
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 1.0
key application as Application,
@Consumption.valueHelpDefinition: [{ entity: { name: 'ZBS_I_DCPTeamVH', element : 'Team' } }]
team as Team,
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
description as Description
}
Hide key
To hide the key, we need three things. First, the text must be available in the entity or bound via association. Second, we store the corresponding text in the key, and as a final step, we determine the display of the key field.
Text
To do this, we create two fields in the custom entity that receive the data type from our tables. The fields can be defined at the end of the custom entity.
@UI.hidden: true
TeamDescription : zbs_demo_dcp_text;
@UI.hidden: true
ApplDescription : zbs_demo_dcp_text;
Then we read the data in our query implementation and fill the fields in the GET_REMOTE_DATA method. To do this, we implement a very simple read routine in the loop.
SELECT SINGLE FROM ZBS_I_DCPTeamVH
FIELDS Description
WHERE Team = @line->team
INTO @line->TeamDescription.
SELECT SINGLE FROM ZBS_I_DCPApplicationVH
FIELDS Description
WHERE Application = @line->application
INTO @line->ApplDescription.
Hint: You should always remember to exclude the fields from the selection, otherwise no data will be displayed. Therefore, we must also add the two fields to the DELETE_FIELDS statement.
Annotation
In the final step, we add the appropriate annotations to the fields. With "ObjectModel.text.element" we define the text for the key field, and with "UI.textArrangement" we can display the key with the text or even hide it completely. This means the user no longer sees it, but it is still used in our application.
@ObjectModel.text.element: [ 'TeamDescription' ]
@UI.textArrangement: #TEXT_ONLY
team : zbs_demo_dcp_team;
@ObjectModel.text.element: [ 'ApplDescription' ]
@UI.textArrangement: #TEXT_ONLY
application : zbs_demo_dcp_appl;
We can hide the actual text fields with "UI.hidden"; we don't need to display them as additional text. In return, we now get cleanly formatted fields.
Change
However, if we switch to change mode, the user will see the technical content again and no longer the readable values.
To change this behavior, we need to adapt the two search helps and make the same changes as when integrating them with the annotations. You can find the finished views in the GitHub repository if you'd like to see the customizations in detail; we'll save the listing for now.
Additional Binding
We now have a search help that could theoretically use a value from the current entity to limit the search set, or even be automatically populated. In this case, we can use the "Additional Binding" to ensure that this field is included in the search help. To do this, we add an additional property to the search help.
@Consumption.valueHelpDefinition: [ { entity: { name: 'ZBS_I_DCPApplicationVH', element: 'Application' },
additionalBinding: [ { element: 'Team', localElement: 'team', usage: #FILTER } ] } ]
The additional binding connects a field in the search help with a local element or a constant value. We can further refine the "USAGE" setting and adapt its functionality to our needs. The field has the following properties:
- #FILTER - The additional fields are used to restrict the search results.
- #RESULT - The fields from the search also overwrite the other fields in the entity when a search result is selected.
- #FILTER_AND_RESULT - A combination of the two previous settings.
In this example, we use the #FILTER variant to include the field in our filter and directly restrict the values. To do this, we look at the available applications (5), then select a product, and in the second step, we only see two applications, as the additional filter takes effect.
Semantic key
As a bonus, we also adjust the semantic key of the application so that navigation continues to work on a technical level, but the user only sees the description in the application. However, this is an example, since the application is likely to be operated by the developer, who needs the name of the software component for their work. For this we have the semantic key for navigation:
@ObjectModel.semanticKey: [ 'staging', 'sc_name' ]
If we now define the text field in our key field and hide the text, we can influence the appearance.
@UI.textArrangement: #TEXT_ONLY
@ObjectModel.text.element: [ 'descr' ]
key sc_name : abap.char(18);
@UI.hidden: true
descr : abap.char(60);
The following result would now be displayed in the application. The technical key is no longer visible, and the description, like the key, is highlighted in bold for better visibility.
Complete example
As always, you can find all the customizations to the application in our GitHub repository on RAP. You can use the commit to track the changes made and get a simple overview of the topic.
Conclusion
With a few annotations, we can improve the search help and the UI, making it easier for users to work with our application. With today's tips, the next application should be significantly better.