Skip to Content
Product Information
Author's profile photo Mariana Naboka

Annotating annotations in SAP Cloud Application Programming (CAP) with code completion

I’ve heard from several developers that specifying annotations is the most challenging part when developing applications based on the SAP Fiori elements framework. This is especially true if you have no tools support, as many of us felt trying to add annotations in .cds files of CAP (SAP Cloud Application Programming) projects. We recently introduced code completion for OData annotations in CAP, so even the trickiest things, such as applying annotations to an existing annotation or its part can be achieved with a few clicks.

In this blog post, I will show you how it works with a couple of examples.

Example 1: Annotating UI.DataField records to control table responsiveness

 

In this example, I have a table of safety incidents represented by the annotation “UI.LineItem”.

annotate service.SafetyIncidents with @UI.LineItem : [
{
    $Type : 'UI.DataField',
    Value : title,
},
{
    $Type : 'UI.DataField',
    Value : priority_code,
},
{
    $Type : 'UI.DataField',
    Value : category_code,
},
{
    $Type : 'UI.DataField',
    Value : incidentStatus_code,
},
];

On small screens, such as a typical smartphone, the first three columns keep their places by default, and all the other columns move to the second line. But, sometimes you want to show the important information in the right part of the table on large screens and still keep them distinctly visible on the small screens too. In this case, the columns in the middle should slide down to the second line: note the Priority column in the right screenshot below.

 

To implement this, I applied the annotation “UI.Importance” to UI.DataField records in my LineItem. So, I annotate the data fields for incident title, category and status with High importance, to keep them always distinctly visible. The only non-high value now moves to the second line, giving its space to the VIPs.

annotate service.SafetyIncidents with @UI.LineItem : [
{
    $Type : 'UI.DataField',
    Value : title,
    ![@UI.Importance] : #High,
},
{
    $Type : 'UI.DataField',
    Value : priority_code,
},
{
    $Type : 'UI.DataField',
    Value : category_code,
    ![@UI.Importance] : #High,
},
{
    $Type : 'UI.DataField',
    Value : incidentStatus_code,
    ![@UI.Importance] : #High,
},
];

Even if you forget the correct syntax for this annotation/value or do not have this example in front of you, use the code completion (CTRL+Space) next to the Value and select a suggestion from the list – it could be even faster than copying!

In this video, you can see it yourself:

Note: Alternatively, I could have annotated the priority_code record with the low importance rather than the 3 records with high, but with the real-life tables having much more than 4 columns it makes more sense to mark those that are more important and ignore the remaining ones.

Example 2: Annotating UI.LineItem to highlight table rows based on criticality

In this example, I would like to highlight the rows in my table based on the incident priority. Thus, priority would still be easily visible, even when the value appears in the second row.

According to the SAP UI5 documentation, I need to apply the annotation “UI.Criticality” to the whole Ui.LineItem annotation.

This is really tricky in cds syntax, even for experienced users: the LineItem annotation should be wrapped in curly brackets { } with $value and then annotated with UI.Criticality in ![ ] syntax. Finally, the value pointing to the entity element containing criticality information should be added:

annotate service.SafetyIncidents with @UI.LineItem : {
    ![@UI.Criticality] : priority.criticality,
    $value:  [
        {
            $Type             : 'UI.DataField',
            Value             : title,
            ![@UI.Importance] : #High,
        },
        {
            $Type : 'UI.DataField',
            Value : priority_code,
        },
        {
            $Type             : 'UI.DataField',
            Value             : category_code,
            ![@UI.Importance] : #High,
        },
        {
            $Type             : 'UI.DataField',
            Value             : incidentStatus_code,
            ![@UI.Importance] : #High,
        },
        ]
};

Thankfully, with code completion all this is achieved with just a few clicks: use CTRL+Space right before the collection brackets [] of the LineItem and choose UI.Criticality – all the wrapping and formatting will be done automatically. Now use the code completion again to select the value and you are done:

Try it out and provide feedback

You can try this out yourself in Visual Studio Code with  SAP Cloud Platform core data services plug-in for Visual Studio Code (v 3.0.0 or higher) any time, as it is already enhanced with the @sap/ux-cds-odata-language-server-extension.

If you use SAP Business Application Studio, you can find it in the SAP Cloud Business Application dev space.

Once you try it, share your experience with OData annotation support in the comments: what works well, what requires improvement and what else you like to know about LSP features for OData annotations (code completion, diagnostics, etc).

More information on OData annotation support in CAP CDS

Assigned Tags

      28 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Mariana Naboka
      Mariana Naboka
      Blog Post Author

      Please note, the link to the documentation has been updated

      Author's profile photo Martin Koch
      Martin Koch

      Great!
      Are there any plans to make the CAP open source or usable outside the SAP Cloud Platform (e.g. on premise with postgres)

      thanks!

      Martin

      Author's profile photo Christian Georgi
      Christian Georgi

      Hi,

      wrt open sourcing CAP: yes, we would like to do it and ponder about it every now and then.  Still no concrete plans yet, mainly due to the high load on the dev teams here.

      For PostgreSQL, see the community works on it: https://blogs.sap.com/2020/08/20/postgresql-persistence-adapter-for-cap-node.js/

       

      Regards, Christian

      Author's profile photo Martin Koch
      Martin Koch

      Hi Christian

      thanks for the info!

      It would be great if it will be open sourced. The CAP is a fantastic programming model.

      We are a SAP Partner developing SaaS apps - currently in plain Spring / Spring Boot but we are thinking about switching to CAP / node.js

      Regards,

      Martin

      Author's profile photo Christian Georgi
      Christian Georgi

      Thanks so much! 🙂

      Author's profile photo Mariana Naboka
      Mariana Naboka
      Blog Post Author

      Please note that   SAP Cloud Platform core data services plug-in for Visual Studio Code is now renamed to SAP CDS Language Support but it still provides the annotation LSP features described in this post

      Author's profile photo Max Schnürle
      Max Schnürle

      Dear Mariana,

       

      I have a self developed bookshop Odata Service Fiori Elements App in my Business Application Staudio, code can be seen here --> miyasuta/central-launchpad-cap (github.com)

      Now I am trying to extend it via Fiori tools with an additional button (action). I also have been following your video : https://www.youtube.com/watch?v=p1f0Albi7eE

      and documentation. Code is added to manifest and the extension controller file but the extensions won't show up in the app. Do you have any idea why this is not working? Do the extensions for instance only work for fiori standard apps with an OData Service from Backend etc.?

       

      I would highly appreciate your answer:)

      Best Regards

      Max

      Author's profile photo Hitesh Parmar
      Hitesh Parmar

      Hello Max,

      Could you please share more information on how you use Fiori tools to add an custom action? Where exactly are you adding this button?
      In the attached github repo, I couldn't find your extension coding.

      Best Regards,

      Hitesh

      Author's profile photo Max Schnürle
      Max Schnürle

      Hi Hitesh Parmar

       

      thanks for your response:)

      this was the template that i used to implement the extension. All I added was the extension in my manifest file plus the extension controller ( both in the app/fiori/webapp folder)

      I uploaded my extension project here:

      SnarkWay/bookshopapp (github.com)

      Do you find an issue?

      Do the annotations from the service file overwrite my extension?

       

      Best Regards

      Max

      Author's profile photo Hitesh Parmar
      Hitesh Parmar

      Hello Max Schnürle ,

       

      Thank you for updating your project.

      I see that you are using OData V2 syntax for adding extensions which is incorrect as your application is based on OData V4.

      Kindly refer to this documentation and use the syntax mentioned for OData V4 https://sapui5.hana.ondemand.com/#/topic/dd78acad2f164560ad6b0e24ed2cd8ee

      I hope this will solve your issue.

       

      Best Regards,

      Hitesh

      Author's profile photo Max Schnürle
      Max Schnürle

      Hi Hitesh Parmar

      thank you:) the OData v4 coding does not show up when doing this via the guided development approach. there only the v2 approach appears.

      Nevertheless I updated my manifest(starting line 105) but the button still won't appear:

      SnarkWay/bookshopapp (github.com)

      I am not sure what the issue is there. I would be thankful if you could take a look.

       

      Best Regards

      Max

      Author's profile photo Hitesh Parmar
      Hitesh Parmar

      Hi Max Schnürle ,

      Yes, you are correct, currently the Guided Development only supports OData V2. To see the guides only applicable to OData V4 you can group guides by OData Version.

      We will take this up to improve the UX.

      Group%20guides%20by%20OData%20version

      Group guides by OData version

      Your coding looks correct to me. Let me take a deeper look into this.

       

      Best Regards,

      Hitesh

      Author's profile photo Hitesh Parmar
      Hitesh Parmar

      Hi Max Schnürle ,

       

      I tried with your application and I can see that the button is rendered properly. I have a feeling that you overlooked the button in the UI. With your current coding in manfiest.json, global action will be added.

      See here: https://sapui5.hana.ondemand.com/#/topic/7619517a92414e27b71f02094bd08d06

      Custom%20Global%20Action

      Custom Global Action

      Additionally make sure to make use of the following controller code (as mentioned in the documentation).

       extendFunctionBooks: function(oContext, aSelectedContexts) {
              // oContext :  is the binding context of the current entity 
              // aSelectedContexts : contains an array of binding contexts corresponding to 
              // 						selected items in case of table action (or) 
              // 						current entity in case of header / footer action. 
          }

       

      Best Regards,

      Hitesh

      Author's profile photo Max Schnürle
      Max Schnürle

      Hi Hitesh Parmar

      thank you so much:)

      I indeed overlooked the button and now changed its position to the header of the lineItems.

      Never the less the 'message' action is not yet called correctly. Can you see why?

      code is updated here:

      SnarkWay/bookshop (github.com)

       

      Author's profile photo Max Schnürle
      Max Schnürle

      Hi Hitesh Parmar

      I fogured out how the customAction.js File has to be confugred to call the message Action:

      customActions.js

      customActions.js

      Author's profile photo Max Schnürle
      Max Schnürle

      Hitesh Parmar

      now the question is how I can start a workflow instance from that customEvent?

      I am using Archana's blog post for that:

      https://blogs.sap.com/2020/08/27/starting-workflow-from-custom-fiori-application-in-cloud-foundry/

      but the $ajax call throws an error because it doesnt find the XSRF Token(502-BadGatewayError)

      i thought its because Im calling from SAP Fiori Elements App but it should work the same as in UI5.

      Do you have any idea?

       

      Best Regards

      Max

      Author's profile photo Max Schnürle
      Max Schnürle

      Hitesh Parmar

       

      approach works similar to https://blogs.sap.com/2020/08/27/starting-workflow-from-custom-fiori-application-in-cloud-foundry/. I just had to correct the url. Thank you!

      Author's profile photo Pavel Belski
      Pavel Belski

      Hi @Hitesh Parmar,in OData V2 was an annotation loadDataOnAppLaunch to eliminate Go button,
      I can not find the way to do it in V4, could you please help?

      "dataLoadSettings": {
      "loadDataOnAppLaunch": "always"
      }

       

      thx in advance,
      Pavel

      Author's profile photo Hitesh Parmar
      Hitesh Parmar

      HI Pavel Belski ,

      In OData V4, the manifest property is called initialLoad, which can have values  AutoDisabled, and Enabled . To see the same in Page Editor as shown below, you need to have specificatoin version 1.90.0 which will be released in few days, you can check it here https://www.npmjs.com/package/@sap/ux-specification.

      For now, you should see boolean values.

      Page%20editor

      Page editor

      Please have a look here https://sapui5.hana.ondemand.com/#/topic/1cf5c7f5b81c4cb3ba98fd14314d4504 , you need to

      expand section SAP Fiori Elements for OData V4.

      Best regards,
      Hitesh

      Author's profile photo Sebastian Esch
      Sebastian Esch

      Hi Mariana Naboka,

      we are struggling with the UI.Hidden annotation. We can set it to a property of our entity, but our requirement is to control actions that mark a Product as Favourite if it is not marked as favourite and to remove it from the Favourites if it is marked as a Favourite.

      In one case it's easy, the annotation is "![@UI.Hidden]: isFavourite", but in the second case we would like to use an expression to negate the isFavourite property of the entity. Is this possbile with the CAP Annotation Syntax?

      As a workaround we have introduced a second property "isNotFavourite", but this is a rather ugly workaround.

      We were not able to find any examples of documentation regarding expressions for CAP CDS Annotations.

      Kind regards,

      Sebastian

      Author's profile photo Marcel Waechter
      Marcel Waechter

      Hi Sebastian Esch,

      SAP Fiori elements floorplans for OData v4 support OData annotation expressions. Not at all places yet but at least all the usages of hidden are enabled for expressions.

      But: unfortunately CAP CDS does not yet support annotation expressions.

      Until then you can help you with local annotations, like:

      <Record Type="UI.DataFieldForAction">
         <PropertyValue Property="Label" String="Your Action Label" />
         <PropertyValue Property="Action" String="yourNameSpace.YourAction" />
         <Annotation Term="UI.Hidden">
            <Not>
               <Path>isFavorite</Path>
            </Not>
         </Annotation>
      </Record>

      The issue with having local annotations in this case means you would need to override on a term level, so for example if this is an action in a table the whole UI.LineItem. So this is actually not a very good example of having local annotations if you have all the annotations in your CDS.

      Can you tell me if you use CAP JAVA or CAP Node? I will try to reach the product owner to see if when this can be expected.

      Best regards,
      Marcel

      Author's profile photo Sebastian Esch
      Sebastian Esch

      Hi Marcel Waechter,

      thanks for the hint with the local annotations. We are using CAP on Node.js.

      Kind regards,

      Sebastian

      Author's profile photo Mariana Naboka
      Mariana Naboka
      Blog Post Author
      you can also use the same logical operator in cap cds, if you work with the latest cds compiler. CDS compiler then converts it to the code suggested by Marcel Waechter above.
      Here is how it looks in cap cds:
      {
              $Type         : 'UI.DataFieldForAction',
              Label         : 'Your Action Label',
              Action        : 'yourService.YourAction',
              ![@UI.Hidden] : {$edmJson : {$Not : [{$Path : 'isFavorite' } ] } },
          }
      Author's profile photo Steffen Weinstock
      Steffen Weinstock

      Please note that this feature is not yet available externally. It is planned to come with the next CAP release, probably beginning of July.

      Author's profile photo Sebastian Esch
      Sebastian Esch

      Hi Mariana Naboka,

      cool, I would have never guessed the syntax. 🙂

      That means with $edmJson I can emulate the annotation XML structure?

      Kind regards,

      Sebastian

      Author's profile photo Sebastian Esch
      Sebastian Esch

      Ah, good to know.

      Author's profile photo Mariana Naboka
      Mariana Naboka
      Blog Post Author

      Steffen Weinstock

      Sorry, my fault. Got lost in compiler versions 🙁

      Sebastian Esch

      So you can either use the xml, or wait for the compiler version containing this feature.

      BTW, Fiori tools already provide the code completion for logical operators in xml annotation files - by default they are sorted out to the end of the code completion list.

      Once it is released, we will do our best to provide the code completion to simplify it for the end users also in CAP cds.

      Author's profile photo Sebastian Esch
      Sebastian Esch

      Hi Mariana Naboka,

      we will patiently wait for the July release of CAP and then remove our workaround. 🙂

      I haven't used the Fiori Tools XML Editor yet - the last time I did Fiori Elements with local annotations, Web IDE was the tool of choice and BAS / Fiori Tools was not around yet.

      Cheers,

      Sebastian