Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
MioYasutake
Active Contributor

Introduction

Fiori elements list report supports the prefilling of fields with default values when creating a new entity. Users will see a popup asking for input parameters. Based on these inputs, an event handler determines the default values to be pre-filled.

prefill-2-1.pngprefill-2-2.png

This functionality is specified through the use of annotations for prefilling fields, as illustrated below:

 

<Annotation Term="Common.DraftRoot">
    <Record Type="Common.DraftRootType">
        <PropertyValue Property="NewAction" String="com.sap.gateway.srvd.c_salesordermanage_sd.v0001.CreateWithSalesOrderType"/>
        ....
        ....
    </Record>
</Annotation>

 

Source: Prefilling Fields When Creating a New Entity | UI5 Demo Kit

My aim is to achieve this functionality with CAP Node.js. While CAP Java documentation provides guidance on implementing this feature, there is currently no equivalent documentation for CAP Node.js.

Implementation

Here goes the implementation. As you can see, it is fairly simple.

Service Definition

Service definition should follow the instructions provided in the CAP Java documentation.

  1. Define an action bound to the draft-enabled entity with an explicitly binding parameter typed with many $self.
  2. Annotate the draft-enabled entity with @Common.DraftRoot.NewAction: '<action name>'.

 

using { my.booklist as db } from '../db/schema';

service BooklistService {
    @odata.draft.enabled
    @Common.DraftRoot.NewAction: 'BooklistService.createDraft'
    entity Books as projection on db.Books actions {
    action createDraft(in: many $self, title: String) returns Books;
  };
}

 

Action Implementation

The action must perform the following tasks:

  1. Set initial data.
  2. Create a draft with initial data.
  3. Return the draft.

 

import cds from '@sap/cds'

module.exports = class BooklistService extends cds.ApplicationService {
    init() {
        const { Books } = this.entities;

        this.on('createDraft', Books, async (req) => {  
            //set initial data
            const data =  {
                ID: cds.utils.uuid(),
                title: req.data.title,
                publisher: 'Test'
            };
            
            //create a draft
            const book = await this.send({
                query: INSERT.into(Books).entries(data),
                event: "NEW",
            });

            //return the draft
            return book;
        })
        return super.init();
    }
}

 

According to the Q&A below, CAP Node.js does not provide draft API, so srv.send() has to be used instead.

https://community.sap.com/t5/technology-q-a/how-can-i-return-a-draft-entity-using-cap-node-js/qaq-p/...

Conclusion

In this blog, we explored how to prefill fields with default values when creating a new entity using CAP Node.js. By defining an action bound to the draft-enabled entity and implementing this action to set initial data and create a draft, developers can effectively prefill fields in their applications.

 

9 Comments
Dinu
Contributor

That's how you create a draft!!

Thanks.

 

akuller_q
Participant

Another option is to use @Common.DefaultValuesFunction to define a function that returns the parameters for the new object.

MioYasutake
Active Contributor
0 Kudos

@akuller_q 

Thank you for your suggestion! I looked into it and discovered the following blog post on the topic.

https://community.sap.com/t5/technology-blogs-by-sap/cap-with-fiori-elements-side-effects-custom-act...

Cmdd
Participant
0 Kudos

Hi @MioYasutake ! 

I'm asking you because (despite all my researches)  this is the only blog post where I could find a reference of the usage of the keyword "many $self" in a bound action.
I thought it was a way to pass a Collection of entities (selected from a table) to the action but I'm not sure I got it right...
What is its purpose? I think the documentations is not very clear about it.
I also published a question on sap dev and stackoverflow but no answers at all.... 😞

https://community.sap.com/t5/application-development-discussions/how-to-perform-a-cap-bound-action-o... 

https://stackoverflow.com/questions/78244073/how-to-perform-a-sap-cap-bound-action-on-a-collection 


 

MioYasutake
Active Contributor
0 Kudos

@Cmdd 

Thanks for your comment ! 

Unfortunately, I haven't seen the usage of 'many $self' in contexts other than this particular use case. For your requirement, I would suggest trying InvocationGrouping: ChangeSet. However, based on your post, it seems this approach didn't work either, correct?

 

tatiana_fetecua
Explorer
0 Kudos

Hi @MioYasutake ! 

First thanks for this very helpful blog!

I have a similar requirement, I need to pre-fill values when creating a new entity (having draft enable) the difference is that the data that I need to pre-fill is a composition of many in a second level hierarchy (a grand-child entity), I need to default the grand-child entity information once the child node is created.

I tried to create the draft in the handler when  the child is created, trying to fill the grand-child entities using the  "this.send:" query that you have explained here, but I get an error "A draft can only be modified via its root entity", any idea how to pre-fill/create the draft for sub-entities?

Thanks

 

Cmdd
Participant

@MioYasutake 

You are right, I already tried with "InvocationGrouping: ChangeSet" but it has a veeeeery weird behaviour: if I set it via annotation, it wraps each single POST in a $batch call and sends a request for each selected row (a lot of requests, not ideal at all); if I set it via invokeAction editFlowAPI it wraps correctly all POSTs in a single $batch call but then the backend callback still processes each entity individually (i.e. it is called n times for n selected rows) and if I select a lot of rows this causes a request timeout 😞

I can't find a proper way to meet my requirements. I actually use an unbound action passing all the IDs of the selected rows (which I don't like)...  but unbound actions still don't have Side Effects (I think it's in the roadmap).

Anyway... thank you for your effort at simplifyings things in this jungle

Chiara

 

MioYasutake
Active Contributor
0 Kudos

@tatiana_fetecua 

Thanks for your comment!

As the error message "A draft can only be modified via its root entity" indicates, the method explained in this blog only works for creating a root entity. I looked into your code, and added below logic.

 

const cds = require('@sap/cds')

module.exports = class MachineService extends cds.ApplicationService {
    init() {
        this.on('CREATE', 'GroupVersion.drafts', async(req, next) => {
            //create grand child
            const limitsGroupVersion = {
                ID: cds.utils.uuid(),
                groupVersionID_ID : req.data.ID,
                value1: 'default1',
                value2: 'default2',
                DraftAdministrativeData_DraftUUID: cds.utils.uuid()
            }
            //insert draft data
            const query =  INSERT.into ('MachineService.LimitsGroupVersion.drafts') .entries(limitsGroupVersion)
            await cds.run(query)
            return next()
        })
        return super.init()
    }
}

 

Additionally, it was necessary to change the cardinality from 'Composition of one' to 'Composition of many' for the child-to-grandchild relationship.

 

entity GroupVersion:cuid{
  groupID : Association to groups;   
  // limitsGroupVersion: Composition of one LimitsGroupVersion on limitsGroupVersion.groupVersionID = $self;
  limitsGroupVersion: Composition of many LimitsGroupVersion on limitsGroupVersion.groupVersionID = $self;
}

 

 

 

As a result, a grand child entity was created along with a child entity.  I am not sure if this is the right approach, but at least it is working.

MioYasutake_1-1712304923357.png

 

tatiana_fetecua
Explorer

@MioYasutake yes thanks again this helped me a lot, I was having and issue because it was not refreshing the Fiori Elements UI app, but then I changed it to after create and I used the same DraftUUID(DraftAdministrativeData_DraftUUID: req.DraftAdministrativeData_DraftUUID,) as the parent and it worked correctly. 

 

 

Labels in this area