For a great user experience, it is very important that users can enter data quickly, fluently, and efficiently – even on a slow network. This is particularly relevant for power users, i.e., users who spend significant time with data entry, often using keyboard shortcuts to move from field to field. In this post, we will provide guidance on how to achieve efficient data entry for draft-enabled SAP Fiori elements apps.
There are many aspects to consider when building efficient applications. Here, we will focus on the following aspects:
- using the appropriate table type,
- providing multiple empty table rows in edit mode,
- optimizing the execution of side effects,
- and simulating slow networks for testing.
While most of this post is relevant for any backend, it also covers some aspects that are specific to apps based on the SAP ABAP RESTful Application Programming Model (RAP) or the SAP Cloud Application Programming Model (CAP).
Core principles of efficient data entry
- Minimize waiting times caused by validations or side effects – the consistency of the business object can be checked by the user at any time by pressing the ENTER key on a field.
- Avoid error or warning messages for fields that have not been filled out yet.
- Support column-based data entry in a table without error or warning messages.
- Allow entering incomplete data or references to not yet existing (master) data.
Terminology
Before discussing concrete suggestions, on how you can optimize your applications, we have to clarify some important terminology:
- Draft handling:
- A draft is an interim version of a business entity that has not yet been saved as active version explicitly. This version is automatically saved in the background whenever its data is added or changed.
- SAP Fiori elements leverages the draft concept to help prevent data loss and to allow users to pause and resume data entry corresponding to their own workflow.
- Side effects:
- If a user changes the content of a field, or performs another activity, this change can potentially influence other aspects (like fields, tables, etc.) on the UI. This system behavior is called a side effect.
- Side effects are performed in the backend. However, the frontend needs to be informed which fields might be influenced by a change and thus need to be updated. Otherwise, the UI might still display outdated data.
- For SAP Fiori elements apps, side effect annotations can be used for updating fields on the UI, for changing the visibility or enablement of fields, or for checking the consistency of fields.
- Each side effect can define some triggering conditions (e.g. source entities or source properties) as well as targets that are to be refreshed or triggered after the source data was changed (e.g. target entities, target properties, or actions).
- You can explore different ways to leverage side effect definitions in the SAP Fiori elements flexible programming model explorer.
- Actions:
- An action represents business functionality that is part of the OData service and can be triggered by the frontend. Actions can implement validation functionality, calculate additional data, or contain any other logic that might be required for the use case.
- Actions are declared by the backend and their implementation is specific to the used technology.
Some RAP-specific terms include:
- Validations:
- A validation in RAP is a part of the business object behavior that checks the consistency of business object instances based on trigger conditions.
- A validation is implicitly invoked by the business object’s framework when the trigger condition of the validation is fulfilled.
- Trigger conditions can be create, update, and delete operations or field value changes.
- Validations are always invoked during the save sequence.
- Determinations:
- A determination in RAP is a part of the business object behavior that modifies instances of business objects based on trigger conditions.
- Two types of determinations are distinguished depending on the stage of the program flow they are executed at: on modify determinations and on save determinations.
- Determine actions:
- Determine actions in RAP allow the business object consumer to call determinations and validations upon request.
- Determine actions are primarily meant to be called by side effects to give the user immediate feedback after changing UI fields or field groups in draft-enabled applications.
- Combined with side effects, determine actions act as an early execution of parts of the save sequence that already runs determinations and validations before the draft instance is prepared and activated.
Furthermore, there are also CAP-specific details:
- Event handlers:
- An event handler defines the implementation to react to specific events that happen in an OData service.
- Events include data read and modification operations as well as triggered actions.
- Event handlers are the main way to interact with the OData service provided by CAP and thus embed business logic.
- They can contain arbitrary programming logic, including what is considered as validations or determinations in RAP.
Best Practices
With this knowledge in mind, let’s look at some best practices you can start implementing today.
Using the appropriate table type
The use of the correct table type for each table in a Fiori app is very important for good user experience and thus efficient data entry. There are two main options to consider.
The Responsive Table is the default table in SAP Fiori elements and also the preference for most use cases. Its focus is working on line items as a whole rather than on individual cells. It is specifically optimized to also work well on touch devices.
You should use the Grid Table instead, if at least one of the following scenarios fits your use case:
- The table can contain a large number of rows.
- The comparison of items (in different rows/columns) is important.
- The cell level and spatial relationship between cells are more important than the entirety of a single line item.
- Line items are not independent of each other, or operations across columns are required.
- It is important that new rows are created at the end of the table.
For more information about the different table types, refer to the corresponding Fiori design guidelines.
Providing multiple empty table rows in edit mode
Another very important aspect is the uninterrupted entry of multiple objects in tables. Here, the creation rows functionality provides the best usability and should therefore normally be used for tables on an object page.
Creation Rows are special empty rows that are displayed in the edit mode of a table to directly create new entries. In the responsive table, the rows are added at the top, while in the grid table, the rows are added at the bottom. Initially, the empty rows do not have any corresponding entry in the backend. Once a field in an empty row is modified and the focus is moved away from that field, the row is sent to the backend and is inserted into the draft table. Afterwards, a new empty creation row is added to the table automatically.
An alternative is the Inline Create functionality: new items can be created in the table by pressing the Create button. This inserts an empty row in the table, which is sent directly to the backend. While creation rows usually allow for a better user experience, you should prefer the inline create functionality if you need to use a tree table, as the creation rows are not supported there.
If the objects to create are very complex, both the creation rows and the inline create are not recommended; use sub object pages in these cases.
For SAP Fiori elements applications for OData V2, creation rows can be enabled in the manifest file as follows:
"ObjectPage|Travel": {
"entitySet": "Travel",
"component": {
"name": "sap.suite.ui.generic.template.ObjectPage",
"settings": {
"sections": {
"Booking": {
"createMode": "creationRows"
}
}
}
}
}
You can additionally configure default values for these creation rows with the Common.DefaultValuesFunction
annotation.
For applications for OData V4, use the following configuration:
"TravelObjectPage": {
"type": "Component",
"id": "TravelObjectPage",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"entitySet": "Travel",
"controlConfiguration": {
"_Booking/@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": {
"creationMode": {
"name": "InlineCreationRows"
}
}
}
}
}
}
}
Some applications might have their own backend logic for creating empty rows; this has to be removed in order for creation rows to work correctly.
Optimizing the execution of side effects
No consistency checks or messages after draft-based data modification
Do not perform validations/consistency checks in draft-based data modification operations (e.g. OData POST, PATCH and MERGE requests). Also, do not directly send error or warning messages in these cases. Of course, there are some rare but reasonable exceptions for severe errors that need to be reported immediately, e.g. if data cannot be written properly into the draft storage.
Instead, postpone these checks to the draft preparation phase, which are called via pressing the ENTER key or right before the draft saving. If the checks are necessary to happen directly after the data modification, you should model appropriate side effects.
Leverage refreshing through pressing the ENTER key
As already mentioned before, the user can always check the consistency of a business object by pressing the ENTER key on a field. This leverages so-called global side effects. By default, the draft preparation action is configured as global side effect, but you can also configure another action by adding a side effect and leaving out the source definition:
<Annotation Term="com.sap.vocabularies.Common.v1.SideEffects">
<Record>
<PropertyValue Property="TriggerAction"
String="cds_m2_sd_travel.cds_m2_sd_travel_Entities/CheckBooking"/>
</Record>
</Annotation>
Fiori elements for OData V2 provides an option which allows users to refresh the whole application in these cases. However, this option is highly performance critical and shall never be used. It can be disabled with the setting forceGlobalRefresh:
"sap.ui.generic.app": {
"_version": "1.3.0",
"settings": {
"forceGlobalRefresh": false
}
}
false is the default value since UI5 1.106, so applications based on later versions can also omit the setting.
Side effects should only be used where reasonable
While there are seldom cases where side effects are the correct way to go, in most cases, side effects break with the given core principles. Never create side effects for single fields; instead combine multiple fields (e.g. all fields of the header data) in the SourceProperties
of the side effect annotation and precisely define in the TargetProperties
what should be refreshed.
You can use side effects in the following cases:
- Fields influence the visibility or enablement of one or more related fields.
- The consistency of a larger number of fields that belong together needs to be checked because related facets depend on them. For example, the consistency of the header data influences related facets on the Object Page.
- There is the need to refresh data that was prefilled by the backend.
- Also here, it is crucial for the performance of your app to always combine
SourceProperties
and never create side effects for single properties. - Example: Update the field
TotalPrice
when the header data is entered:<Annotation Term="com.sap.vocabularies.Common.v1.SideEffects"> <Record> <PropertyValue Property="SourceProperties"> <Collection> <PropertyPath>TravelID</PropertyPath> <PropertyPath>AgencyID</PropertyPath> <PropertyPath>CustomerID</PropertyPath> <PropertyPath>BeginDate</PropertyPath> <PropertyPath>EndDate</PropertyPath> <PropertyPath>BookingFee</PropertyPath> <PropertyPath>CurrencyCode</PropertyPath> <PropertyPath>Memo</PropertyPath> <PropertyPath>Status</PropertyPath> <PropertyPath>Confirmed</PropertyPath> </Collection> </PropertyValue> <PropertyValue Property="TargetProperties"> <Collection> <String>TotalPrice</String> </Collection> </PropertyValue> </Record> </Annotation>
- Also here, it is crucial for the performance of your app to always combine
Note that properties containing messages like errors or warnings (usually SAP__Messages
in RAP) always have to be added to the to the TargetProperties
of side effect annotations in the OData V4 stack whenever new messages might occur:
<Annotation Term="com.sap.vocabularies.Common.v1.SideEffects">
<Record>
<PropertyValue Property="SourceProperties">
<Collection>
<PropertyPath>TravelID</PropertyPath>
<PropertyPath>AgencyID</PropertyPath>
<PropertyPath>CustomerID</PropertyPath>
<PropertyPath>BeginDate</PropertyPath>
<PropertyPath>EndDate</PropertyPath>
<PropertyPath>BookingFee</PropertyPath>
<PropertyPath>CurrencyCode</PropertyPath>
<PropertyPath>Memo</PropertyPath>
<PropertyPath>Status</PropertyPath>
<PropertyPath>Confirmed</PropertyPath>
</Collection>
</PropertyValue>
<PropertyValue Property="TargetProperties">
<Collection>
<String>TotalPrice</String>
<String>SAP__Messages</String>
</Collection>
</PropertyValue>
</Record>
</Annotation>
Simulating slow networks for testing
To verify that your changes had an impact, you should carefully test your application. Specifically make sure that you also test in a slow network – your end user might not have the same great connection as you have.
Typically, you can simulate such a slow network very easily. For example in the Chrome development tools, you can simply choose the option “Slow 3G” or define a custom option with a network latency of 500ms in the Network tab.
Negative examples
With the above best practices in mind, we occasionally see apps that do not follow these principles. Some examples of practices to avoid include:
- A loading indicator appears as soon as a value is entered in a field and the tab key is pressed. As a result, the user is blocked for some time.
- A user enters correct data in the first or any other form field and receives error messages that other form fields (which were not reached yet in the tab sequence) have not been filled out.
- A user presses the create button of a table and receives an error message that data has not been entered in the table yet.
- A user wants to enter several values in a dedicated table column, before continuing to enter data in the remaining table columns; however, an error message is shown when moving from the first to the second row.
By sticking to the principles described in this article, and avoiding the examples here, you will be able to deliver a great experience to the power users that enter data as a large part of their daily work with SAP.
Conclusion
Efficient data entry is crucial for end users. In this post, we highlighted some core principles and best practices that you can and should apply today to improve the overall experience for your users.
The main things to take away are that you should
- make sure to use the correct table type,
- allow creating multiple rows at the same time using creation rows,
- and minimize the time end users have to wait for side effects, validations, or determinations to complete, or be disturbed by premature error messages.
As a general rule, end users should be able to enter the data with their own strategy in their own working mode without being forced to follow a specific style of data entry.
Have you experienced some of the issues highlighted here? Were you already able to improve your user experience in these areas? Do you have any additional ideas or suggestions on what could be done to improve this even further? Let us know in the comments. We are looking forward to hearing from you!