Defining Determine Actions and Side Effect

Objectives

After completing this lesson, you will be able to:
  • Define determine actions.
  • React on user input using side effects.

Determine Actions

Determine actions in RAP

Determine actions allow the business object consumer to call determinations and validations on request. In managed scenarios, actions do not have an action implementation. Instead, you assign determinations and validations to a determine action. Whenever a determine action is called, the determinations and validations assigned to it are evaluated and then only those determinations and validations are executed whose trigger conditions are fulfilled.

Determine actions are primarily meant to be called by side effects (see later in this lesson) to give the user immediate feedback after changing UI fields or field groups in draft-enabled applications. But it is also possible to execute them directly like any other action, for example, through a MODIFY ENTITIES … EXECUTE call or by assigning them to a button on the SAP Fiori UI.

The following important restrictions apply:

  • You cannot add on modify determinations to determine actions.
  • For determine actions, feature and authorization control is not enabled.
  • You cannot execute determine actions inside implementations of determinations and validations.
  • In unmanaged scenarios, determine actions must be implemented manually for active instances.
Syntax for defining determine actions

Determine actions are defined in the behavior definition with the determine action statement, followed by the name of the determine action.

You assign determinations and validations by listing them inside a pair of curly brackets after the determine action name. Each determination and validation is followed by a semicolon.

Usually, the determinations and validations are only executed if their respective trigger conditions are met. However, ff a determination or validation is defined with the always flag, this determination or validation is executed regardless of its trigger conditions. After a determination with the always flag has been executed, it can be triggered again by other determinations belonging to the same determine action.

Caution

After calling a determine action, the order in which the validations and determinations are executed, is independent of the specified order in the determine action definition. Assigned determinations are always executed before assigned validations. The execution order among the determinations and validations themselves is defined by the framework.

Ways to reuse determine actions Projections and Interfaces

Like other actions, determine actions must be reused for consumption in every projection behavior definition and interface behavior definition. The syntax is use determine action <name>;

Side Effects

Side effects in RAP

Side effects are useful in UI scenarios based on draft-enabled BOs, to make a Fiori Elements UI aware that data changes of defined fields require the recalculation of other data values, permissions, or messages.

Since draft-enabled scenarios use a stateless communication pattern, the UI doesn't trigger a reload of all BO-related properties for every user input. If only certain fields are changed by the end user in edit mode, that is on the draft BO instance, the user can't expect data consistency of displayed data on the UI at all times. In other words, when a UI user changes data of a draft instance, there is not necessarily a READ request for all fields that are displayed. Without side effects, It can lead to inconsistencies of displayed data on the draft instance, for example when data is calculated based on other fields.

Side effects solve this problem. They define the interdependency between fields and other BO characteristics to trigger a reload of affected properties. In particular, side effects are efficient since there is no full reload of all BO-properties, but only of those that are affected by particular user input.

Note

There are two ways to define side effects: You can either define side effects in the behavior projection or the behavior definition of the business object or by adding UI annotations in the UI project using the Fiori Tools. In this course, we only cover the definition of side effects in the back end implementation.

You can define side effects for data changes - either of specified fields, or the entire BO entity - or the execution of actions.

Possible reactions, called targets, of side effects can be the reload of data - either of specified fields, the entire BO entity, or a specified BO entity - or the reload of authorizations or messages.

Note

The reload of messages is not supported by OData V2 UI services.

A special type of side effects executes a determine action and then reloads specified data (see later).

Side effects for information reload

You define side effects in the entity behavior body, using the side effects{ } statement. One side effects statement can contain any number of side effects, each finished by a semicolon (;).

Ordinary side effects consist of the side effect trigger followed by the affects keyword and a comma-separated list of targets.

Possible triggers are as follows:

field <field_name>

Whenever the specified field <field_name> is changed on the user interface, the side effect is triggered and the defined targets are reloaded. Exactly one field must be specified as a trigger property.

$self

Whenever the current RAP BO entity is modified using a changing standard operation, the side effect is triggered and the defined targets are reloaded.

In case of the $self trigger property, the following limitations apply to the targets that can be specified after affects:

  • No fields of the current entity self an be specified. However, fields from associated entities can be defined using path expressions.
  • The current entity $self cannot be specified as target.
action <action_name>

Whenever the specified action <action_name> is executed on the user interface, the side effect is triggered and the defined targets are reloaded. Exactly one action must be specified as the trigger property.

The comma-separated list after affects can contain the following side effect targets:

field <field_name>

The specified field <field_name> is reloaded when the side effect is triggered. One or more fields can be specified as side-effect target. The asterisk * reloads all fields of the same entity instance. It is also possible to specify fields from other entity instances as targets. They must be defined via an association path. In case of the trigger property $self, the target cannot be a field of the current entity $self.

$self

The current RAP BO entity is reloaded. In case of the trigger property $self, it is not allowed to specify the current entity $self as the target.

entity <association_name>

The entity that is the target of the <association_name> association is reloaded when the side effect is triggered.

permissions ...

The features and authorization control of the specified properties are reloaded when the side effect is triggered. After the permissions keyword, you can list standard operations (keywords update and delete), actions (keyword action), and fields (keyword field). You can also specify permissions for elements and operations of associated entities via an association path. Multiple permissions can be listed in parentheses, separated by commas.

messages

All messages stored in the reported response parameter are reloaded when the side effect is triggered. This target is only supported by the OData V4 protocol. It is not support by OData V2.

Examples for side effect triggers

The code example defines three side effects with different triggers. The first is triggered by value changes of field f1, the second is triggered by any change to the current BO entity, and the third side effect is triggered when the a1 action is executed.

Examples for side effect target

This example illustrates different target types. All five side effects are triggered by changes to a field. The targets, however, are different. If the first side effect is triggered, the data of a single field is reloaded. The second side effect specifies a list of fields to be reloaded, and the third side effects reloads all fields. With the target $self, the entire BO entity is reloaded.

The fifth side effect triggers a reload of all fields and of the authorization and feature control information for the update operation and the a1 action.

Note

It is not possible to define more than one side effect for the same trigger. However, one side effect can combine different targets, for example fields and permissions.

Determine Action Side Effects

Determine action side effects

The side effects which we discussed before, only trigger a reload of information. A special side-effect variant triggers the execution of a determine action before it reloads the information. We call this variant a determine action side effect.

You define a determine action side effect by using the determine action key word, followed by the name of the determine action. You specify the information that need to be reloaded as before, that is, after the affects addition. The same possibilities and restrictions apply as discussed before.

As triggers, only data changes are allowed. Actions as triggers are not supported. The sources, that are the data changes that serve as triggers, are listed after the executed on addition.

You can specify the following data as triggers for a determine action:

field <field_name>

The determine action is triggered whenever the specified field <field_name> is changed. You can specify one or more fields as the trigger for a determine action. By using more than one field, the fields are implicitly considered as a field group. The side effect is only triggered if the cursor is set outside of the group of source fields after changing at least one of them.

$self

The determine action is triggered whenever anything on its own entity instance is changed.

entity<association_name>

The determine action is triggered whenever anything on the specified entity is changed.

Define Determine Actions and Side Effects

In this exercise, you define determine actions to enable the direct execution of validations and determinations. Then you use side effects to make sure the determine actions are triggered by the UI after certain user actions.

Note

In this exercise, replace ## with your group number.

Solution

Repository Object TypeRepository Object ID
CDS Data Definition (Model)/LRN/437D_R_TRAVEL
CDS Data Definition (Projection)/LRN/437D_C_TRAVEL
CDS Behavior Definition (Model)/LRN/437D_R_TRAVEL
CDS Behavior Definition (Projection)/LRN/437D_C_TRAVEL

Task 1: Define a Determine Action and a Side Effect

In your business object, define a determine action (suggested name: checkCustomer) to trigger the validation of the customer ID. Expose the determine action to the OData UI service and define a side effect to execute the determine action when the user changes the customer ID.

Steps

  1. Edit the behavior definition on data model level Z##_R_TRAVEL. After the validations and determination definition, define a determine action (suggested name: checkCustomer) that triggers the validation of the customer ID.

    1. After the definition of the validations and the determination, add the following code:

      Code Snippet
      1234
      determine action checkCustomer { validation validateCustomer; }
  2. Activate the behavior definition.

    1. Press Ctrl + F3 to activate the development object.

  3. Edit the behavior definition on projection level Z##_C_TRAVEL and add the determine action to the projection.

    1. Add the following code:

      Code Snippet
      1
      use action checkCustomer;
  4. After the projection of the determine action, define a side effect that triggers the determine action.

    1. Add the following code:

      Code Snippet
      12345
      side effects { determine action checkCustomer }
  5. Use changes to the CustomerId field as the side effect trigger.

    1. Adjust your code as follows:

      Code Snippet
      123456
      side effects { determine action checkCustomer executed on field CustomerId }
  6. Specify the messages as the side effect target that the UI might have to reload after the execution of the side effect.

    1. Adjust your code as follows:

      Code Snippet
      123456
      side effects { determine action checkCustomer executed on field CustomerId affects messages; }
  7. Activate the behavior definition.

    1. Press Ctrl + F3 to activate the development object.

  8. Restart the OData UI service preview and verify that the determine action is executed after you edit the Customer ID.

    1. Open the service binding, place the cursor on the root entity, and choose Preview....

    2. In the preview, display the details for one of the travels. Change to edit mode and delete the value in the Customer ID field.

    3. Press Tab to save the draft and jump to the next input field.

      Result

      You immediately see the error message from the validation.

Task 2: Define a Calculated View Element

Extend the root entity of your business object with a view element for the duration of the travel in days (suggested name: Duration). Use the dats_days_between SQL function to calculate this information based on the persistent values of begin_date and end_date.

Expose the new view element to your OData UI service and add it to the list page and the object page. Then set it to read-only.

Steps

  1. Edit the CDS view entity on data model level Z##_R_Travel. Insert a new row before the Status view element, and use code completion to insert a call of SQL function dats_days_between.

    1. In the element list of the view definition, insert a new row above the Status view element.

    2. Enter dats and press Ctrl + Space to invoke code completion.

    3. From the suggestion list, choose dats_days_between( date1, date2 ) (function).

  2. Supply the function with the table fields begin_date and end_date and define a name for the new view element (suggested name: Duration) .

    1. Adjust the code as follows:

      Code Snippet
      12345
      end_date as EndDate, dats_days_between( begin_date, end_date ) as Duration, status as Status,
  3. Activate the data definition.

    1. Press Ctrl + F3 to activate the development object.

  4. Now edit the projection view Z##_C_Travel. Add the new view element and annotate this element with a suitable label, for example, Duration (days).

    1. Adjust the code as follows:

      Code Snippet
      123456
      EndDate, @EndUserText.label: 'Duration (days)' Duration, Status,
  5. Activate the data definition.

    1. Press Ctrl + F3 to activate the development object.

  6. Now edit the metadata extension Z##_C_TRAVEL and add @UI.lineItem and @UI.identification annotations for the new view element.

    1. Add the following code:

      Code Snippet
      12345
      @UI: { lineItem: [ { position: 35, importance: #LOW } ], identification: [ { position: 65, importance: #LOW } ] } Duration;
  7. Activate the metadata extension.

    1. Press Ctrl + F3 to activate the development object.

  8. Now open the behavior definition on data model level Z##_R_TRAVEL and use a quick fix to add the new field to the draft table.

    1. Choose the error icon with a light bulb next to the draft table statement.

    2. Choose Recreate draft table z##_travel_d for entity z##_r_travel.

    3. Press Ctrl + F3 to activate the draft table.

  9. Return to the behavior definition and add the new view element to the same FIELD statement as the Status view element.

    1. Adjust the code as follows:

      Code Snippet
      123456
      field ( readonly ) Status, Duration, ChangedAt, ChangedBy, LocChangedAt;
  10. Use a pragma to suppress the warning Field "DURATION" of entity "Z##_R_TRAVEL" does not have a mapping to table "Z##_TRAVEL". A "mapping" definition should be added..

    Hint

    You find the relevant pragma in the Problem Description of the syntax warning.
    1. In the Problems view, right-click the syntax warning and choose Problem Description.

    2. Adjust the code as follows:

      Code Snippet
      123
      define behavior for Z##_R_Travel alias Travel persistent table z##_travel ##UNMAPPED_FIELD draft table z##_travel_d

Task 3: Update the Calculated Field with a Side Effect

In your business object, define and implement a determination that calculates the value of the Duration field based on the starting date and end date (suggested name: determineDuration). Make sure that the determination is executed during the save phase and is triggered by changes to either of the two date fields.

Then define a determine action (suggested name: adjustDuration) to trigger the execution of the determination and the validations of the date fields - in case you did the optional part of that exercise. Then define a side effect that is triggered by changes to either of the date fields.

Note

Because this side effect triggers a determination, it is possible that it is not only relevant for the UI service. Therefore, you define it on data model level and let the behavior projection use the side effect of the underlying business object.

Steps

  1. In the behavior definition on data model level Z##_R_TRAVEL, define a determination (suggested name: determineDuration) that is executed during save and triggered by a change of the BeginDate element or the EndDate element.

    1. Add the following code:

      Code Snippet
      1234
      determination determineDuration on save { field BeginDate, EndDate; }
  2. Activate the behavior definition and use a quick fix to add the determination implementation method to the local handler class.

    1. Press Ctrl + F3 to activate the development object.

    2. In the determination statement, place the cursor on the name of the determination and press Ctrl + 1 to invoke the quick assist proposals.

    3. From the list of available quick fixes, choose Add method for determination determineduration of entity z##_r_travel in local class lhc_travel.

  3. In the method implementation, use EML in local mode to read the starting date and the end date of all affected travel instances and store the result in an inline declared internal table (suggested name: travels).

    1. Adjust the code as follows:

      Code Snippet
      123456789
      METHOD determineDuration. READ ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel FIELDS ( BeginDate EndDate ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). ENDMETHOD.
  4. Implement a loop over the travels, assigning an inline declared field symbol (suggested name: <travel>). Inside the loop, update the Duration field in the internal table with the difference between EndDate and BeginDate.

    1. Adjust the code as follows:

      Code Snippet
      12345678910111213
      METHOD determineDuration. READ ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel FIELDS ( BeginDate EndDate ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>). <travel>-Duration = <travel>-EndDate - <travel>-BeginDate. ENDLOOP. ENDMETHOD.
  5. After the loop, implement an EML statement in local mode to update the duration for all affected travel instances.

    Hint

    You can use your internal table travels directly after the WITH addition if you place it inside a CORRESPONDING expression.
    1. Adjust the code as follows:

      Code Snippet
      12345678910111213141516171819
      METHOD determineDuration. READ ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel FIELDS ( BeginDate EndDate ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). LOOP AT travels ASSIGNING FIELD-SYMBOL(<travel>). <travel>-Duration = <travel>-EndDate - <travel>-BeginDate. ENDLOOP. MODIFY ENTITIES OF Z##_R_Travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Duration ) WITH CORRESPONDING #( travels ). ENDMETHOD.
  6. Activate the behavior implementation and return to the behavior definition. Define a determine action (suggested name: adjustDuration) that triggers the execution of the determination and the validations for the date fields.

    If you did not implement validations for the date fields, let the determine action only trigger the determination.
    1. Press Ctrl + F3 to activate the development object.

    2. In the behavior definition, after the definition of the first determine action, add the following code:

      Code Snippet
      12345678
      determine action adjustDuration { validation validateBeginDate; validation validateEnddate; validation validateDateSequence; determination determineDuration; }
  7. Define a side effect that executes the determine action adjustDuration after the user changed either of the fields BeginDate or EndDate and specifies field Duration and the messages as targets.

    1. After the definition of the determine action, add the following code:

      Code Snippet
      12345678
      side effects { determine action adjustDuration executed on field BeginDate, field EndDate affects field Duration, messages; }
  8. Activate the behavior definition on data model level and edit the behavior definition on projection level. Add the determine action adjustDuration to the projection of the root entity and make the projection reuse the side effects that are defined on data model level.

    1. Press Ctrl + F3 to activate the development object.

    2. In the behavior definition on projection level, adjust the code as follows:

      Code Snippet
      1234567891011121314
      projection; strict ( 2 ); use draft; use side effects; define behavior for Z##_C_Travel use etag { use action adjustDuration; }
  9. Activate the behavior projection. Then reload the OData UI service preview and verify that the determine action is executed after you edit the date fields.

    1. Press Ctrl + F3 to activate the development object.

    2. Open the service binding, place the cursor on the root entity and choose Preview....

    3. In the preview, display the details for one of the travels. Change to edit mode and change the value in the Starting Date field.

    4. Press Tab to save the draft and jump to the next input field.

      Result

      The determine action is not yet triggered because there is a second trigger field.
    5. Change the value of the End Date field and press Tab to save the draft and jump to the next input field.

      Result

      Now, the determine action is triggered. If your input is not valid, you see error messages. If your input is valid, the content of the Duration field is updated.