Creating App-Specific Actions

Objectives

After completing this lesson, you will be able to:

  • Create actions with parameters

App-Specific Actions

In SAP Fiori elements, you can use generic actions such as Delete and Create. If your app needs actions beyond this scope, you can create your own application-specific actions.

App-specific actions can be either annotation-based actions or custom actions.

Annotation-Based Actions

Annotation-based actions are created using corresponding annotations. They can be used in the following cases:

  • User confirmation is required, for example for critical actions. A popover dialog opens, and the user has to confirm the action. For more information, see Adding Confirmation Popovers for Actions.
  • Additional user input is required. Some values may already be filled in.
  • The action triggers a back-end call through an OData service, such as Approve.
  • The action triggers navigation, for example to a different app. For more information, see Configuring Navigation.

Custom Actions

Custom actions can be created if the action you need cannot be covered by annotation-based actions. They are created using extension points which need to be registered in the manifest.json file. For more information, see Adding Custom Actions Using Extension Points.

Visibility of Annotation-Based Actions

You can change the visibility of annotation-based actions with the @Core.OperationAvailable annotation.

The annotation can either have a static value (Boolean true or false) or a dynamic value (a path pointing to a property). If the value is static and set to true, the action will always be available, regardless of which item is selected. If the value is dynamic, the action can be enabled or disabled dynamically, depending on a certain condition.

Have a look at the following CAP CDS annotation:

Code snippet
actions {
   deductDiscount @(
    Core.OperationAvailable : { $edmJson: { $Eq: [{ $Path: 'in/TravelStatus_code'}, 'O']}}
  );
}
Expand

In this example, the action deductDiscount will only be available for items with the TravelStatus'O' (open). The value for the annotation Core.OperationAvailable is a dynamic expression enclosed in { $edmJson: { … }}.

For more information about dynamic expressions in OData Version 4, see OData Common Schema Definition Language (CSDL) JSON Representation Version 4.01 (oasis-open.org).

The CDS compiler translates the expression into the corresponding XML code. The annotation above is translated to the following OData representation:

Code snippet
<Annotations Target="TravelService.deductDiscount(TravelService.Travel)">
	<Annotation Term="Core.OperationAvailable">
	  <Eq>
	    <Path>in/TravelStatus_code</Path>
	      <String>O</String>
	  </Eq>
	</Annotation>
</Annotations>
Expand

For more information about CAP CDS and dynamic expressions, see: Dynamic Expressions.

Create an Application-Specific Action with a Mandatory Parameter

In addition to generic actions common to all SAP Fiori elements apps, you can also create app-specific actions.

In this exercise, you will create an annotation-based action for the Travels table. It will deduct a discount from both the booking fee and the total price. The action will have a mandatory parameter: the discount percentage with the default value of 5. The action will be added to the table toolbar in the list report.

Task Flow

In this exercise, you will first create the deductDiscount action and implement it. The action will only apply to travels with the travel status Open. Then, you will use the Page Editor to add the action to the table toolbar in the list report. Finally, you will test the newly created action.

Prerequisites

You have completed the exercise Make the Delete Action Unavailable for Accepted and Canceled Travels in the unit Examining Actions Available on the List Report (lesson: Applying Generic Actions Provided by SAP Fiori Elements). Alternatively, you can check out its solution branch: solution/make-delete-action-unavailable-for-accepted-travels.

Watch the Simulation and Perform the Steps

This exercise contains a simulation displaying all the steps. You can follow the simulation with your own trial account.

Steps

  1. Create the action and implement it.

    1. Open the travel-service.cds file in SAP Business Application Studio.

    2. Add the definition of the new action deductDiscount. Note that it has a mandatory parameter, and its default value is five percent.

      Add the following code snippet to the actions section of the travel-service.cds file:

      Code snippet
      action deductDiscount(@(UI.ParameterDefaultValue : 5)percent: Percentage not null @mandatory ) returns Travel;
      Expand

    3. Open the travel-service.js file to implement the action. The action will update both the TotalPrice and BookingFee properties if the Travel Status is not 'A' (Accepted) and the Booking Fee is not null.

      Add the following code snippet to the srv/travel-service.js file:

      Code snippet
        this.on ('deductDiscount', async req => {
          let discount = req.data.percent / 100
          let succeeded = await UPDATE (req._target)
            .where `TravelStatus_code != 'A'`
            .and `BookingFee is not null`
            .with (`
              TotalPrice = round (TotalPrice - BookingFee * ${discount}, 3),
              BookingFee = round (BookingFee - BookingFee * ${discount}, 3)
            `)
          if (!succeeded) { //> let's find out why...
            let travel = await SELECT.one `TravelID as ID, TravelStatus_code as status, BookingFee` .from (req._target)
            if (!travel) throw req.reject (404, `Travel "${travel.ID}" does not exist; may have been deleted meanwhile.`)
            if (travel.status === 'A') req.reject (400, `Travel "${travel.ID}" has been approved already.`)
            if (travel.BookingFee == null) throw req.reject (404, `No discount possible, as travel "${travel.ID}" does not yet have a booking fee added.`)
          } else {
            // Note: it is important to read from this, not db to include draft handling
            // REVISIT: through req._target workaround, IsActiveEntity is non-enumerable, which breaks this.read(Travel, req.params[0])
            const [{ TravelUUID, IsActiveEntity }] = req.params
            return this.read(Travel, { TravelUUID, IsActiveEntity })
          }
        })
      Expand

  2. Restrict the availability of the action to travels with the Open travel status.

    1. Open the field-control.cds file and add the @Core.OperationAvailable annotation to the deductDiscount action.

      Add the following code snippet to the app/travel_processor/field-control.cds file:

      Code snippet
      deductDiscount @(
          Core.OperationAvailable : { $edmJson: { $Eq: [{ $Path: 'in/TravelStatus_code'}, 'O']}}
        );
      Expand

    2. The action will now only be available if the Travel Status is 'O' (Open).

  3. Open the Page Editor to add the changes.

    1. Go to List Report > Toolbar and add the TravelService.deductDiscount action to the table toolbar.

    2. Select the action you've just added. Note that you can change its settings in the pane on the right.

    3. Select the Label annotation to generate a text key in the i18n.properties file.

  4. Return to the app to check your newly created action.

    1. The action Deduct Discount now appears in the table toolbar.

    2. Select a travel with the Accepted travel status. Note that the action is not available.

    3. Now select a travel with the Open travel status. The action is now available and can be applied.

    4. Apply the Deduct Discount action to the selected travel. Both the Booking Fee and Total Price have now been decreased by the specified amount.

Result

In this lesson, you have added an app-specific action to the table toolbar and restricted the availability of the action for specific items.

Note

Next Steps

For more information, see Actions in the List Report.

Log in to track your progress & complete quizzes