Introduction
To enable multi-inline editing also for default root view entities, RAP offers the solution to model a technical root entity with just one instance, from which you can navigate to its object page. This singleton pattern provides a single entrance point to all master data instances. Multi-inline-edit capabilities are always necessary if you want the end user of a UI app to be able to edit, add, and delete all instances directly on a Fiori UI list report without navigating to the object page of one instance. It is always relevant for customizing and maintaining complete table data. Multi-inline capabilities on the UI represent the same feature scope as transaction SM30 in SAP GUI.
Following Steps to enable Multiline Editing with Singleton instance
Step 1: Create Custom Table.
Step 2: Create Interface view on top of table (Child Entity)
Step 3: Create root view entity With Reference to standard view
step 4: Create Consumption view on top of root view and add Metadata.
step 5: Create Consumption view on top of child view and add Metadata.
step 6: create behavior definition on top of base root view (mapping)
make left out join from root view entity to the child table to get all the fields.
step 7: create behavior definition for root consumption view
make application as draft enable
step 8: create draft table for root table
step 9: create draft table for child table.
step 10: Create Service Definition for root consumption view
step 11: Create Service Binding
Preview application
Step 1: Create Custom Table.
@EndUserText.label : ‘Emplyoyee Data’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zemp_a {
key employee_id : abap.char(10) not null;
first_name : abap.char(50);
last_name : abap.char(50);
department : abap.char(20);
joining_date : abap.dats;
is_active : abap.char(1);
changed_by : abap.char(12);
local_last_changed_at : abp_locinst_lastchange_tstmpl;
changed_at : abp_lastchange_tstmpl;
Step 2: Create Interface view on top of table (Child Entity)
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Employee Interface’
//@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity ZI_EMP_AR as select from zemp_a
association to parent ZI_SINGLETONE_AR as _Semp
on $projection.EmpSingleton = _Semp.EmpSingleton
{
@EndUserText.label : ‘Employee ID’
key employee_id as EmployeeId,
1 as EmpSingleton,
@EndUserText.label: ‘First Name’
first_name as FirstName,
@EndUserText.label: ‘Last Name’
last_name as LastName,
@EndUserText.label: ‘Department’
department as Department,
@EndUserText.label: ‘Joining Date’
joining_date as JoiningDate,
@EndUserText.label: ‘Status’
is_active as IsActive,
@Semantics.user.lastChangedBy: true
changed_by as ChangedBy,
// ETag Field
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
// Total ETag Field
@Semantics.systemDateTime.lastChangedAt: true
changed_at as ChangedAt,
_Semp
}
Step 3: Create root view entity (Singleton) With Reference to standard view.
make left outer join in the root view entity to access all fields from the employee table .
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Employee Singletone Interface’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_SINGLETONE_AR as select from I_Language
left outer join zemp_a as zemp on 1 = 1 // to get all the field from the table
composition[ 0..* ] of ZI_EMP_AR as _EMP
{
key 1 as EmpSingleton,
max(zemp.changed_at) as maxChangedAt, //Done for total etag
_EMP
}
where I_Language.Language = $session.system_language
step 4: Create Consumption view on top of root view.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Consumption view for Singletone Employee’
@Metadata.ignorePropagatedAnnotations: true
@UI.headerInfo: {
typeName: ‘Manage Employee’,
typeNamePlural: ‘Employee Singleton’,
title: {
type: #STANDARD,
value: ‘EmpSingleton’,
targetElement: ‘_EMP’
} }
define root view entity ZC_SINGLETONE
provider contract transactional_query
as projection on ZI_SINGLETONE_AR
{
@UI.facet: [{
purpose: #STANDARD,
parentId: ”,
position: 10,
label: ‘Employee Multil inline Edit’,
type: #LINEITEM_REFERENCE,
targetElement: ‘_EMP’
}]
@UI.lineItem: [{ position: 10 }]
key EmpSingleton,
maxChangedAt, //done for total etag field
/* Associations */
_EMP : redirected to composition child ZC_EMP_AR
step 5: Create Consumption view on top of child view.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Consumption view for Employees’
//@Metadata.ignorePropagatedAnnotations: true
@UI: { headerInfo: {
typeName: ‘Employee’,
typeNamePlural: ‘Employees’,
title: {
type: #STANDARD,
label: ‘Employee’,
value: ‘EmployeeId’
} }
}
define view entity ZC_EMP_AR
as projection on ZI_EMP_AR
{
@UI.facet: [{
type:#IDENTIFICATION_REFERENCE
}]
@UI:{ lineItem: [{ position: 10 }], identification: [{ position: 10 }] }
key EmployeeId,
// @ui:{ lineItem: [{ position: 20 }], identification: [{ position: 20 }] }
EmpSingleton,
@UI:{ lineItem: [{ position: 30 }], identification: [{ position: 30 }] }
FirstName,
@UI:{ lineItem: [{ position: 40 }], identification: [{ position: 40 }] }
LastName,
@UI:{ lineItem: [{ position: 50 }], identification: [{ position: 50 }] }
Department,
@UI:{ lineItem: [{ position: 60 }], identification: [{ position: 60 }] }
JoiningDate,
@UI:{ lineItem: [{ position: 70 }], identification: [{ position: 70 }] }
IsActive,
// @ui:{ lineItem: [{ position: 80 }], identification: [{ position: 80 }] }
// ChangedBy,
// LocalLastChangedAt,
// ChangedAt,
_Semp : redirected to parent ZC_SINGLETONE
}
step 6: create behavior definition on top of base root view (mapping)
managed implementation in class zbp_i_singletone_ar unique;
strict ( 2 );
with draft;
define behavior for ZI_SINGLETONE_AR //alias <alias_name>
with unmanaged save // when we dont want save data to table then defin with unmanaged save
draft table ZDT_SINGLETONE
//persistent table t002
lock master
total etag maxChangedAt
authorization master ( instance )
//etag master <field_name>
##draft_op_not_required //to hide the warnings, create uptade, delete
{
// dont want modify the data for root entity
// create;
// update;
// delete;
field ( readonly ) EmpSingleton;
association _EMP { create; with draft; }
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;
}
define behavior for ZI_EMP_AR //alias <alias_name>
persistent table zemp_a
draft table ZDT_EMP_AR
lock dependent by _Semp
authorization dependent by _Semp
etag master LocalLastChangedAt
{
update;
delete;
field ( mandatory : create, readonly :update ) EmployeeId;
field ( readonly ) EmpSingleton, ChangedAt, ChangedBy;
field ( mandatory ) FirstName;
association _Semp;
mapping for zemp_a
{
EmployeeId = employee_id;
FirstName = first_name;
LastName = last_name;
Department = department;
JoiningDate = joining_date;
IsActive = is_active;
ChangedBy = changed_by;
ChangedAt = changed_at;
LocalLastChangedAt = local_last_changed_at;
}
}
step 07: create behavior definition for root consumption view
make application as draft enable .
rojection;
strict ( 2 );
use draft;
define behavior for ZC_SINGLETONE //alias <alias_name>
{
use action Edit;
use action Activate;
use action Discard;
use action Prepare;
use action Resume;
use association _EMP { create; with draft; }
}
define behavior for ZC_EMP_AR //alias <alias_name>
//use etag
{
use update;
use delete;
use association _Semp { with draft; }
}
step 08: create draft table for root table.
@EndUserText.label : ‘Draft table for entity ZI_SINGLETONE_AR’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zdt_singletone {
key empsingleton : abap.int1 not null;
maxchangedat : abap.dec(21,7);
“%admin” : include sych_bdl_draft_admin_inc;
step 09: create draft table for child table.
@EndUserText.label : ‘Draft table for entity ZI_EMP_AR’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zdt_emp_ar {
key employeeid : abap.char(10) not null;
empsingleton : abap.int1;
firstname : abap.char(50);
lastname : abap.char(50);
department : abap.char(20);
joiningdate : abap.dats;
isactive : abap.char(1);
changedby : abap.char(12);
locallastchangedat : abp_locinst_lastchange_tstmpl;
changedat : abp_lastchange_tstmpl;
“%admin” : include sych_bdl_draft_admin_inc;
}
step 11: Create Service Definition for root consumption view
@EndUserText.label: ‘Employee Data Maintainance’
define service ZSR_ENPLOYEE_DATA {
expose ZC_SINGLETONE;
expose ZC_EMP_AR;
}
step 11: Create Service Binding
Preview application
Click Singleton Data
Click on Edit (Here you can edit multiple data and save it to the database table.)
Conclusion
Enabling multiline editing in RAP requires careful configuration of both the backend (CDS Views and Business Logic). Proper data types, UI components, and backend handling must be in place to ensure seamless interaction with multiline text fields.
Introduction To enable multi-inline editing also for default root view entities, RAP offers the solution to model a technical root entity with just one instance, from which you can navigate to its object page. This singleton pattern provides a single entrance point to all master data instances. Multi-inline-edit capabilities are always necessary if you want the end user of a UI app to be able to edit, add, and delete all instances directly on a Fiori UI list report without navigating to the object page of one instance. It is always relevant for customizing and maintaining complete table data. Multi-inline capabilities on the UI represent the same feature scope as transaction SM30 in SAP GUI. Following Steps to enable Multiline Editing with Singleton instance Step 1: Create Custom Table. Step 2: Create Interface view on top of table (Child Entity) Step 3: Create root view entity With Reference to standard view step 4: Create Consumption view on top of root view and add Metadata. step 5: Create Consumption view on top of child view and add Metadata. step 6: create behavior definition on top of base root view (mapping) make left out join from root view entity to the child table to get all the fields. step 7: create behavior definition for root consumption view make application as draft enable step 8: create draft table for root table step 9: create draft table for child table. step 10: Create Service Definition for root consumption view step 11: Create Service Binding Preview application Step 1: Create Custom Table. @EndUserText.label : ‘Emplyoyee Data’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zemp_a {
key employee_id : abap.char(10) not null;
first_name : abap.char(50);
last_name : abap.char(50);
department : abap.char(20);
joining_date : abap.dats;
is_active : abap.char(1);
changed_by : abap.char(12);
local_last_changed_at : abp_locinst_lastchange_tstmpl;
changed_at : abp_lastchange_tstmpl; Step 2: Create Interface view on top of table (Child Entity) @AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Employee Interface’
//@Metadata.ignorePropagatedAnnotations: true
@ObjectModel.usageType:{
serviceQuality: #X,
sizeCategory: #S,
dataClass: #MIXED
}
define view entity ZI_EMP_AR as select from zemp_a
association to parent ZI_SINGLETONE_AR as _Semp
on $projection.EmpSingleton = _Semp.EmpSingleton
{
@EndUserText.label : ‘Employee ID’
key employee_id as EmployeeId,
1 as EmpSingleton,
@EndUserText.label: ‘First Name’
first_name as FirstName,
@EndUserText.label: ‘Last Name’
last_name as LastName,
@EndUserText.label: ‘Department’
department as Department,
@EndUserText.label: ‘Joining Date’
joining_date as JoiningDate,
@EndUserText.label: ‘Status’
is_active as IsActive,
@Semantics.user.lastChangedBy: true
changed_by as ChangedBy,
// ETag Field
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
// Total ETag Field
@Semantics.systemDateTime.lastChangedAt: true
changed_at as ChangedAt,
_Semp
} Step 3: Create root view entity (Singleton) With Reference to standard view. make left outer join in the root view entity to access all fields from the employee table . @AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Employee Singletone Interface’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_SINGLETONE_AR as select from I_Language
left outer join zemp_a as zemp on 1 = 1 // to get all the field from the table
composition[ 0..* ] of ZI_EMP_AR as _EMP
{
key 1 as EmpSingleton,
max(zemp.changed_at) as maxChangedAt, //Done for total etag
_EMP
}
where I_Language.Language = $session.system_language step 4: Create Consumption view on top of root view. @AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Consumption view for Singletone Employee’
@Metadata.ignorePropagatedAnnotations: true
@UI.headerInfo: {
typeName: ‘Manage Employee’,
typeNamePlural: ‘Employee Singleton’,
title: {
type: #STANDARD,
value: ‘EmpSingleton’,
targetElement: ‘_EMP’
} }
define root view entity ZC_SINGLETONE
provider contract transactional_query
as projection on ZI_SINGLETONE_AR
{
@UI.facet: [{
purpose: #STANDARD,
parentId: ”,
position: 10,
label: ‘Employee Multil inline Edit’,
type: #LINEITEM_REFERENCE,
targetElement: ‘_EMP’
}]
@UI.lineItem: [{ position: 10 }]
key EmpSingleton,
maxChangedAt, //done for total etag field
/* Associations */
_EMP : redirected to composition child ZC_EMP_AR step 5: Create Consumption view on top of child view. @AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Consumption view for Employees’
//@Metadata.ignorePropagatedAnnotations: true
@UI: { headerInfo: {
typeName: ‘Employee’,
typeNamePlural: ‘Employees’,
title: {
type: #STANDARD,
label: ‘Employee’,
value: ‘EmployeeId’
} }
}
define view entity ZC_EMP_AR
as projection on ZI_EMP_AR
{
@UI.facet: [{
type:#IDENTIFICATION_REFERENCE
}]
@UI:{ lineItem: [{ position: 10 }], identification: [{ position: 10 }] }
key EmployeeId,
// @ui:{ lineItem: [{ position: 20 }], identification: [{ position: 20 }] }
EmpSingleton,
@UI:{ lineItem: [{ position: 30 }], identification: [{ position: 30 }] }
FirstName,
@UI:{ lineItem: [{ position: 40 }], identification: [{ position: 40 }] }
LastName,
@UI:{ lineItem: [{ position: 50 }], identification: [{ position: 50 }] }
Department,
@UI:{ lineItem: [{ position: 60 }], identification: [{ position: 60 }] }
JoiningDate,
@UI:{ lineItem: [{ position: 70 }], identification: [{ position: 70 }] }
IsActive,
// @ui:{ lineItem: [{ position: 80 }], identification: [{ position: 80 }] }
// ChangedBy,
// LocalLastChangedAt,
// ChangedAt,
_Semp : redirected to parent ZC_SINGLETONE
} step 6: create behavior definition on top of base root view (mapping) managed implementation in class zbp_i_singletone_ar unique;
strict ( 2 );
with draft;
define behavior for ZI_SINGLETONE_AR //alias <alias_name>
with unmanaged save // when we dont want save data to table then defin with unmanaged save
draft table ZDT_SINGLETONE
//persistent table t002
lock master
total etag maxChangedAt
authorization master ( instance )
//etag master <field_name>
##draft_op_not_required //to hide the warnings, create uptade, delete
{
// dont want modify the data for root entity
// create;
// update;
// delete;
field ( readonly ) EmpSingleton;
association _EMP { create; with draft; }
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;
}
define behavior for ZI_EMP_AR //alias <alias_name>
persistent table zemp_a
draft table ZDT_EMP_AR
lock dependent by _Semp
authorization dependent by _Semp
etag master LocalLastChangedAt
{
update;
delete;
field ( mandatory : create, readonly :update ) EmployeeId;
field ( readonly ) EmpSingleton, ChangedAt, ChangedBy;
field ( mandatory ) FirstName;
association _Semp;
mapping for zemp_a
{
EmployeeId = employee_id;
FirstName = first_name;
LastName = last_name;
Department = department;
JoiningDate = joining_date;
IsActive = is_active;
ChangedBy = changed_by;
ChangedAt = changed_at;
LocalLastChangedAt = local_last_changed_at;
}
} step 07: create behavior definition for root consumption view make application as draft enable . rojection;
strict ( 2 );
use draft;
define behavior for ZC_SINGLETONE //alias <alias_name>
{
use action Edit;
use action Activate;
use action Discard;
use action Prepare;
use action Resume;
use association _EMP { create; with draft; }
}
define behavior for ZC_EMP_AR //alias <alias_name>
//use etag
{
use update;
use delete;
use association _Semp { with draft; }
} step 08: create draft table for root table. @EndUserText.label : ‘Draft table for entity ZI_SINGLETONE_AR’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zdt_singletone {
key empsingleton : abap.int1 not null;
maxchangedat : abap.dec(21,7);
“%admin” : include sych_bdl_draft_admin_inc; step 09: create draft table for child table. @EndUserText.label : ‘Draft table for entity ZI_EMP_AR’
@AbapCatalog.enhancement.category : #EXTENSIBLE_ANY
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zdt_emp_ar {
key employeeid : abap.char(10) not null;
empsingleton : abap.int1;
firstname : abap.char(50);
lastname : abap.char(50);
department : abap.char(20);
joiningdate : abap.dats;
isactive : abap.char(1);
changedby : abap.char(12);
locallastchangedat : abp_locinst_lastchange_tstmpl;
changedat : abp_lastchange_tstmpl;
“%admin” : include sych_bdl_draft_admin_inc;
} step 11: Create Service Definition for root consumption view @EndUserText.label: ‘Employee Data Maintainance’
define service ZSR_ENPLOYEE_DATA {
expose ZC_SINGLETONE;
expose ZC_EMP_AR;
} step 11: Create Service Binding Preview application Click Singleton Data Click on Edit (Here you can edit multiple data and save it to the database table.) Conclusion Enabling multiline editing in RAP requires careful configuration of both the backend (CDS Views and Business Logic). Proper data types, UI components, and backend handling must be in place to ensure seamless interaction with multiline text fields. Read More Application Development Blog Posts articles
#SAP