Search

Serving Fiori UIs

CAP provides out-of-the-box support for SAP Fiori Elements frontends.

This guide explains how to add one or more SAP Fiori Elements apps to a CAP project, how to add SAP Fiori Elements annotations to respective service definitions, etc. In the following sections, we are always referring to SAP Fiori Elements when mentioning Fiori.

Content

Adding SAP Fiori Apps to CAP Projects

As showcased in cap/samples, SAP Fiori apps should be added as sub folders to the app/ of a CAP project. Each sub folder constitutes an individual SAP Fiori application, with local annotations, manifest.json, etc. So, a typical folder layout would look like this:

Folder / Sub Folder Description
app/ all Fiori apps should go in here…
    browse/ Fiori app for end users
    orders/ Fiori app for order managementt
    admin/ Fiori app for admins
    index.html For sandbox tests
srv/ all services
db/ domain models, and db stuff

By Copying from cap/samples

For example, you can copy the SAP Fiori apps from cap/samples as a template and modify the content as appropriate.

Adding SAP Fiori Annotations

The main content to be added is service definitions annotated with information how to render respective data.

What Are SAP Fiori Annotations?

SAP Fiori elements apps are generic frontends, which construct and render the pages and controls based on annotated metadata documents. The annotations provide semantic annotations used to render such content, for example:

annotate CatalogService.Books with @(
  UI: {
    SelectionFields: [ ID, price, currency_code ],
    LineItem: [
      {Value: title},
      {Value: author, Label:'{i18n>Author}'},
      {Value: genre.name},
      {Value: price},
      {Value: currency.symbol, Label:' '},
    ]
  }
);

Find this source and many more in cap/samples Learn more on OData Annotations in CDS

Where to Put Them?

While CDS in principle allows to add such annotations everywhere in your models, we recommend putting them in separate .cds files placed in your ./app/* folders, for example, as follows.

./app  #> all your fiori annotations should go here, for example:
   ./admin
      fiori-service.cds #> annotating ../srv/admin-service.cds
   ./browse
      fiori-service.cds #> annotating ../srv/cat-service.cds
   index.cds
./srv  #> all service definitions should stay clean in here:
   admin-service.cds   
   cat-service.cds
...

See this also in cap/samples/fiori

Reasoning: This recommendation essentially follows best practices and guiding principles of Conceptual Modeling and Separation of Concerns.

Maintaining Annotations

Maintaining OData annotations in .cds files is accelerated by the SAP Fiori tools - CDS OData Language Server @sap/ux-cds-odata-language-server-extension comprised in SAP Cloud Platform core data services plugin. It assists you with adding and editing OData annotations in CDS syntax with:

  • Code Completion
  • Validation against the OData vocabularies and project metadata
  • Navigation to the referenced annotations
  • Quick view of vocabulary information
  • Internationalization support

These assisting features are provided for OData annotations in CDS syntax and can’t be used yet for the core data services common annotations.

@sap/ux-cds-odata-language-server-extension module doesn’t require any manual installation. The latest version is fetched by default from npmjs.com as indicated in the user preference setting CDS > Contributions: Registry.

Code Completion

CDS OData Language Server provides a list of context-sensitive suggestions based on the service metadata and OData vocabularies. You can use it to choose OData annotation terms, their properties, and values from the list of suggestions in annotate directives applied to service entities and entity elements. See annotate directives for more details.

Using Code Completion

To trigger the code completion, press Ctrl + Space (Windows), CMD + Space (Mac). The list of suggested values is displayed.

You can filter the list of suggested values by typing more characters.

Navigate to the desired value using up or down arrows or your mouse. Accept the highlighted value using Enter key or a mouse click. Use code completion to add and change individual values (word-based completion) and to add small code blocks containing annotation structures along with mandatory properties (micro-snippets). In an active code snippet, you can use Tab to quickly move to the next tab stop.

Example: Annotating Service Entities

(cursor position indicated by |)

  1. Place cursor in the annotate directive for a service entity, for example annotate Foo.Bar with ; and trigger the code completion.

  2. Type u to filter the suggestions and choose {} UI. Micro-snippet @UI : {|} is inserted: annotate Foo.Bar with @UI : {|};

  3. Use the code completion again to add an annotation term from UI vocabulary, in this example SelectionFields. Micro snippet for this annotation is added and the cursor is placed right after the term name letting you define a qualifier: annotate Foo.Bar with @UI : {SelectionFields | : []};

  4. Press the Tab key to move the cursor to the next tab stop and use the code completion again to add values. As UI.SelectionFields annotation is a collection of entity elements (entity properties), all elements of the annotated entity are suggested.

    Note: To choose an element of an associated entity, first select the corresponding association from the list and type . (period). Elements of associated entity are suggested.

    You can add multiple values separated by comma.

        annotate Foo.Bar with @UI : { SelectionFields : [
            description, assignedIndividual.lastName|
            ],
            };
    
  5. Add a new line after , (comma) and use the code completion again to add another annotation from UI vocabulary, such as LineItem. Line item is a collection of DataField records. To add a record, select the record type you need from the completion list.

         annotate Foo.Bar with @UI : { SelectionFields : [
             description, assignedIndividual.lastName
             ],
             LineItem : [
                 {
                     $Type:'UI.DataField',
                     Value : |,
                 },
                 ]
    
          };
    

    For each record type, two kinds of micro-snippets are provided: one containing only mandatory properties and one containing all properties defined for this record (full record). Usually you need just a subset of properties. So, you either select a full record and then remove the properties you don’t need, or add the record containing only required properties and then add the remaining properties.

  6. Use code completion to add values for the annotation properties.

        annotate Foo.Bar with @UI : { SelectionFields : [
            description, assignedIndividual.lastName
            ],
            LineItem : [
                {
                    $Type:'UI.DataField',
                    Value : description,
                },
                {
                    $Type:'UI.DataFieldForAnnotation',
                    Target :  'assignedIndividual/@Communication.Contact',
                },|
              ]
               };
    
    

    To add values pointing to annotations defined in another CDS source, you must reference this source with using directive. See The using Directive for more details.

Example: Annotating Entity Elements

(cursor position indicated by |)

  1. Place the cursor in the annotate directive, for example annotate Foo.Bar with {|};, add a new line and trigger the code completion. You get the list of entity elements. Choose the one you want to annotate.

        annotate Foo.Bar with {
            code|
        };
    
  2. Press Space and use the code completion again and choose {} UI. Micro-snippet @UI : {|} is inserted:

        annotate Foo.Bar with {
            code @UI : { | }
        };
    
  3. Trigger completion again and choose an annotation term from UI vocabulary, in this example: Hidden.

        annotate Foo.Bar with {
            code @UI : {Hidden : |}
        };
    
  4. Press the Tab key to move the cursor to the next tab stop and use the code completion again to add the value. As UI.Hidden annotation is of Boolean type, the values true and false is suggested:

        annotate Foo.Bar with {
            code @UI : {Hidden : false }
        };
    

Diagnostics

CDS OData Language Server server validates OData annotations in .cds files against the service metadata and OData vocabularies. It also checks provided string content for language-dependent annotation values and warns you if the format doesn’t match the internationalization (i18n) key reference. It makes you aware that this string is hard-coded and won’t change based on the language setting in your application. See Internationalization support for more details.

Validation is performed when you open a .cds file and then is retriggered with each change to the relevant files.

You can view the diagnostic messages by hovering over the highlighted part in the annotation file or by opening the problems panel. Click on the message in problems panel to navigate to the related place in the annotation file.

If an annotation value points to the annotation defined in another CDS source, you must reference this source with using directive to avoid warnings. See The using Directive for more details.

CDS OData Language Server enables quick navigation to the definition of referenced annotations. For example, if your annotation file contains a DataFieldForAnnotation record referencing an Identification annotation defined in the service file, you can view which file it’s defined in and what fields or labels this annotation contains. You can even update the Identification annotation or add comments.

You can navigate to the referenced annotation using Peek Definition and Go To Definition features.

If the referenced annotation is defined in another CDS source, you must reference this source with using directive to enable the navigation. See The using Directive for more details.

Peek Definition

Peek Definition lets you preview and update the referenced annotation without switching away from the code that you’re writing. It’s triggered when your cursor is inside the referenced annotation value.

  • Using keyboard: press Alt + F12 (Windows), Option F12 (Mac)
  • Using mouse: right-click and select Peek Definition If an annotation is defined in multiple sources, all these sources are listed. You can select which one you would like to view or update. Annotation layering isn’t considered.

Go to Definition

Go To Definition lets you navigate to the source of the referenced annotation and opens the source file scrolled to the respective place in a new tab. It’s triggered when your cursor is inside the referenced annotation value.

Place your text cursor inside the path referencing the annotation term segment or translatable string value and trigger Go to Definition:

  • Using keyboard: press F12 in VS Code, Ctrl + F11 in SAP Business Application Studio
  • Using mouse: right-click and select Go To Definition
  • Using keyboard and mouse: Ctrl + mouse click (Windows), CMD + mouse click (Mac)

If an annotation is defined in multiple sources, Peek definition listing these sources will be shown instead. Annotation layering isn’t considered.

Documentation (Quick Info)

Annotation language server provides a quick information for annotation terms, record types, and properties used in the annotation file or provided as suggestions in code completion lists. This information is retrieved from the respective OData vocabularies and can provide answers to the following questions:

  • What is the type and purpose of the annotation term / record type / property?
  • What targets can the annotation term apply to?
  • Is the annotation term / record type / property experimental? Is it deprecated?
  • Is this annotation property mandatory or optional?

The exact content depends on the availability in OData vocabularies.

To view the quick info for an annotation term, record type or property used in the annotation file, hover your mouse over it. The accompanying documentation is displayed in a hover window, if provided in the respective OData vocabularies.

To view the quick info for each suggestion in the code completion list, either press Ctrl + Space (Windows), CMD + Space (Mac) or click the info icon. The accompanying documentation for the suggestion expands to the side. The expanded documentation stays so and updates as you navigate the list. You can close this by pressing Ctrl + Space / CMD + Space again or by clicking on the close icon.

Internationalization Support

When you open an annotation file, all language-dependent string values are checked against the i18n.properties file. Each value that doesn’t represent a valid reference to the existing text key in the i18n.properties file, is indicated with a warning. A Quick Fix action is suggested to generate a text key in i18n file and substitute your string value with the reference to that entry.

Prefer @title and @description

Influenced by JSON Schema, CDS supports the common annotations @title and @description, which are mapped to corresponding OData annotations as follows:

CDS JSON Schema OData
@title title @Common.Label
@description description @Core.Description

It’s recommended to prefer these annotations over the OData ones in protocol-agnostic data models and service models, for example:

annotate my.Books with { //...
   title @title: 'Book Title';
   author @title: 'Author ID';
}

Prefer @readonly, @mandatory, …

CDS supports @readonly as a [common annotation, which translates to respective OData annotations from the @Capabilities vocabulary. We recommend using the former for reasons of conciseness and comprehensibility as shown in this example:

@readonly entity Foo {  // entity-level
  @readonly foo ...     // element-level
}

is equivalent to:

entity Foo @(Capabilities:{
  // entity-level
  InsertRestrictions.Insertable: false,
  UpdateRestrictions.Updatable: false,
  DeleteRestrictions.Deletable: false
}) {
  // element-level
  @Core.Computed foo ...
}

Similar recommendations apply to @mandatory and others → see Common Annotations.

Draft-Based Editing

Fiori supports edit sessions with draft states stored on the server, so users can interrupt and continue later on, possibly from different places and devices. CAP as well as Fiori elements provide out-of-the-box support for Drafts as outlined below… It’s recommended to always use Draft when your application needs data input by end users.

For details and guidelines about that, see SAP Fiori Design Guidelines for Draft

Find a working end-to-end version in cap/samples/fiori

Enabling Draft with @odata.draft.enabled

To enable draft for an entity exposed by a service, simply annotate it with @odata.draft.enabled as in this example:

annotate AdminService.Books with @odata.draft.enabled;

See that live in cap/samples

Enabling Draft for Localized Data

Additionally annotate the underlying base entity in the base model with @fiori.draft.enabled to also support drafts for localized data:

annotate sap.capire.bookshop.Books with @fiori.draft.enabled;

Background: Fiori Drafts requires single keys of type UUID, which isn’t the case by default for the automatically generated _texts entities (→ see the Localized Data guide for details). The annotation @fiori.draft.enabled tells the compiler to add such a technical primary key element named ID_texts.

See that live in cap/samples

Validating Drafts

You can add custom handlers to add specific validations as usual. In addition, in case of draft, you can register handlers to the PATCH events to validate input per field, during the edit session, as follows.

… in Java

You can add your validation logic in before operation event handlers. Specific events for draft operations exist, see Java > Handling Fiori Draft Events for more details.

… in Node.js

You can add your validation logic in the before operation handler for the CREATE or UPDATE event (as in the case of nondraft implementations) or on the SAVE event (specific to drafts only):

srv.before ('CREATE','Books', (req)=>{ ... }) // run before create
srv.before ('UPDATE','Books', (req)=>{ ... }) // run before create
srv.before ('SAVE','Books', (req)=>{...})     // run at final save only

In addition, you can add field-level validations on the individual PATCH events:

srv.before ('PATCH','Books', (req)=>{...}) // run during editing

These get triggered during the draft edit session whenever the user tabs from one field to the next, and can be used to provide early feedback.

Value Help Support

In addition to supporting the standard @Common.ValueList annotations as defined in the OData Vocabularies, CAP provides advanced convenient support for Value Help as understood and supported by Fiori.

Convenience option @cds.odata.valuelist

Simply add annotation @cds.odata.valuelist to an entity, and all Associations targeting this entity will automatically receive Value Lists in Fiori clients. For example:

@cds.odata.valuelist
entity Currencies {}
service BookshopService {
   entity Books { //...
      currency : Association to Currencies; 
   }
}

Pre-defined types in @sap/cds/common

The reuse types in @sap/cds/common already have this added to base types and entities, so all uses automatically benefit from this, this is an effective excerpt of respective definitions in @sap/cds/common:

type Currencies : Association to sap.common.Currencies;
context sap.common {
  entity Currencies : CodeList {...};
  entity CodeList { name : localized String; ... }
}
annotate sap.common.CodeList with @(
   UI.Identification: [name],
   cds.odata.valuelist,
);

Usages of @sap/cds/common

In effect, usages of @sap/cds/common stay clean of any pollution, for example:

using { Currency } from '@sap/cds/common';
entity Books { //...
  currency : Currency;
}

Find that also in our cap/samples

Still, all Fiori UIs on all services exposing Books will automatically receive Value Help for currencies. You can also benefit from that when deriving your project-specific code list entities from sap.common.CodeList.

Resulting Annotations in EDMX

Here is an example how this ends up as OData Common.ValueList annotations:

<Annotations Target="AdminService.Books/currency_code">
   <Annotation Term="Common.ValueList">
      <Record Type="Common.ValueListType">
         <PropertyValue Property="CollectionPath" String="Currencies"/>
         <PropertyValue Property="Label" String="Currency"/>
         <PropertyValue Property="Parameters">
            <Collection>
               <Record Type="Common.ValueListParameterInOut">
                  <PropertyValue Property="ValueListProperty" String="code"/>
                  <PropertyValue Property="LocalDataProperty" PropertyPath="currency_code"/>
               </Record>
               <Record Type="Common.ValueListParameterDisplayOnly">
                  <PropertyValue Property="ValueListProperty" String="name"/>
               </Record>
            </Collection>
         </PropertyValue>
      </Record>
   </Annotation>
</Annotation>

Test