Technology Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
iulia2
Associate
Associate
4,597

Introduction

In this workshop, we will build a simple Item business object using the ABAP RESTful Application Programming Model (RAP) on an SAP BTP trial system.

RAP is SAP’s strategic programming model for developing cloud-ready, Fiori-enabled business applications in ABAP. It provides a clean, end-to-end programming model where you can define business objects, behaviors, services, and UI-ready projections in a structured way.

In this blog, we will:

  • Create a database table for items.

  • Define CDS root and projection views.

  • Implement behavior definitions with CRUD, and enhance it with actions, validations and determinations.

  • Expose the business object as an OData service.

  • Preview it in Fiori.

  • Model relationships using associations and compositions between entities.

  • Implement value helps to improve user input and Fiori UX.

By the end, you will understand how RAP objects are structured, connected, and how Clean Core principles are applied in a real example.

Why RAP?

  • Cloud-ready: SAP’s standard for S/4HANA cloud extensions and custom apps.

  • Clean Core compliant: No modifications to standard objects; all extensibility is via APIs.

  • End-to-end model: Business logic, UI, draft handling, and authorizations all in one place.

  • Consistency: Automatic CRUD, locking, and draft handling.

  • Fiori-ready: Generates OData services consumable by Fiori Elements.

  • Productivity: Faster development than classical ABAP or BOPF.

Environment Setup

For this demo, we use ABAP Development Tools (ADT) in Eclipse connected to an ABAP on Cloud project on an SAP BTP trial system. This setup gives us access to all RAP development tools and lets us preview Fiori apps directly from Eclipse.

If you’re setting this up for the first time, SAP provides a step-by-step tutorial to install Eclipse IDE, add the ABAP Development Tools (ADT) plugin and connect to your SAP system. Download the Eclipse IDE and add the ABAP Development Tools (ADT) Plugin | SAP Tutorials 

Useful ADT Shortcuts

Here are some useful ADT keyboard shortcuts for the ABAP development in Eclipse.

shortcuts.png

Info: You can display the full list of available shortcuts in the Show Key Assit in ADT by pressing Ctrl+Shift+L.

 

Part 1: Simple BO

Step 1: Create a Package

Before creating RAP objects, we need a package to organize them. Packages are like folders in Eclipse—they help manage all the development objects together.

1. In Eclipse, right-click your ABAP on Cloud projectNewABAP Package.

1.png

2. Enter package details

2.png

 

 

 

 

 

 

 

 

 

 

Why this matters:

  • All RAP objects (tables, CDS views, behavior definitions, service definitions) should be in the same package.

  • It ensures clean core compliance, meaning your custom objects don’t modify standard SAP objects directly.

 

Step 2: Create Database Table

The database table is the foundation of your RAP business object. For this example, we’ll create a table called ZITEM02 for our Item BO. 

1. Right-click your package → NewOther Repository Object → search for "Database Table".

3.png4.png

2. Add the fields and mark item_id as the primary key.

@EndUserText.label : 'Item Table'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zitem_02 {

  key client            : abap.clnt not null;
  key item_id           : sysuuid_x16 not null;
  name                  : abap.char(50) not null;
  description           : abap.char(100);
  price                 : abap.dec(10,2);
  status_code           : abap.char(5) not null;
  local_created_by      : abp_creation_user;
  local_created_at      : abp_creation_tstmpl;
  local_last_changed_by : abp_locinst_lastchange_user;
  local_last_changed_at : abp_locinst_lastchange_tstmpl;
  last_changed_at       : abp_lastchange_tstmpl;

}

3. Activate the table.

5.png

The table ZITEM_02 serves as the persistent layer for your RAP business object. All data is stored here. 

 

Administrative Fields:

local_created_by/ local_created_at → track who created the record and when.

local_last_changed_by / local_last_changed_at → track local changes before activation (important for draft-enabled objects).

last_changed_at → tracks the last committed change; used as the ETag for locking and consistency.

Why Admin Fields Matter:

RAP automatically uses them for draft management, locking, and optimistic concurrency control. Even if your app is simple, including them makes your BO ready for enhancements such as validations, actions, and side effects.

 

Step 3: Create CDS 

1. Root View 

The root view sits on top of the database table and defines the entity structure for your business object. It is the source for the behavior definition.

1. Right-click the database table → New Data Definition 

6.png

2. Select the Root View Entity template.

7.png

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Root View Entity for Item'
@Metadata.allowExtensions: true
define root view entity ZR_ITEM_02
  as select from zitem_02
{
  key item_id               as ItemId,
      name                  as Name,
      description           as Description,
      price                 as Price,
      status_code           as StatusCode,

      local_created_by      as LocalCreatedBy,
      local_created_at      as LocalCreatedAt,
      local_last_changed_by as LocalLastChangedBy,
      local_last_changed_at as LocalLastChangedAt,
      last_changed_at       as LastChangedAt
}

Table = raw persistence.

Root view = business-level representation, enriched with semantics, annotations, and ready for behavior logic.

The root view defines the primary key item_id to match the table.

2. Projection View

The projection view is built on top of the root view and serves as the entry point for service exposure. Here, we select which fields are exposed in the transactional OData service. We can also include UI annotations to guide Fiori Elements in rendering the app automatically.

1. Right-click the Root View CDS → New Data Definition 

2. Select the Projection View template. 

8.png

The root view contains the core business data and logic, while the projection only exposes what the frontend needs. This way, we don’t modify or clutter the core data model with UI-specific details, keeping the core stable and extensible.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projection View for Item'
@Metadata.allowExtensions: true
define root view entity ZC_ITEM_02
  provider contract transactional_query
  as projection on ZR_ITEM_02
{
  key ItemId,
      Name,
      Description,
      Price,
      StatusCode
}

For today, I’ll keep the UI annotations minimal here and show how to add them cleanly in a metadata extension later, which keeps our projection view tidy and focused on data exposure.

 

Step 4: Create Behavior Definition

The behavior definition specifies how this entity behaves at runtime: which operations are allowed, It maps fields from the root view to the database table, and how draft actions are handled.

1. Right-click the Root View CDS → New Behavior Definition

9.png

Since the purpose of this first example is to display the list report, we only need to map fields from the root view to the database table. 

managed implementation in class zbp_r_item_02 unique;
strict ( 2 );

define behavior for ZR_ITEM_02 alias Item
persistent table zitem_02
lock master
authorization master ( instance )
etag master LastChangedAt
{
  create ( authorization : global );
  update;
  delete;
  field ( readonly, numbering : managed ) ItemId;

  mapping for zitem_02
    {
      ItemId      = item_id;
      Name        = name;
      Description = description;
      Price       = price;
      StatusCode  = status_code;
      LastChangedAt = last_changed_at;
      LocalCreatedAt = local_created_at;
      LocalCreatedBy = local_created_by;
      LocalLastChangedAt = local_last_changed_at;
      LocalLastChangedBy = local_last_changed_by;
    }
}

 

  • readonly → The field cannot be changed by the user. RAP ensures it is protected in the frontend and backend.

  • numbering : managed → RAP automatically generates unique values for this field when a new entity is created. You don’t need to write your own logic for primary keys like UUIDs or sequences.

     

The behavior projection allows us to expose only a subset of behavior from the main entity for a specific service. This is useful if we want different services to expose different parts of the behavior without modifying the core behavior definition.

projection;
strict ( 2 );

define behavior for ZC_ITEM_02 alias Item
{
  use create;
  use update;
  use delete;
}

 

Step 5: Create Service Definition

The service definition declares which projection views are exposed as OData services. Here, we expose our projection view, making our User BO available for Fiori or API consumption. Service definitions are protocol-agnostic and can later be consumed via different bindings.

@EndUserText.label: 'Service Definition for Item'
define service ZSD_ITEM {
  expose ZC_ITEM_02 as Item;
}

 

Step 6: Create Service Binding

Right-click the Service Definition → New Service Binding

10.png

Finally, the service binding connects the service definition to a specific protocol, such as OData V2 or V4. After activation, Publish the service and it can be accessed directly from Fiori elements or any OData client.

11.png

 

At this stage, our Business Object is not draft-enabled and does not support create/update/delete yet.
This means the Fiori Elements application shows a read-only list report, and because the table is empty, the preview will be blank.

12.png

This step is intentional — it confirms that:

  • the CDS views are correct

  • the projection is exposed

  • the service binding is active

  • the app renders successfully with no errors

Even with no data, seeing the list report appear is an important milestone. In the next sections, we will add draft support and enable full CRUD operations.

 

Part 2: Enabling Draft, CRUD Operations, and Transactional Behavior in the RAP Business Object

In Part 1 of this series, we created the foundation of our RAP application:

  • A clean core–compliant database table

  • The CDS root view entity

  • The projection view

  • A minimal behavior definition

  • A service binding that exposes our object to Fiori

At that stage, the generated application was intentionally read-only, serving as a good starting point to verify that the service and CDS structure work correctly.

Now, in Part 2, we take the next major step: turning our read-only object into a full transactional Business Object using the managed RAP BO approach. This includes:

  • Activating Create, Update, and Delete support

  • Enabling Draft handling for safe data entry

  • Allowing the Fiori Elements app to automatically render UI flows such as Edit, Save, Delete, and Discard Draft

  • Preparing the BO for further business logic enhancements (which we will add in Part 3)

Why We Enable Draft in RAP?

Draft is one of the key features of Fiori Elements applications.

Before enabling it, it’s important to explain why draft is needed.

Users expect modern UI behavior

In modern SAP Fiori apps, users do not commit their changes immediately. Instead:

  • They can start editing an object.

  • Leave the page.

  • Navigate back later.

  • Recover their unsaved changes.

This improves the UX significantly and eliminates accidental data loss.

Draft prevents incomplete database commits

Draft ensures that:

  • Partial or inconsistent data is never saved.

  • Users can enter complex data in multiple steps.

  • Locks on active instances are minimized.

This is especially important in enterprise systems with many concurrent users.

RAP provides draft automatically

Thanks to RAP’s managed BO runtime, draft functionality requires no custom code — only a few declarations in the behavior definition.

 

Step 1: Updating the Behavior Definition of the Root Entity

1. Add with draft;

2. Add draft table zitem_02_d;

This associates the BO with a draft table.

If the draft table does not exist yet, ADT will offer to create it automatically.

3. Click on "Quick assist" ( CTRL + 1 )

13.png

 

@EndUserText.label : 'Draft table for entity ZR_ITEM_02'
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zitem_02_d {

  key mandt          : mandt not null;
  key itemid         : sysuuid_x16 not null;
  name               : abap.char(50);
  description        : abap.char(100);
  price              : abap.dec(10,2);
  statuscode         : abap.char(5);
  localcreatedby     : abp_creation_user;
  localcreatedat     : abp_creation_tstmpl;
  locallastchangedby : abp_locinst_lastchange_user;
  locallastchangedat : abp_locinst_lastchange_tstmpl;
  lastchangedat      : abp_lastchange_tstmpl;
  "%admin"           : include sych_bdl_draft_admin_inc;

}

Draft tables contain:

  • Table fields

  • Administrator fields

  • Temporary user changes

This table supports parallel editing and safe drafts.

4. Additional Draft Actions and What They Do

In RAP, the draft handling pattern can be extended beyond the basic actions .
For more advanced scenarios — especially when working with optimized draft flows, background checks, or multi-step editing — we can include the following actions:

  draft action Activate optimized;
  draft action Edit;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare { }

Beyond the basic Edit / Activate / Discard pattern, RAP provides additional draft actions to improve performance and user experience.

  • Resume – restores an existing draft so users can continue where they left off.

  • Prepare – pre-fills data before the user sees the draft instance.

These actions require no manual coding unless you want to enhance them, and together they deliver a modern, safe, Fiori-compliant editing workflow.

5. Add total etag LastChangedAt;

This ETag is used by RAP to guarantee concurrency and data consistency.
It prevents situations where one user accidentally overwrites changes made by another user.
It is also required for draft handling and for the Fiori Elements UI, which relies on this timestamp to detect outdated edits.
RAP automatically manages this field, updating it whenever the record is changed.

6. Update the Behavior Projection

The projection behavior definition exposes the business behavior to the Fiori UI and the OData service. Even if the root BO supports create, update, delete, and draft actions, they must be explicitly enabled in the behavior projection. Without this step, Fiori Elements will not show the Create/Edit/Delete or draft buttons. The projection layer ensures clean core separation by keeping internal logic in the root BO while exposing only the required operations to the UI.

projection;
strict ( 2 );
use draft;

define behavior for ZC_ITEM_02 alias Item
{
  use create;
  use update;
  use delete;

  use action Activate;
  use action Edit;
  use action Discard;
  use action Resume;
  use action Prepare;
}

7. Create Behavior Implementation Class 

Eclipse generates a global ABAP class with the naming convention. 

14.png

 

Understanding What “Managed Implementation” Means

Standard CRUD is generated automatically

You do not need to write: create/ update/ delete logic, draft save/ load logic, locking logic, ETag management

RAP provides all of this out of the box.

The class is only for custom behavior

This is where we will later implement validations (check input before save), determinations (auto-fill fields), actions (mark item as completed), unmanaged logic, feature control, authorization checks, if needed.

But for Part 2 (Draft + CRUD), the class stays empty.

8. Creating the Metadata Extension (UI Annotations)

After creating the projection view and exposing the BO behavior, we need to define how this data should be displayed in Fiori Elements apps.

This is where metadata extensions come into play. They allow you to add UI annotations without touching the core CDS view, keeping your implementation clean and following Clean Core principles.

Right-click on the Consumption View → NewMetadata Extension

@Metadata.layer: #CORE
annotate entity ZC_ITEM_02 with
{
  @UI.facet: [
         {
             purpose: #STANDARD,
             type: #IDENTIFICATION_REFERENCE,
             label: 'Items',
             position: 10
         } ]
  ItemId;
  @UI.lineItem: [{ position: 10, label: 'Name' }]
  @UI.identification: [{ position: 10, label: 'Name' }]
  Name;
  @UI.lineItem: [{ position: 20, label: 'Description' }]
  @UI.identification: [{ position: 20, label: 'Description' }]
  Description;
  @UI.lineItem: [{ position: 30, label: 'Price' }]
  @UI.identification: [{ position: 30, label: 'Price' }]
  Price;
  @UI.lineItem: [{ position: 40, label: 'Status Code' }]
  @UI.identification: [{ position: 40, label: 'Status Code' }]
  StatusCode;

}

The metadata extension defines how the Item BO is displayed in Fiori Elements. Using annotations like @ui.lineItem and @ui.Identification, we specify which fields appear in the list report and object header. The @ui.facet groups the fields into a standard identification card. Placing these annotations in a metadata extension layer ensures Clean Core compliance, separating UI concerns from business logic, while enabling a ready-to-use Fiori preview.

After adding draft-enabled behavior and the metadata extension, let’s see the results in the Fiori preview:

17.png

Create Button appears automatically at the top of the list report and clicking it opens a draft object, ready for entry.

19.png

Delete Button appears at the top of the list report.

List Report displays all annotated fields without manually selecting columns, shows all items.

18.png

Object Page: clicking a list entry opens the object page.

Edit Button is visible on the object page, clicking it opens the draft for editing, supporting safe modifications before activation.

This confirms that draft-enabled RAP objects, combined with metadata annotations, automatically generate a full-featured Fiori UI: list report, object page, and CRUD actions—all without writing UI code manually.

 

Part 3: Enhancing the Item BO with Actions, Determinations, Validations

In Part 1 and Part 2, we created the basic structure of a RAP Business Object, enabled draft handling, activated CRUD operations, and prepared a clean UI using metadata extensions.

Now, in Part 3, we introduce the most important part of RAP:
Business logic — implemented through:

  • Validations (check before save)

  • Determinations (auto-fill fields)

  • Actions (custom operations triggered by the user)

  • Side Effects (UI refresh logic after actions)

To demonstrate these features in a realistic scenario, we switch to a second simple Business Object: Leave Requests

This BO includes fields such as employee name, requested days, and status.
With it, we implement:

  • A validation to ensure the requested days are > 0

  • A determination that sets the default status

  • A custom action Approve Leave Request

  • Metadata annotations to show an action button in the UI

  • Side effects to refresh the UI automatically after approval

Leave Request BO — Behavior Definition

Here is the behavior definition, which we will explain piece by piece:

managed implementation in class zbp_r_leavereq unique;
strict ( 2 );
with draft;

define behavior for ZR_LEAVEREQ
persistent table zleavereq
draft table zleavereq_d
lock master
total etag LastChangedAt
authorization master ( instance )
etag master LastChangedAt
{
  create ( authorization : global );
  update;
  delete;
  field ( readonly, numbering : managed ) Leaveguid;

  draft action Activate optimized;
  draft action Edit;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare { }

  validation checkDays on save { create; update; field DaysReq; }
  determination setDefaultStatus on modify { create; }
  action approveLeaveRequest;


  mapping for zleavereq
    {
      Leaveguid          = leaveguid;
      EmployeeName       = employee_name;
      DaysReq            = days_req;
      Status             = status;
      LocalCreatedAt     = local_created_at;
      LocalCreatedBy     = local_created_by;
      LocalLastChangedAt = local_last_changed_at;
      LocalLastChangedBy = local_last_changed_by;
      LastChangedAt      = last_changed_at;
    }
}

Validation: checkDays

A validation allows you to stop a save operation if business rules are violated.

In this example, we ensure that requested days > 0.

Validation Logic in the Behavior Implementation Class

METHOD checkDays.
  READ ENTITIES OF zr_leavereq IN LOCAL MODE
  ENTITY zr_leavereq
  ALL FIELDS
  WITH CORRESPONDING #( keys )
  RESULT DATA(lt_leave_req).

  LOOP AT lt_leave_req ASSIGNING FIELD-SYMBOL(<fs_lr>).
    IF <fs_lr>-DaysReq < 1.
      APPEND VALUE #( %tky = <fs_lr>-%tky ) TO failed-zr_leavereq.
      APPEND VALUE #(
          %tky =  <fs_lr>-%tky
          %msg = new_message_with_text(
          text = 'Number of days should be > 0.'
          severity = if_abap_behv_message=>severity-error )
      ) TO reported-zr_leavereq.
    ENDIF.
  ENDLOOP.
ENDMETHOD.

What This Does

  • Prevents invalid leave requests
  • Displays an error in the Fiori UI
  • Ensures data consistency

The validation is triggered on save, both for create and update.

Determination: setDefaultStatus

A determination is used to auto-fill or derive values without UI involvement.

Here, whenever a leave request is created, we automatically set its initial status:

METHOD setDefaultStatus.
  DATA(key) = VALUE #( keys[ 1 ] OPTIONAL ).

  MODIFY ENTITIES OF zr_leavereq IN LOCAL MODE
  ENTITY zr_leavereq
  UPDATE FIELDS ( Status )
  WITH VALUE #( ( %tky = key-%tky
                  Status = 'Pending' ) ).

ENDMETHOD.

Action: approveLeaveRequest

Actions implement custom operations that the user triggers manually from the UI.

Here, we add an Approve action.

METHOD approveLeaveRequest.
  DATA(key) = VALUE #( keys[ 1 ] OPTIONAL ).

  MODIFY ENTITIES OF zr_leavereq IN LOCAL MODE
  ENTITY zr_leavereq
  UPDATE FIELDS ( Status )
  WITH VALUE #( ( %tky = key-%tky
                  Status = 'Approved' ) ).

ENDMETHOD.

When is an Action Appropriate?

Use an action when:

  •  you require manual user intent (approve, reject, submit)
  •  the change differs from standard CRUD
  •  you want a button in the UI

Adding the Action to the Behavior Projection

Without adding it to the projection, the UI cannot call the action.

projection;
strict ( 2 );
use draft;

define behavior for ZC_LEAVEREQ
{
  use create;
  use update;
  use delete;

  use action approveLeaveRequest;

  side effects
  {
    action approveLeaveRequest affects $self, field Status;
  }

  use action Activate;
  use action Edit;
  use action Discard;
  use action Resume;
  use action Prepare;
}

Side Effects — Keeping Fiori UI Updated

Side effects tell RAP which fields or entities might change when an action runs.

This ensures the UI refreshes automatically after the action is executed.

This informs Fiori Elements:

  • After pressing “Approve”, refresh the Status field

  • Update list report and object page automatically

Without side effects, the UI does not update until a manual refresh.

Adding an Inline Action Button to the Fiori List Report

In the Metadata Extension, you expose the action as a button:

@UI.lineItem: [{
  position: 40,
  type: #FOR_ACTION,
  dataAction: 'approveLeaveRequest',
  label: 'Approve',
  inline: true
}]

Meaning of inline: true

✔ The approve button appears directly in the table row
✔ User can approve without navigating to the object page
✔ Great for workflow-like processes

image.png

 

Part 4: CDS Relationships: Associations and Composition

In this part, we move to a more realistic RAP scenario, where our business object is no longer a single table.
Instead, we have Projects and Tasks, and these objects need to be connected.

RAP uses CDS associations and compositions to model these relationships.

CDS Associations — Linking Entities Together

An association is basically a link between two view entities — similar to a JOIN — but it is not executed until we actually navigate to it.

Association Between Task and User

In our app, each Task is assigned to a User.
We represent this using an association:

define view entity ZI_TASK
  as select from ztask_01
  association [0..1] to ZR_USER         as _User    on _User.UserCode = $projection.Assignee
{

What this means (simple explanation)

  • association → create a link between Task and User

  • ZR_USER → the target entity (the User root)

  • _User → the name we will use to navigate to it

  • on ... → join condition ( Assignee equals the UserCode )

Cardinality — What [0..1] Means

Cardinality describes how many related records the association can return.

This means:

  • 0 → the task might not have an assigned user (Assignee is empty)

  • 1 → if a user is assigned, there can be only one matching UserCode

This is a typical “optional 1-to-1” relationship.

If Assignee = 'USR01', we get that one user.
If Assignee is empty, we get nothing.

 

Using the Association in the Projection

Inside the projection view ZC_TASK, we read the user’s name through the _User association:

_User.UserName as AssigneeName,

This is only read, not stored anywhere in the Task table.

To make SAP Fiori show the text automatically, we add:

@ObjectModel.text.element: ['AssigneeName']
Assignee,

Result in the Fiori UI:

  • The UI displays UserCode + UserName
    image.png

 

 

 

 

 

 

 

 

Why Associations Are Great

  • They avoid duplicate data

  • They allow “on-demand” navigation

  • They plug naturally into Fiori Elements
    (value helps, display texts, navigation links)

Composition — Building a Parent/Child Relationship (Project → Tasks)

Now that we’ve seen simple associations, let’s move to something more powerful in RAP:
composition.

A composition is not just a link — it models a parent/child relationship where the child’s lifecycle depends entirely on the parent.

In our example:

  • Project is the parent

  • Tasks are the children

This is a very common RAP pattern used in real applications.

Defining the Composition in the Root Entity

In the Project root view (ZR_PROJECT), we define:

composition [0..*] of ZI_TASK as _Tasks

What this means (simple explanation):

  • composition → strong parent–child relationship

  • [0..*] → a project can have zero, one, or many tasks

  • of ZI_TASK → tasks are the child entity

  • _Tasks → the navigation property we will use in the UI

This tells RAP that:

 Tasks belong to a project
 Tasks cannot exist without the project
 If the project is deleted, all its tasks must be deleted too
 Tasks must always reference a ProjectId (its parent)

This is different from a normal association — it defines ownership.

Exposing the Composition in the Projection

In the Project projection (ZC_PROJECT), we expose the composition:

_Tasks : redirected to composition child ZC_TASK

This allows the UI to show the list of Tasks inside the Project object page, including:

  • Create Task

  • Edit Task

  • Delete Task

All directly in the Project UI.

Without this redirection, the Tasks table wouldn't appear in the Fiori Elements app.

Behavior Definition: Root and Child Must Work Together

Since compositions define hierarchy, the behavior must reflect that hierarchy.

define behavior for ZR_PROJECT alias Project
{
  create;
  update;
  delete;

  association _Tasks { create; with draft; }
}

Meaning:

  • The Project behavior controls the entire object

  • It is allowed to create children (_Tasks { create; })

  • Children are created in the context of the parent

In Fiori:

  • When you open a Project → Task table appears

  • “Create” inside the Tasks table creates a child of this project

define behavior for ZI_TASK alias Task
{
  update;
  delete;

  field (readonly) ParentId;

  association _Project { with draft; }
}

Meaning:

  • Tasks cannot create their own parent

  • Tasks inherit locking and authorization from the Project

  • Tasks cannot change the ParentId (because it is read-only)

The lifecycle looks like this:

  • You create a Project

  • Inside the project, you add Tasks

  • Tasks cannot exist outside the Project

Why Composition Is Important

It gives RAP a clear structure of your data model
It enables nested object pages (Projects → Tasks)
It simplifies locking, authorization, and lifecycle management
Fiori Elements automatically understands parent/child UI patterns
Child objects cannot “float” or become inconsistent

 

Part 5: Value Helps 

So far, we built our entities, associations, and composition.
But in a real Fiori Elements app, users shouldn’t manually type in IDs.

Instead, they should select values from a value help (similar to a search help in classical ABAP).

In RAP, value helps are implemented using CDS view entities and the annotation:

@Consumption.valueHelpDefinition

Creating a Value Help View

Let’s look at the value help for selecting a User.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Value Help for User'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZI_USER_VH
  as select from ZR_USER
{
    @UI.hidden: true
  key UserId,
      UserCode,
      UserName
}
  • It selects from ZR_USER, our user root entity.

  • The value help will show UserCode and UserName in the popup search dialog.

  • This view is not a projection — it's a standalone VH definition recommended for RAP apps.

This view is now ready to be used by any field that needs a value help for Users.

Plugging the Value Help into the Task Projection

In the Task projection, we attach the value help to the Assignee field:

@Consumption.valueHelpDefinition: [{
    entity: { name: 'ZI_USER_VH', element: 'UserCode' },
    useForValidation: true
}]
Assignee,

What this does:

Opens a value help popup
Shows columns from ZI_USER_VH (UserCode, UserName)
Ensures the selected UserCode is valid (useForValidation: true)
Prevents users from entering invalid users

image.png

 

 

3 Comments